private bool CheckForAssignmentToSelf(BoundAssignmentOperator node) { if (!node.HasAnyErrors && IsSameLocalOrField(node.Left, node.Right)) { Error(ErrorCode.WRN_AssignmentToSelf, node); return(true); } return(false); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { CheckForAssignmentToSelf(node); if (_inExpressionLambda && node.Left.Kind != BoundKind.ObjectInitializerMember && node.Left.Kind != BoundKind.DynamicObjectInitializerMember) { Error(ErrorCode.ERR_ExpressionTreeContainsAssignment, node); } return(base.VisitAssignmentOperator(node)); }
private BoundExpression HoistRefInitialization(SynthesizedLocal local, BoundAssignmentOperator node) { Debug.Assert(local.SynthesizedKind == SynthesizedLocalKind.Spill); Debug.Assert(local.SyntaxOpt != null); Debug.Assert(this.OriginalMethod.IsAsync); var right = (BoundExpression)Visit(node.Right); var sideEffects = ArrayBuilder <BoundExpression> .GetInstance(); bool needsSacrificialEvaluation = false; var hoistedFields = ArrayBuilder <StateMachineFieldSymbol> .GetInstance(); AwaitExpressionSyntax awaitSyntaxOpt; int syntaxOffset; if (F.Compilation.Options.OptimizationLevel == OptimizationLevel.Debug) { awaitSyntaxOpt = (AwaitExpressionSyntax)local.GetDeclaratorSyntax(); syntaxOffset = this.OriginalMethod.CalculateLocalSyntaxOffset(awaitSyntaxOpt.SpanStart, awaitSyntaxOpt.SyntaxTree); } else { // These are only used to calculate debug id for ref-spilled variables, // no need to do so in release build. awaitSyntaxOpt = null; syntaxOffset = -1; } var replacement = HoistExpression(right, awaitSyntaxOpt, syntaxOffset, local.RefKind, sideEffects, hoistedFields, ref needsSacrificialEvaluation); proxies.Add(local, new CapturedToExpressionSymbolReplacement(replacement, hoistedFields.ToImmutableAndFree(), isReusable: true)); if (needsSacrificialEvaluation) { var type = TypeMap.SubstituteType(local.Type.TypeSymbol).TypeSymbol; var sacrificialTemp = F.SynthesizedLocal(type, refKind: RefKind.Ref); Debug.Assert(TypeSymbol.Equals(type, replacement.Type, TypeCompareKind.ConsiderEverything2)); return(F.Sequence(ImmutableArray.Create(sacrificialTemp), sideEffects.ToImmutableAndFree(), F.AssignmentExpression(F.Local(sacrificialTemp), replacement, isRef: true))); } if (sideEffects.Count == 0) { sideEffects.Free(); return(null); } var last = sideEffects.Last(); sideEffects.RemoveLast(); return(F.Sequence(ImmutableArray <LocalSymbol> .Empty, sideEffects.ToImmutableAndFree(), last)); }
private BoundExpression VisitAssignmentOperator(BoundAssignmentOperator node, bool used) { var loweredRight = VisitExpression(node.Right); BoundExpression left = node.Left; BoundExpression loweredLeft; switch (left.Kind) { case BoundKind.PropertyAccess: loweredLeft = VisitPropertyAccess((BoundPropertyAccess)left, isLeftOfAssignment: true); break; case BoundKind.IndexerAccess: loweredLeft = VisitIndexerAccess((BoundIndexerAccess)left, isLeftOfAssignment: true); break; default: loweredLeft = VisitExpression(left); break; } return(MakeStaticAssignmentOperator(node.Syntax, loweredLeft, loweredRight, node.IsRef, node.Type, used)); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { BoundSpillSequenceBuilder builder = null; var right = VisitExpression(ref builder, node.Right); BoundExpression left = node.Left; if (builder == null) { left = VisitExpression(ref builder, left); } else { // if the right-hand-side has await, spill the left var leftBuilder = new BoundSpillSequenceBuilder(); switch (left.Kind) { case BoundKind.Local: case BoundKind.Parameter: // locals and parameters are directly assignable, LHS is not on the stack so nothing to spill break; case BoundKind.FieldAccess: var field = (BoundFieldAccess)left; // static fields are directly assignable, LHS is not on the stack, nothing to spill if (field.FieldSymbol.IsStatic) { break; } // instance fields are directly assignable, but receiver is pushed, so need to spill that. var receiver = VisitExpression(ref leftBuilder, field.ReceiverOpt); receiver = Spill(builder, receiver, field.FieldSymbol.ContainingType.IsValueType ? RefKind.Ref : RefKind.None); left = field.Update(receiver, field.FieldSymbol, field.ConstantValueOpt, field.ResultKind, field.Type); break; case BoundKind.ArrayAccess: var arrayAccess = (BoundArrayAccess)left; // array and indices are pushed on stack so need to spill that var expression = VisitExpression(ref leftBuilder, arrayAccess.Expression); expression = Spill(builder, expression, RefKind.None); var index = this.VisitExpression(ref builder, arrayAccess.Index); left = arrayAccess.Update(expression, index, arrayAccess.Type); break; default: // must be something indirectly assignable, just visit and spill as an ordinary Ref (not a RefReadOnly!!) // // NOTE: in some cases this will result in spiller producing an error. // For example if the LHS is a ref-returning method like // // obj.RefReturning(a, b, c) = await Something(); // // the spiller would eventually have to spill the evaluation result of "refReturning" call as an ordinary Ref, // which it can't. left = Spill(leftBuilder, VisitExpression(ref leftBuilder, left), RefKind.Ref); break; } leftBuilder.Include(builder); builder = leftBuilder; } return(UpdateExpression(builder, node.Update(left, right, node.IsRef, node.Type))); }
private BoundExpression MakePropertyAssignment( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol property, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, ImmutableArray <int> argsToParamsOpt, BoundExpression rewrittenRight, TypeSymbol type, bool used) { // Rewrite property assignment into call to setter. var setMethod = property.GetOwnOrInheritedSetMethod(); if ((object)setMethod == null) { Debug.Assert((property as SourcePropertySymbol)?.IsAutoProperty == true, "only autoproperties can be assignable without having setters"); var backingField = (property as SourcePropertySymbol).BackingField; return(_factory.AssignmentExpression( _factory.Field(rewrittenReceiver, backingField), rewrittenRight)); } // We have already lowered each argument, but we may need some additional rewriting for the arguments, // such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. ImmutableArray <LocalSymbol> argTemps; rewrittenArguments = MakeArguments( syntax, rewrittenArguments, property, setMethod, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out argTemps, invokedAsExtensionMethod: false, enableCallerInfo: ThreeState.True); if (used) { // Save expression value to a temporary before calling the // setter, and restore the temporary after the setter, so the // assignment can be used as an embedded expression. TypeSymbol exprType = rewrittenRight.Type; LocalSymbol rhsTemp = _factory.SynthesizedLocal(exprType); BoundExpression boundRhs = new BoundLocal(syntax, rhsTemp, null, exprType); BoundExpression rhsAssignment = new BoundAssignmentOperator( syntax, boundRhs, rewrittenRight, exprType); BoundExpression setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rhsAssignment)); return(new BoundSequence( syntax, AppendToPossibleNull(argTemps, rhsTemp), ImmutableArray.Create(setterCall), boundRhs, type)); } else { BoundCall setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, setMethod, AppendToPossibleNull(rewrittenArguments, rewrittenRight)); if (argTemps.IsDefaultOrEmpty) { return(setterCall); } else { return(new BoundSequence( syntax, argTemps, ImmutableArray <BoundExpression> .Empty, setterCall, setMethod.ReturnType.TypeSymbol)); } } }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { // Assume value of expression is used. return(VisitAssignmentOperator(node, used: true)); }
// Rewrite object initializer member assignment and add it to the result. // new SomeType { Member = 0 }; // ~~~~~~~~~~ private void AddObjectInitializer( ref ArrayBuilder <BoundExpression> dynamicSiteInitializers, ref ArrayBuilder <LocalSymbol> temps, ArrayBuilder <BoundExpression> result, BoundExpression rewrittenReceiver, BoundAssignmentOperator assignment) { Debug.Assert(rewrittenReceiver != null); // Update the receiver for the field/property access as we might have introduced a temp for the initializer rewrite. BoundExpression rewrittenLeft = null; // Do not lower pointer access yet, we'll do it later. if (assignment.Left.Kind != BoundKind.PointerElementAccess) { rewrittenLeft = VisitExpression(assignment.Left); } BoundKind rhsKind = assignment.Right.Kind; bool isRhsNestedInitializer = rhsKind == BoundKind.ObjectInitializerExpression || rhsKind == BoundKind.CollectionInitializerExpression; BoundExpression rewrittenAccess; switch ((rewrittenLeft ?? assignment.Left).Kind) { case BoundKind.ObjectInitializerMember: { var memberInit = (BoundObjectInitializerMember)rewrittenLeft; if (!memberInit.Arguments.IsDefaultOrEmpty) { var args = EvaluateSideEffectingArgumentsToTemps( memberInit.Arguments, memberInit.MemberSymbol?.GetParameterRefKinds() ?? default(ImmutableArray <RefKind>), result, ref temps); memberInit = memberInit.Update( memberInit.MemberSymbol, args, memberInit.ArgumentNamesOpt, memberInit.ArgumentRefKindsOpt, memberInit.Expanded, memberInit.ArgsToParamsOpt, memberInit.ResultKind, memberInit.ReceiverType, memberInit.BinderOpt, memberInit.Type); } rewrittenAccess = MakeObjectInitializerMemberAccess(rewrittenReceiver, memberInit, isRhsNestedInitializer); if (!isRhsNestedInitializer) { // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(assignment.Right); result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, assignment.Type, used: false)); return; } break; } case BoundKind.ArrayAccess: { var arrayAccess = (BoundArrayAccess)rewrittenLeft; var indices = EvaluateSideEffectingArgumentsToTemps( ImmutableArray.Create(arrayAccess.Index), paramRefKindsOpt: default, result, ref temps); rewrittenAccess = arrayAccess.Update(rewrittenReceiver, indices[0], arrayAccess.Type); if (!isRhsNestedInitializer) { // Rewrite simple assignment to field/property. var rewrittenRight = VisitExpression(assignment.Right); result.Add(MakeStaticAssignmentOperator(assignment.Syntax, rewrittenAccess, rewrittenRight, false, assignment.Type, used: false)); return; } break; }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { _mightAssignSomething = true; return(null); }
/// <summary> /// If we have a WinRT type event, we need to encapsulate the adder call /// (which returns an EventRegistrationToken) with a call to /// WindowsRuntimeMarshal.AddEventHandler or RemoveEventHandler, but these /// require us to create a new Func representing the adder and another /// Action representing the Remover. /// /// The rewritten call looks something like: /// /// WindowsRuntimeMarshal.AddEventHandler<EventHandler> /// (new Func<EventHandler, EventRegistrationToken>(@object.add), /// new Action<EventRegistrationToken>(@object.remove), handler); /// /// Where @object is a compiler-generated local temp if needed. /// </summary> /// <remarks> /// TODO: use or delete isDynamic. /// </remarks> private BoundExpression RewriteWindowsRuntimeEventAssignmentOperator(SyntaxNode syntax, EventSymbol eventSymbol, EventAssignmentKind kind, bool isDynamic, BoundExpression rewrittenReceiverOpt, BoundExpression rewrittenArgument) { BoundAssignmentOperator tempAssignment = null; BoundLocal boundTemp = null; if (!eventSymbol.IsStatic && CanChangeValueBetweenReads(rewrittenReceiverOpt)) { boundTemp = _factory.StoreToTemp(rewrittenReceiverOpt, out tempAssignment); } NamedTypeSymbol tokenType = _factory.WellKnownType(WellKnownType.core_runtime_WindowsRuntime_EventRegistrationToken); NamedTypeSymbol marshalType = _factory.WellKnownType(WellKnownType.core_runtime_WindowsRuntime_WindowsRuntimeMarshal); NamedTypeSymbol actionType = _factory.WellKnownType(WellKnownType.core_Action_T).Construct(tokenType); TypeSymbol eventType = eventSymbol.Type.TypeSymbol; BoundExpression delegateCreationArgument = boundTemp ?? rewrittenReceiverOpt ?? _factory.Type(eventType); BoundDelegateCreationExpression removeDelegate = new BoundDelegateCreationExpression( syntax: syntax, argument: delegateCreationArgument, methodOpt: eventSymbol.RemoveMethod, isExtensionMethod: false, type: actionType); BoundExpression clearCall = null; if (kind == EventAssignmentKind.Assignment) { MethodSymbol clearMethod; if (TryGetWellKnownTypeMember(syntax, WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__RemoveAllEventHandlers, out clearMethod)) { clearCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: clearMethod, rewrittenArguments: ImmutableArray.Create <BoundExpression>(removeDelegate), type: clearMethod.ReturnType.TypeSymbol); } else { clearCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, ImmutableArray.Create <BoundExpression>(removeDelegate), ErrorTypeSymbol.UnknownResultType); } } ImmutableArray <BoundExpression> marshalArguments; WellKnownMember helper; if (kind == EventAssignmentKind.Subtraction) { helper = WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__RemoveEventHandler_T; marshalArguments = ImmutableArray.Create <BoundExpression>(removeDelegate, rewrittenArgument); } else { NamedTypeSymbol func2Type = _factory.WellKnownType(WellKnownType.core_Func_T2).Construct(eventType, tokenType); BoundDelegateCreationExpression addDelegate = new BoundDelegateCreationExpression( syntax: syntax, argument: delegateCreationArgument, methodOpt: eventSymbol.AddMethod, isExtensionMethod: false, type: func2Type); helper = WellKnownMember.System_Runtime_InteropServices_WindowsRuntime_WindowsRuntimeMarshal__AddEventHandler_T; marshalArguments = ImmutableArray.Create <BoundExpression>(addDelegate, removeDelegate, rewrittenArgument); } BoundExpression marshalCall; MethodSymbol marshalMethod; if (TryGetWellKnownTypeMember(syntax, helper, out marshalMethod)) { marshalMethod = marshalMethod.Construct(eventType); marshalCall = MakeCall( syntax: syntax, rewrittenReceiver: null, method: marshalMethod, rewrittenArguments: marshalArguments, type: marshalMethod.ReturnType.TypeSymbol); } else { marshalCall = new BoundBadExpression(syntax, LookupResultKind.NotInvocable, ImmutableArray <Symbol> .Empty, marshalArguments, ErrorTypeSymbol.UnknownResultType); } // In this case, we don't need a sequence. if (boundTemp == null && clearCall == null) { return(marshalCall); } ImmutableArray <LocalSymbol> tempSymbols = boundTemp == null ? ImmutableArray <LocalSymbol> .Empty : ImmutableArray.Create <LocalSymbol>(boundTemp.LocalSymbol); ArrayBuilder <BoundExpression> sideEffects = ArrayBuilder <BoundExpression> .GetInstance(2); //max size if (clearCall != null) { sideEffects.Add(clearCall); } if (tempAssignment != null) { sideEffects.Add(tempAssignment); } Debug.Assert(sideEffects.Any(), "Otherwise, we shouldn't be building a sequence"); return(new BoundSequence(syntax, tempSymbols, sideEffects.ToImmutableAndFree(), marshalCall, marshalCall.Type)); }
public override BoundNode VisitAssignmentOperator(BoundAssignmentOperator node) { CheckForAssignmentToSelf(node); return(base.VisitAssignmentOperator(node)); }