public BoundIndexerAccess Update(bool useSetterForDefaultArgumentGeneration) { if (useSetterForDefaultArgumentGeneration != this.UseSetterForDefaultArgumentGeneration) { var result = new BoundIndexerAccess( this.Syntax, this.ReceiverOpt, this.Indexer, this.Arguments, this.ArgumentNamesOpt, this.ArgumentRefKindsOpt, this.Expanded, this.ArgsToParamsOpt, this.BinderOpt, useSetterForDefaultArgumentGeneration, this.Type, this.HasErrors) { WasCompilerGenerated = this.WasCompilerGenerated, OriginalIndexersOpt = this.OriginalIndexersOpt }; return(result); } return(this); }
public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) { Debug.Assert(node.Indexer.IsIndexer || node.Indexer.IsIndexedProperty); Debug.Assert((object)node.Indexer.GetOwnOrInheritedGetMethod() != null); return(VisitIndexerAccess(node, isLeftOfAssignment: false)); }
private BoundExpression VisitIndexerAccess(BoundIndexerAccess node, bool isLeftOfAssignment) { PropertySymbol indexer = node.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); // Rewrite the receiver. BoundExpression rewrittenReceiver = VisitExpression(node.ReceiverOpt); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. ImmutableArray <BoundExpression> rewrittenArguments = VisitList(node.Arguments); return(MakeIndexerAccess( node.Syntax, rewrittenReceiver, indexer, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.Type, node, isLeftOfAssignment)); }
private BoundExpression MakeIndexerAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol indexer, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <string> argumentNamesOpt, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, ImmutableArray <int> argsToParamsOpt, TypeSymbol type, BoundIndexerAccess oldNodeOpt, bool isLeftOfAssignment) { if (isLeftOfAssignment && indexer.RefKind == RefKind.None) { // This is an indexer set access. We return a BoundIndexerAccess node here. // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. return(oldNodeOpt != null? oldNodeOpt.Update(rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, null, isLeftOfAssignment, type) : new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, null, isLeftOfAssignment, type)); } else { var getMethod = indexer.GetOwnOrInheritedGetMethod(); Debug.Assert((object)getMethod != null); // 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> temps; rewrittenArguments = MakeArguments( syntax, rewrittenArguments, indexer, getMethod, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out temps, enableCallerInfo: ThreeState.True); BoundExpression call = MakePropertyGetAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, getMethod); if (temps.IsDefaultOrEmpty) { return(call); } else { return(new BoundSequence( syntax, temps, ImmutableArray <BoundExpression> .Empty, call, type)); } } }
public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) { var indexer = node.Indexer; var method = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod(); if ((object)method != null) { VisitCall(method, indexer, node.Arguments, node.ArgumentRefKindsOpt, node.ArgumentNamesOpt, node.Expanded, node); } CheckReceiverIfField(node.ReceiverOpt); return(base.VisitIndexerAccess(node)); }
public override BoundNode VisitIndexerAccess(BoundIndexerAccess node) { // Although property arguments with ref indexers are not declarable in C#, they may be usable if (!node.ArgumentRefKindsOpt.IsDefault) { _mightAssignSomething = true; } else { base.VisitIndexerAccess(node); } return(null); }
/// <summary> /// Generates a lowered form of the assignment operator for the given left and right sub-expressions. /// Left and right sub-expressions must be in lowered form. /// </summary> private BoundExpression MakeStaticAssignmentOperator( SyntaxNode syntax, BoundExpression rewrittenLeft, BoundExpression rewrittenRight, bool isRef, TypeSymbol type, bool used) { switch (rewrittenLeft.Kind) { case BoundKind.DynamicIndexerAccess: case BoundKind.DynamicMemberAccess: throw ExceptionUtilities.UnexpectedValue(rewrittenLeft.Kind); case BoundKind.PropertyAccess: { Debug.Assert(!isRef); BoundPropertyAccess propertyAccess = (BoundPropertyAccess)rewrittenLeft; BoundExpression rewrittenReceiver = propertyAccess.ReceiverOpt; PropertySymbol property = propertyAccess.PropertySymbol; Debug.Assert(!property.IsIndexer); return(MakePropertyAssignment( syntax, rewrittenReceiver, property, ImmutableArray <BoundExpression> .Empty, default(ImmutableArray <RefKind>), false, default(ImmutableArray <int>), rewrittenRight, type, used)); } case BoundKind.IndexerAccess: { Debug.Assert(!isRef); BoundIndexerAccess indexerAccess = (BoundIndexerAccess)rewrittenLeft; BoundExpression rewrittenReceiver = indexerAccess.ReceiverOpt; ImmutableArray <BoundExpression> rewrittenArguments = indexerAccess.Arguments; PropertySymbol indexer = indexerAccess.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); return(MakePropertyAssignment( syntax, rewrittenReceiver, indexer, rewrittenArguments, indexerAccess.ArgumentRefKindsOpt, indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, rewrittenRight, type, used)); } case BoundKind.Local: { Debug.Assert(!isRef || ((BoundLocal)rewrittenLeft).LocalSymbol.RefKind != RefKind.None); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, type, isRef: isRef)); } case BoundKind.Parameter: { Debug.Assert(!isRef || rewrittenLeft.GetRefKind() != RefKind.None); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, isRef, type)); } case BoundKind.DiscardExpression: { return(rewrittenRight); } default: { Debug.Assert(!isRef); return(new BoundAssignmentOperator( syntax, rewrittenLeft, rewrittenRight, type)); } } }
private BoundExpression VisitIndexerAccess(BoundIndexerAccess node, bool isLeftOfAssignment) { PropertySymbol indexer = node.Indexer; Debug.Assert(indexer.IsIndexer || indexer.IsIndexedProperty); // Rewrite the receiver. BoundExpression rewrittenReceiver = VisitExpression(node.ReceiverOpt); // Rewrite the arguments. // NOTE: We may need additional argument rewriting such as generating a params array, re-ordering arguments based on argsToParamsOpt map, inserting arguments for optional parameters, etc. // NOTE: This is done later by MakeArguments, for now we just lower each argument. ImmutableArray <BoundExpression> rewrittenArguments = VisitList(node.Arguments); // https://github.com/dotnet/roslyn/issues/30620 if (rewrittenReceiver?.Type.SpecialType == SpecialType.System_String && rewrittenArguments.Length == 1 && rewrittenArguments[0].Type.SpecialType == SpecialType.None) { var F = _factory; var indexLocal = F.StoreToTemp(rewrittenArguments[0], out BoundAssignmentOperator indexAssign); var stringLocal = F.StoreToTemp(rewrittenReceiver, out BoundAssignmentOperator stringAssign); var indexValueSymbol = (PropertySymbol)F.WellKnownMember(WellKnownMember.System_Index__Value); var indexFromEndSymbol = (PropertySymbol)F.WellKnownMember(WellKnownMember.System_Index__FromEnd); var argType = rewrittenArguments[0].Type; if (TypeSymbol.Equals(argType, _compilation.GetWellKnownType(WellKnownType.core_Index), TypeCompareKind.ConsiderEverything2)) { // string[Index] is rewritten as: // index.FromEnd ? s[s.Length - index.Value] : s[index.Value]; var indexValueExpr = F.Property(indexLocal, indexValueSymbol); return(F.Sequence( ImmutableArray.Create <LocalSymbol>( indexLocal.LocalSymbol, stringLocal.LocalSymbol), ImmutableArray.Create <BoundExpression>( indexAssign, stringAssign), F.Conditional( F.Property(indexLocal, indexFromEndSymbol), F.Indexer(stringLocal, node.Indexer, F.Binary( BinaryOperatorKind.Subtraction, F.SpecialType(SpecialType.System_Int32), F.Call(stringLocal, F.SpecialMethod(SpecialMember.System_String__Length)), indexValueExpr)), F.Indexer(stringLocal, node.Indexer, indexValueExpr), F.SpecialType(SpecialType.System_Char)))); } else if (TypeSymbol.Equals(argType, _compilation.GetWellKnownType(WellKnownType.core_Range), TypeCompareKind.ConsiderEverything2)) { // string[Range] is translated to: // var start = range.Start.FromEnd ? array.Length - range.Start.Value : range.Start.Value; // var end = range.End.FromEnd ? array.Length - range.End.Value : range.End.Value; // string.Substring(start, end - start) var rangeStartSymbol = (PropertySymbol)F.WellKnownMember(WellKnownMember.System_Range__Start); var rangeEndSymbol = (PropertySymbol)F.WellKnownMember(WellKnownMember.System_Range__End); var arrayCopySymbol = F.WellKnownMethod(WellKnownMember.System_Array__Copy); var startLocal = F.StoreToTemp( F.Conditional( F.Property(F.Property(indexLocal, rangeStartSymbol), indexFromEndSymbol), F.Binary( BinaryOperatorKind.Subtraction, F.SpecialType(SpecialType.System_Int32), F.Call(stringLocal, F.SpecialMethod(SpecialMember.System_String__Length)), F.Property(F.Property(indexLocal, rangeStartSymbol), indexValueSymbol)), F.Property(F.Property(indexLocal, rangeStartSymbol), indexValueSymbol), F.SpecialType(SpecialType.System_Int32)), out BoundAssignmentOperator startAssign); var endLocal = F.StoreToTemp( F.Conditional( F.Property(F.Property(indexLocal, rangeEndSymbol), indexFromEndSymbol), F.Binary( BinaryOperatorKind.Subtraction, F.SpecialType(SpecialType.System_Int32), F.Call(stringLocal, F.SpecialMethod(SpecialMember.System_String__Length)), F.Property(F.Property(indexLocal, rangeEndSymbol), indexValueSymbol)), F.Property(F.Property(indexLocal, rangeEndSymbol), indexValueSymbol), F.SpecialType(SpecialType.System_Int32)), out BoundAssignmentOperator endAssign); var substringExpr = F.Call( stringLocal, F.WellKnownMethod(WellKnownMember.System_String__Substring), startLocal, F.Binary(BinaryOperatorKind.Subtraction, F.SpecialType(SpecialType.System_Int32), endLocal, startLocal)); return(F.Sequence( ImmutableArray.Create( indexLocal.LocalSymbol, stringLocal.LocalSymbol, startLocal.LocalSymbol, endLocal.LocalSymbol), ImmutableArray.Create <BoundExpression>( indexAssign, stringAssign, startAssign, endAssign), substringExpr)); } else { throw ExceptionUtilities.Unreachable; } } return(MakeIndexerAccess( node.Syntax, rewrittenReceiver, indexer, rewrittenArguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.Type, node, isLeftOfAssignment)); }
private BoundIndexerAccess TransformIndexerAccess(BoundIndexerAccess indexerAccess, ArrayBuilder <BoundExpression> stores, ArrayBuilder <LocalSymbol> temps) { var receiverOpt = indexerAccess.ReceiverOpt; Debug.Assert(receiverOpt != null); BoundExpression transformedReceiver; if (CanChangeValueBetweenReads(receiverOpt)) { BoundExpression rewrittenReceiver = VisitExpression(receiverOpt); BoundAssignmentOperator assignmentToTemp; // SPEC VIOLATION: It is not very clear when receiver of constrained callvirt is dereferenced - when pushed (in lexical order), // SPEC VIOLATION: or when actual call is executed. The actual behavior seems to be implementation specific in different JITs. // SPEC VIOLATION: To not depend on that, the right thing to do here is to store the value of the variable // SPEC VIOLATION: when variable has reference type (regular temp), and store variable's location when it has a value type. (ref temp) // SPEC VIOLATION: in a case of unconstrained generic type parameter a runtime test (default(T) == null) would be needed // SPEC VIOLATION: However, for compatibility with Dev12 we will continue treating all generic type parameters, constrained or not, // SPEC VIOLATION: as value types. var variableRepresentsLocation = rewrittenReceiver.Type.IsValueType || rewrittenReceiver.Type.Kind == SymbolKind.TypeParameter; var receiverTemp = _factory.StoreToTemp(rewrittenReceiver, out assignmentToTemp, refKind: variableRepresentsLocation ? RefKind.Ref : RefKind.None); transformedReceiver = receiverTemp; stores.Add(assignmentToTemp); temps.Add(receiverTemp.LocalSymbol); } else { transformedReceiver = VisitExpression(receiverOpt); } // Dealing with the arguments is a bit tricky because they can be named out-of-order arguments; // we have to preserve both the source-code order of the side effects and the side effects // only being executed once. // // This is a subtly different problem than the problem faced by the conventional call // rewriter; with the conventional call rewriter we already know that the side effects // will only be executed once because the arguments are only being pushed on the stack once. // In a compound equality operator on an indexer the indices are placed on the stack twice. // That is to say, if you have: // // C().M(z : Z(), x : X(), y : Y()) // // then we can rewrite that into // // tempc = C() // tempz = Z() // tempc.M(X(), Y(), tempz) // // See, we can optimize away two of the temporaries, for x and y. But we cannot optimize away any of the // temporaries in // // C().Collection[z : Z(), x : X(), y : Y()] += 1; // // because we have to ensure not just that Z() happens first, but in addition that X() and Y() are only // called once. We have to generate this as // // tempc = C().Collection // tempz = Z() // tempx = X() // tempy = Y() // tempc[tempx, tempy, tempz] = tempc[tempx, tempy, tempz] + 1; // // Fortunately arguments to indexers are never ref or out, so we don't need to worry about that. // However, we can still do the optimization where constants are not stored in // temporaries; if we have // // C().Collection[z : 123, y : Y(), x : X()] += 1; // // Then we can generate that as // // tempc = C().Collection // tempx = X() // tempy = Y() // tempc[tempx, tempy, 123] = tempc[tempx, tempy, 123] + 1; ImmutableArray <BoundExpression> rewrittenArguments = VisitList(indexerAccess.Arguments); SyntaxNode syntax = indexerAccess.Syntax; PropertySymbol indexer = indexerAccess.Indexer; ImmutableArray <RefKind> argumentRefKinds = indexerAccess.ArgumentRefKindsOpt; bool expanded = indexerAccess.Expanded; ImmutableArray <int> argsToParamsOpt = indexerAccess.ArgsToParamsOpt; ImmutableArray <ParameterSymbol> parameters = indexer.Parameters; BoundExpression[] actualArguments = new BoundExpression[parameters.Length]; // The actual arguments that will be passed; one actual argument per formal parameter. ArrayBuilder <BoundAssignmentOperator> storesToTemps = ArrayBuilder <BoundAssignmentOperator> .GetInstance(rewrittenArguments.Length); ArrayBuilder <RefKind> refKinds = ArrayBuilder <RefKind> .GetInstance(parameters.Length, RefKind.None); // Step one: Store everything that is non-trivial into a temporary; record the // stores in storesToTemps and make the actual argument a reference to the temp. // Do not yet attempt to deal with params arrays or optional arguments. BuildStoresToTemps( expanded, argsToParamsOpt, parameters, argumentRefKinds, rewrittenArguments, forceLambdaSpilling: true, // lambdas must produce exactly one delegate so they must be spilled into a temp actualArguments, refKinds, storesToTemps); // Step two: If we have a params array, build the array and fill in the argument. if (expanded) { BoundExpression array = BuildParamsArray(syntax, indexer, argsToParamsOpt, rewrittenArguments, parameters, actualArguments[actualArguments.Length - 1]); BoundAssignmentOperator storeToTemp; var boundTemp = _factory.StoreToTemp(array, out storeToTemp); stores.Add(storeToTemp); temps.Add(boundTemp.LocalSymbol); actualArguments[actualArguments.Length - 1] = boundTemp; } // Step three: Now fill in the optional arguments. (Dev11 uses the getter for optional arguments in // compound assignments, but for deconstructions we use the setter if the getter is missing.) var accessor = indexer.GetOwnOrInheritedGetMethod() ?? indexer.GetOwnOrInheritedSetMethod(); InsertMissingOptionalArguments(syntax, accessor.Parameters, actualArguments, refKinds); // For a call, step four would be to optimize away some of the temps. However, we need them all to prevent // duplicate side-effects, so we'll skip that step. if (indexer.ContainingType.IsComImport) { RewriteArgumentsForComCall(parameters, actualArguments, refKinds, temps); } rewrittenArguments = actualArguments.AsImmutableOrNull(); foreach (BoundAssignmentOperator tempAssignment in storesToTemps) { temps.Add(((BoundLocal)tempAssignment.Left).LocalSymbol); stores.Add(tempAssignment); } storesToTemps.Free(); argumentRefKinds = GetRefKindsOrNull(refKinds); refKinds.Free(); // This is a temporary object that will be rewritten away before the lowering completes. return(new BoundIndexerAccess( syntax, transformedReceiver, indexer, rewrittenArguments, default(ImmutableArray <string>), argumentRefKinds, false, default(ImmutableArray <int>), null, indexerAccess.UseSetterForDefaultArgumentGeneration, indexerAccess.Type)); }
private BoundExpression MakeIndexerAccess( SyntaxNode syntax, BoundExpression rewrittenReceiver, PropertySymbol indexer, ImmutableArray <BoundExpression> rewrittenArguments, ImmutableArray <string> argumentNamesOpt, ImmutableArray <RefKind> argumentRefKindsOpt, bool expanded, ImmutableArray <int> argsToParamsOpt, TypeSymbol type, BoundIndexerAccess oldNodeOpt, bool isLeftOfAssignment) { // check for System.Array.[Length|LongLength] on a single dimensional array, // we have a special node for such cases. if (rewrittenReceiver != null && rewrittenReceiver.Type.IsArray() && !isLeftOfAssignment) { // NOTE: we are not interested in potential badness of Array.Length property. // If it is bad reference compare will not succeed. var specialIndexer = _compilation.GetSpecialTypeMember(SpecialMember.core_Array_T__item); if (ReferenceEquals(indexer.OriginalDefinition, specialIndexer)) { return(new BoundArrayAccess(syntax, rewrittenReceiver, rewrittenArguments[0], indexer.Type.TypeSymbol)); } } if (isLeftOfAssignment && indexer.RefKind == RefKind.None) { // This is an indexer set access. We return a BoundIndexerAccess node here. // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. return(oldNodeOpt != null? oldNodeOpt.Update(rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, null, isLeftOfAssignment, type) : new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, null, isLeftOfAssignment, type)); } else { var getMethod = indexer.GetOwnOrInheritedGetMethod(); Debug.Assert((object)getMethod != null); // 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> temps; rewrittenArguments = MakeArguments( syntax, rewrittenArguments, indexer, getMethod, expanded, argsToParamsOpt, ref argumentRefKindsOpt, out temps, enableCallerInfo: ThreeState.True); BoundExpression call = MakePropertyGetAccess(syntax, rewrittenReceiver, indexer, rewrittenArguments, getMethod); if (temps.IsDefaultOrEmpty) { return(call); } else { return(new BoundSequence( syntax, temps, ImmutableArray <BoundExpression> .Empty, call, type)); } } }