From ed2d70ad6386d43a2f0f6b9396a12d418c48c684 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 29 Nov 2021 06:35:00 -0800 Subject: [PATCH 1/2] Hold Receiver directly in bound node for implicit indexer access Related to #57855. --- .../Portable/Binder/Binder.ValueChecks.cs | 537 ++++++++++++------ .../Portable/Binder/Binder_Expressions.cs | 27 +- .../Portable/Binder/Binder_Invocation.cs | 2 +- .../CSharp/Portable/Binder/Binder_Patterns.cs | 2 +- .../Portable/Binder/Binder_Statements.cs | 17 +- .../Portable/BoundTree/BoundArrayAccess.cs | 14 - .../CSharp/Portable/BoundTree/BoundCall.cs | 17 - .../Portable/BoundTree/BoundDagEvaluation.cs | 2 + .../Portable/BoundTree/BoundExpression.cs | 72 +++ .../BoundTree/BoundImplicitIndexerAccess.cs | 18 +- .../Portable/BoundTree/BoundIndexerAccess.cs | 15 - ...ndInterpolatedStringArgumentPlaceholder.cs | 2 + .../CSharp/Portable/BoundTree/BoundNodes.xml | 13 +- .../CSharp/Portable/BoundTree/Expression.cs | 2 +- .../Portable/BoundTree/NullabilityRewriter.cs | 5 +- .../MemberSemanticModel.NodeMapBuilder.cs | 8 - .../Portable/FlowAnalysis/AbstractFlowPass.cs | 2 +- .../NullableWalker.DebugVerifier.cs | 1 + .../Portable/FlowAnalysis/NullableWalker.cs | 113 ++-- .../Generated/BoundNodes.xml.Generated.cs | 42 +- .../LocalRewriter_IndexerAccess.cs | 37 +- 21 files changed, 565 insertions(+), 383 deletions(-) delete mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs delete mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs delete mode 100644 src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 5e87a51db6152..147df6c131d80 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -396,12 +396,9 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // we need to handle properties and event in a special way even in an RValue case because of getters case BoundKind.PropertyAccess: case BoundKind.IndexerAccess: + case BoundKind.ImplicitIndexerAccess when ((BoundImplicitIndexerAccess)expr).IndexerOrSliceAccess.Kind == BoundKind.IndexerAccess: return CheckPropertyValueKind(node, expr, valueKind, checkingReceiver, diagnostics); - case BoundKind.ImplicitIndexerAccess: - var implicitIndexer = (BoundImplicitIndexerAccess)expr; - return CheckValueKind(node, implicitIndexer.IndexerOrSliceAccess, valueKind, checkingReceiver, diagnostics); - case BoundKind.EventAccess: return CheckEventValueKind((BoundEventAccess)expr, valueKind, diagnostics); } @@ -472,26 +469,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // array access is readwrite variable if the indexing expression is not System.Range case BoundKind.ArrayAccess: - { - if (RequiresRefAssignableVariable(valueKind)) - { - Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); - return false; - } - - var boundAccess = (BoundArrayAccess)expr; - if (boundAccess.Indices.Length == 1 && - TypeSymbol.Equals( - boundAccess.Indices[0].Type, - Compilation.GetWellKnownType(WellKnownType.System_Range), - TypeCompareKind.ConsiderEverything)) - { - // Range indexer is an rvalue - Error(diagnostics, GetStandardLvalueError(valueKind), node); - return false; - } - return true; - } + return checkArrayAccessValueKind(node, valueKind, ((BoundArrayAccess)expr).Indices, diagnostics); // pointer dereferencing is a readwrite variable case BoundKind.PointerIndirectionOperator: @@ -571,7 +549,7 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin case BoundKind.Call: var call = (BoundCall)expr; - return CheckCallValueKind(call, node, valueKind, checkingReceiver, diagnostics); + return CheckMethodReturnValueKind(call.Method, call.Syntax, node, valueKind, checkingReceiver, diagnostics); case BoundKind.FunctionPointerInvocation: return CheckMethodReturnValueKind(((BoundFunctionPointerInvocation)expr).FunctionPointer.Signature, @@ -582,7 +560,18 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin diagnostics); case BoundKind.ImplicitIndexerAccess: - throw ExceptionUtilities.Unreachable; + var implicitIndexer = (BoundImplicitIndexerAccess)expr; + switch (implicitIndexer.IndexerOrSliceAccess) + { + case BoundArrayAccess arrayAccess: + return checkArrayAccessValueKind(node, valueKind, arrayAccess.Indices, diagnostics); + + case BoundCall sliceAccess: + return CheckMethodReturnValueKind(sliceAccess.Method, sliceAccess.Syntax, node, valueKind, checkingReceiver, diagnostics); + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexer.IndexerOrSliceAccess.Kind); + } case BoundKind.ImplicitIndexerReceiverPlaceholder: break; @@ -622,6 +611,27 @@ internal bool CheckValueKind(SyntaxNode node, BoundExpression expr, BindValueKin // At this point we should have covered all the possible cases for anything that is not a strict RValue. Error(diagnostics, GetStandardLvalueError(valueKind), node); return false; + + bool checkArrayAccessValueKind(SyntaxNode node, BindValueKind valueKind, ImmutableArray indices, BindingDiagnosticBag diagnostics) + { + if (RequiresRefAssignableVariable(valueKind)) + { + Error(diagnostics, ErrorCode.ERR_RefLocalOrParamExpected, node); + return false; + } + + if (indices.Length == 1 && + TypeSymbol.Equals( + indices[0].Type, + Compilation.GetWellKnownType(WellKnownType.System_Range), + TypeCompareKind.ConsiderEverything)) + { + // Range indexer is an rvalue + Error(diagnostics, GetStandardLvalueError(valueKind), node); + return false; + } + return true; + } } private static bool CheckNotNamespaceOrType(BoundExpression expr, BindingDiagnosticBag diagnostics) @@ -988,9 +998,6 @@ private static bool RequiresVariableReceiver(BoundExpression receiver, Symbol sy && receiver?.Type?.IsValueType == true; } - private bool CheckCallValueKind(BoundCall call, SyntaxNode node, BindValueKind valueKind, bool checkingReceiver, BindingDiagnosticBag diagnostics) - => CheckMethodReturnValueKind(call.Method, call.Syntax, node, valueKind, checkingReceiver, diagnostics); - protected bool CheckMethodReturnValueKind( MethodSymbol methodSymbol, SyntaxNode callSyntaxOpt, @@ -2096,56 +2103,108 @@ internal static uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainin return GetRefEscape(eventAccess.ReceiverOpt, scopeOfTheContainingExpression); case BoundKind.Call: - var call = (BoundCall)expr; - - var methodSymbol = call.Method; - if (methodSymbol.RefKind == RefKind.None) { - break; - } + var call = (BoundCall)expr; - return GetInvocationEscapeScope( - call.Method, - call.ReceiverOpt, - methodSymbol.Parameters, - call.Arguments, - call.ArgumentRefKindsOpt, - call.ArgsToParamsOpt, - scopeOfTheContainingExpression, - isRefEscape: true); + var methodSymbol = call.Method; + if (methodSymbol.RefKind == RefKind.None) + { + break; + } + + return GetInvocationEscapeScope( + call.Method, + call.ReceiverOpt, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: true); + } case BoundKind.FunctionPointerInvocation: - var ptrInvocation = (BoundFunctionPointerInvocation)expr; + { + var ptrInvocation = (BoundFunctionPointerInvocation)expr; + + var methodSymbol = ptrInvocation.FunctionPointer.Signature; + if (methodSymbol.RefKind == RefKind.None) + { + break; + } + + return GetInvocationEscapeScope( + methodSymbol, + receiverOpt: null, + methodSymbol.Parameters, + ptrInvocation.Arguments, + ptrInvocation.ArgumentRefKindsOpt, + argsToParamsOpt: default, + scopeOfTheContainingExpression, + isRefEscape: true); + } - methodSymbol = ptrInvocation.FunctionPointer.Signature; - if (methodSymbol.RefKind == RefKind.None) + case BoundKind.IndexerAccess: { - break; + var indexerAccess = (BoundIndexerAccess)expr; + var indexerSymbol = indexerAccess.Indexer; + + return GetInvocationEscapeScope( + indexerSymbol, + indexerAccess.ReceiverOpt, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: true); } - return GetInvocationEscapeScope( - methodSymbol, - receiverOpt: null, - methodSymbol.Parameters, - ptrInvocation.Arguments, - ptrInvocation.ArgumentRefKindsOpt, - argsToParamsOpt: default, - scopeOfTheContainingExpression, - isRefEscape: true); + case BoundKind.ImplicitIndexerAccess: + var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; - case BoundKind.IndexerAccess: - var indexerAccess = (BoundIndexerAccess)expr; - var indexerSymbol = indexerAccess.Indexer; + // Note: the Argument and LengthOrCountAccess use is purely local - return GetInvocationEscapeScope( - indexerSymbol, - indexerAccess.ReceiverOpt, - indexerSymbol.Parameters, - indexerAccess.Arguments, - indexerAccess.ArgumentRefKindsOpt, - indexerAccess.ArgsToParamsOpt, - scopeOfTheContainingExpression, - isRefEscape: true); + switch (implicitIndexerAccess.IndexerOrSliceAccess) + { + case BoundIndexerAccess indexerAccess: + var indexerSymbol = indexerAccess.Indexer; + + return GetInvocationEscapeScope( + indexerSymbol, + implicitIndexerAccess.Receiver, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: true); + + case BoundArrayAccess: + // array elements are readwrite variables + return Binder.ExternalScope; + + case BoundCall call: + var methodSymbol = call.Method; + if (methodSymbol.RefKind == RefKind.None) + { + break; + } + + return GetInvocationEscapeScope( + call.Method, + implicitIndexerAccess.Receiver, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: true); + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.IndexerOrSliceAccess.Kind); + } + break; case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; @@ -2295,55 +2354,113 @@ internal static bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint return CheckFieldLikeEventRefEscape(node, eventAccess, escapeFrom, escapeTo, diagnostics); case BoundKind.Call: - var call = (BoundCall)expr; - - var methodSymbol = call.Method; - if (methodSymbol.RefKind == RefKind.None) { - break; - } + var call = (BoundCall)expr; - return CheckInvocationEscape( - call.Syntax, - methodSymbol, - call.ReceiverOpt, - methodSymbol.Parameters, - call.Arguments, - call.ArgumentRefKindsOpt, - call.ArgsToParamsOpt, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: true); + var methodSymbol = call.Method; + if (methodSymbol.RefKind == RefKind.None) + { + break; + } - case BoundKind.IndexerAccess: - var indexerAccess = (BoundIndexerAccess)expr; - var indexerSymbol = indexerAccess.Indexer; + return CheckInvocationEscape( + call.Syntax, + methodSymbol, + call.ReceiverOpt, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: true); + } - if (indexerSymbol.RefKind == RefKind.None) + case BoundKind.IndexerAccess: { - break; - } + var indexerAccess = (BoundIndexerAccess)expr; + var indexerSymbol = indexerAccess.Indexer; - return CheckInvocationEscape( - indexerAccess.Syntax, - indexerSymbol, - indexerAccess.ReceiverOpt, - indexerSymbol.Parameters, - indexerAccess.Arguments, - indexerAccess.ArgumentRefKindsOpt, - indexerAccess.ArgsToParamsOpt, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: true); + if (indexerSymbol.RefKind == RefKind.None) + { + break; + } + + return CheckInvocationEscape( + indexerAccess.Syntax, + indexerSymbol, + indexerAccess.ReceiverOpt, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: true); + } case BoundKind.ImplicitIndexerAccess: var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; - // Note: the LengthOrCountAccess use is purely local - return CheckRefEscape(node, implicitIndexerAccess.IndexerOrSliceAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); + + // Note: the Argument and LengthOrCountAccess use is purely local + + switch (implicitIndexerAccess.IndexerOrSliceAccess) + { + case BoundIndexerAccess indexerAccess: + var indexerSymbol = indexerAccess.Indexer; + + if (indexerSymbol.RefKind == RefKind.None) + { + break; + } + + return CheckInvocationEscape( + indexerAccess.Syntax, + indexerSymbol, + implicitIndexerAccess.Receiver, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: true); + + case BoundArrayAccess: + // array elements are readwrite variables + return true; + + case BoundCall call: + var methodSymbol = call.Method; + if (methodSymbol.RefKind == RefKind.None) + { + break; + } + + return CheckInvocationEscape( + call.Syntax, + methodSymbol, + implicitIndexerAccess.Receiver, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: true); + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.IndexerOrSliceAccess.Kind); + } + break; case BoundKind.FunctionPointerInvocation: var functionPointerInvocation = (BoundFunctionPointerInvocation)expr; @@ -2538,17 +2655,19 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin return GetValEscape(fieldAccess.ReceiverOpt, scopeOfTheContainingExpression); case BoundKind.Call: - var call = (BoundCall)expr; + { + var call = (BoundCall)expr; - return GetInvocationEscapeScope( - call.Method, - call.ReceiverOpt, - call.Method.Parameters, - call.Arguments, - call.ArgumentRefKindsOpt, - call.ArgsToParamsOpt, - scopeOfTheContainingExpression, - isRefEscape: false); + return GetInvocationEscapeScope( + call.Method, + call.ReceiverOpt, + call.Method.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: false); + } case BoundKind.FunctionPointerInvocation: var ptrInvocation = (BoundFunctionPointerInvocation)expr; @@ -2565,18 +2684,20 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin isRefEscape: false); case BoundKind.IndexerAccess: - var indexerAccess = (BoundIndexerAccess)expr; - var indexerSymbol = indexerAccess.Indexer; - - return GetInvocationEscapeScope( - indexerSymbol, - indexerAccess.ReceiverOpt, - indexerSymbol.Parameters, - indexerAccess.Arguments, - indexerAccess.ArgumentRefKindsOpt, - indexerAccess.ArgsToParamsOpt, - scopeOfTheContainingExpression, - isRefEscape: false); + { + var indexerAccess = (BoundIndexerAccess)expr; + var indexerSymbol = indexerAccess.Indexer; + + return GetInvocationEscapeScope( + indexerSymbol, + indexerAccess.ReceiverOpt, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: false); + } case BoundKind.ImplicitIndexerReceiverPlaceholder: return ((BoundImplicitIndexerReceiverPlaceholder)expr).ValEscape; @@ -2589,8 +2710,42 @@ internal static uint GetValEscape(BoundExpression expr, uint scopeOfTheContainin case BoundKind.ImplicitIndexerAccess: var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; - // Note: the LengthOrCountAccess use is purely local - return GetValEscape(implicitIndexerAccess.IndexerOrSliceAccess, scopeOfTheContainingExpression); + + // Note: the Argument and LengthOrCountAccess use is purely local + + switch (implicitIndexerAccess.IndexerOrSliceAccess) + { + case BoundIndexerAccess indexerAccess: + var indexerSymbol = indexerAccess.Indexer; + + return GetInvocationEscapeScope( + indexerSymbol, + implicitIndexerAccess.Receiver, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: false); + + case BoundArrayAccess: + // only possible in error cases (if possible at all) + return scopeOfTheContainingExpression; + + case BoundCall call: + return GetInvocationEscapeScope( + call.Method, + implicitIndexerAccess.Receiver, + call.Method.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + scopeOfTheContainingExpression, + isRefEscape: false); + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.IndexerOrSliceAccess.Kind); + } case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; @@ -2943,22 +3098,24 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint return CheckValEscape(node, fieldAccess.ReceiverOpt, escapeFrom, escapeTo, true, diagnostics); case BoundKind.Call: - var call = (BoundCall)expr; - var methodSymbol = call.Method; - - return CheckInvocationEscape( - call.Syntax, - methodSymbol, - call.ReceiverOpt, - methodSymbol.Parameters, - call.Arguments, - call.ArgumentRefKindsOpt, - call.ArgsToParamsOpt, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: false); + { + var call = (BoundCall)expr; + var methodSymbol = call.Method; + + return CheckInvocationEscape( + call.Syntax, + methodSymbol, + call.ReceiverOpt, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: false); + } case BoundKind.FunctionPointerInvocation: var ptrInvocation = (BoundFunctionPointerInvocation)expr; @@ -2979,22 +3136,24 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint isRefEscape: false); case BoundKind.IndexerAccess: - var indexerAccess = (BoundIndexerAccess)expr; - var indexerSymbol = indexerAccess.Indexer; - - return CheckInvocationEscape( - indexerAccess.Syntax, - indexerSymbol, - indexerAccess.ReceiverOpt, - indexerSymbol.Parameters, - indexerAccess.Arguments, - indexerAccess.ArgumentRefKindsOpt, - indexerAccess.ArgsToParamsOpt, - checkingReceiver, - escapeFrom, - escapeTo, - diagnostics, - isRefEscape: false); + { + var indexerAccess = (BoundIndexerAccess)expr; + var indexerSymbol = indexerAccess.Indexer; + + return CheckInvocationEscape( + indexerAccess.Syntax, + indexerSymbol, + indexerAccess.ReceiverOpt, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: false); + } case BoundKind.ImplicitIndexerReceiverPlaceholder: if (((BoundImplicitIndexerReceiverPlaceholder)expr).ValEscape > escapeTo) @@ -3022,8 +3181,52 @@ internal static bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint case BoundKind.ImplicitIndexerAccess: var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; - // Note: the LengthOrCountAccess use is purely local - return CheckValEscape(node, implicitIndexerAccess.IndexerOrSliceAccess, escapeFrom, escapeTo, checkingReceiver, diagnostics); + + // Note: the Argument and LengthOrCountAccess use is purely local + + switch (implicitIndexerAccess.IndexerOrSliceAccess) + { + case BoundIndexerAccess indexerAccess: + var indexerSymbol = indexerAccess.Indexer; + + return CheckInvocationEscape( + indexerAccess.Syntax, + indexerSymbol, + implicitIndexerAccess.Receiver, + indexerSymbol.Parameters, + indexerAccess.Arguments, + indexerAccess.ArgumentRefKindsOpt, + indexerAccess.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: false); + + case BoundArrayAccess: + // only possible in error cases (if possible at all) + return false; + + case BoundCall call: + var methodSymbol = call.Method; + + return CheckInvocationEscape( + call.Syntax, + methodSymbol, + implicitIndexerAccess.Receiver, + methodSymbol.Parameters, + call.Arguments, + call.ArgumentRefKindsOpt, + call.ArgsToParamsOpt, + checkingReceiver, + escapeFrom, + escapeTo, + diagnostics, + isRefEscape: false); + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.IndexerOrSliceAccess.Kind); + } case BoundKind.PropertyAccess: var propertyAccess = (BoundPropertyAccess)expr; diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index e6df71f377a8a..3556cd37bd12f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -7565,15 +7565,16 @@ private BoundExpression BindArrayAccess(SyntaxNode node, BoundExpression expr, A Debug.Assert(convertedArguments.Length == 1); var int32 = GetSpecialType(SpecialType.System_Int32, diagnostics, node); - var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(expr.Syntax, GetValEscape(expr, LocalScopeDepth), expr.Type) { WasCompilerGenerated = true }; + var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(expr.Syntax, GetValEscape(expr, LocalScopeDepth), isEquivalentToThisReference: expr.IsEquivalentToThisReference, expr.Type) { WasCompilerGenerated = true }; var argumentPlaceholders = ImmutableArray.Create(new BoundImplicitIndexerValuePlaceholder(convertedArguments[0].Syntax, int32) { WasCompilerGenerated = true }); return new BoundImplicitIndexerAccess( node, + receiver: expr, argument: convertedArguments[0], lengthOrCountAccess: new BoundArrayLength(node, receiverPlaceholder, int32) { WasCompilerGenerated = true }, receiverPlaceholder, - indexerOrSliceAccess: new BoundArrayAccess(node, expr, ImmutableArray.CastUp(argumentPlaceholders), resultType), + indexerOrSliceAccess: new BoundArrayAccess(node, receiverPlaceholder, ImmutableArray.CastUp(argumentPlaceholders), resultType) { WasCompilerGenerated = true }, argumentPlaceholders, resultType); } @@ -8103,8 +8104,8 @@ private bool TryBindIndexOrRangeImplicitIndexer( bool argIsIndex = argIsIndexNotRange.Value(); var receiverValEscape = GetValEscape(receiver, LocalScopeDepth); - var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(receiver.Syntax, receiverValEscape, receiver.Type) { WasCompilerGenerated = true }; - if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, receiver, argIsIndex: argIsIndex, + var receiverPlaceholder = new BoundImplicitIndexerReceiverPlaceholder(receiver.Syntax, receiverValEscape, isEquivalentToThisReference: receiver.IsEquivalentToThisReference, receiver.Type) { WasCompilerGenerated = true }; + if (!TryBindIndexOrRangeImplicitIndexerParts(syntax, receiverPlaceholder, argIsIndex: argIsIndex, out var lengthOrCountAccess, out var indexerOrSliceAccess, out var argumentPlaceholders, diagnostics)) { return false; @@ -8116,6 +8117,7 @@ private bool TryBindIndexOrRangeImplicitIndexer( implicitIndexerAccess = new BoundImplicitIndexerAccess( syntax, + receiver: receiver, argument: BindToNaturalType(argument, diagnostics), lengthOrCountAccess: lengthOrCountAccess, receiverPlaceholder, @@ -8152,13 +8154,10 @@ void checkWellKnown(WellKnownMember member) /// /// Finds pattern-based implicit indexer and Length/Count property. - /// We'll use the receiver placeholder to bind the length access, but the real - /// receiver to bind the indexer access. /// private bool TryBindIndexOrRangeImplicitIndexerParts( SyntaxNode syntax, - BoundExpression receiverPlaceholder, - BoundExpression receiver, + BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? lengthOrCountAccess, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, @@ -8175,8 +8174,8 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // 2. For Index: Has an accessible indexer with a single int parameter // For Range: Has an accessible Slice method that takes two int parameters - if (TryBindLengthOrCount(syntax, receiverPlaceholder, receiver, out lengthOrCountAccess, diagnostics) && - tryBindUnderlyingIndexerOrSliceAccess(syntax, receiver, argIsIndex, out indexerOrSliceAccess, out argumentPlaceholders, diagnostics)) + if (TryBindLengthOrCount(syntax, receiverPlaceholder, out lengthOrCountAccess, diagnostics) && + tryBindUnderlyingIndexerOrSliceAccess(syntax, receiverPlaceholder, argIsIndex, out indexerOrSliceAccess, out argumentPlaceholders, diagnostics)) { return true; } @@ -8191,7 +8190,7 @@ private bool TryBindIndexOrRangeImplicitIndexerParts( // - for Range indexer, this will find `Slice(int, int)` or `string.Substring(int, int)`. bool tryBindUnderlyingIndexerOrSliceAccess( SyntaxNode syntax, - BoundExpression receiver, + BoundImplicitIndexerReceiverPlaceholder receiver, bool argIsIndex, [NotNullWhen(true)] out BoundExpression? indexerOrSliceAccess, out ImmutableArray argumentPlaceholders, @@ -8324,8 +8323,7 @@ void makeCall(SyntaxNode syntax, BoundExpression receiver, MethodSymbol method, private bool TryBindLengthOrCount( SyntaxNode syntax, - BoundExpression receiverPlaceholder, - BoundExpression? receiver, + BoundValuePlaceholderBase receiverPlaceholder, out BoundExpression lengthOrCountAccess, BindingDiagnosticBag diagnostics) { @@ -8338,9 +8336,6 @@ private bool TryBindLengthOrCount( lengthOrCountAccess = BindPropertyAccess(syntax, receiverPlaceholder, lengthOrCountProperty, diagnostics, lookupResult.Kind, hasErrors: false).MakeCompilerGenerated(); lengthOrCountAccess = CheckValue(lengthOrCountAccess, BindValueKind.RValue, diagnostics); - // CheckValue does not handle placeholders well yet, so we're doing some extra check for `this` receiver - CheckImplicitThisCopyInReadOnlyMember(receiver, lengthOrCountProperty.GetOwnOrInheritedGetMethod(), diagnostics); - lookupResult.Free(); return true; } diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 3dd80a377c308..eac89f01308ad 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1464,7 +1464,7 @@ internal bool CheckImplicitThisCopyInReadOnlyMember(BoundExpression receiver, Me { // For now we are warning only in implicit copy scenarios that are only possible with readonly members. // Eventually we will warn on implicit value copies in more scenarios. See https://github.com/dotnet/roslyn/issues/33968. - if (receiver is BoundThisReference && + if (receiver?.IsEquivalentToThisReference == true && receiver.Type.IsValueType && ContainingMemberOrLambda is MethodSymbol containingMethod && containingMethod.IsEffectivelyReadOnly && diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index bf19f78e71699..0bd74d30f154f 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -359,7 +359,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu } else { - hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, receiver: null, out lengthAccess, diagnostics); + hasErrors |= !TryBindLengthOrCount(node, receiverPlaceholder, out lengthAccess, diagnostics); } var analyzedArguments = AnalyzedArguments.GetInstance(); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index b7cbdca662298..7506cb89b7456 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -1566,7 +1566,22 @@ internal static PropertySymbol GetPropertySymbol(BoundExpression expr, out Bound case BoundKind.ImplicitIndexerAccess: { var implicitIndexerAccess = (BoundImplicitIndexerAccess)expr; - propertySymbol = GetPropertySymbol(implicitIndexerAccess.IndexerOrSliceAccess, out receiver, out propertySyntax); + + switch (implicitIndexerAccess.IndexerOrSliceAccess) + { + case BoundIndexerAccess indexerAccess: + propertySymbol = indexerAccess.Indexer; + receiver = implicitIndexerAccess.Receiver; + break; + + case BoundCall or BoundArrayAccess: + receiver = null; + propertySyntax = null; + return null; + + default: + throw ExceptionUtilities.UnexpectedValue(implicitIndexerAccess.IndexerOrSliceAccess.Kind); + } } break; default: diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs deleted file mode 100644 index 8f3678ab17596..0000000000000 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundArrayAccess.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal partial class BoundArrayAccess - { - internal BoundArrayAccess WithReceiver(BoundExpression receiver) - { - return this.Update(receiver, this.Indices, this.Type); - } - } -} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs deleted file mode 100644 index af2bc9a559501..0000000000000 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundCall.cs +++ /dev/null @@ -1,17 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -using System.Diagnostics; - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal partial class BoundCall - { - internal BoundCall WithReceiver(BoundExpression receiver) - { - Debug.Assert(!this.InvokedAsExtensionMethod); - return this.Update(receiver, this.Method, this.Arguments); - } - } -} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs index a7b8a0cbd06c4..21c4a6100604c 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundDagEvaluation.cs @@ -52,9 +52,11 @@ private Symbol? Symbol { switch (indexerAccess) { + // array[Range] case BoundArrayAccess arrayAccess: return arrayAccess.Expression.Type; + // array[Index] case BoundImplicitIndexerAccess { IndexerOrSliceAccess: BoundArrayAccess arrayAccess }: return arrayAccess.Expression.Type; diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs index 6e6a2fa580741..cae8a1fbd0c01 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundExpression.cs @@ -123,6 +123,78 @@ public virtual bool SuppressVirtualCalls public CodeAnalysis.ITypeSymbol? GetPublicTypeSymbol() => Type?.GetITypeSymbol(TopLevelNullability.FlowState.ToAnnotation()); + + public virtual bool IsEquivalentToThisReference => false; + } + + internal partial class BoundValuePlaceholderBase + { + public abstract override bool IsEquivalentToThisReference { get; } + } + + internal partial class BoundValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + } + + internal partial class BoundInterpolatedStringHandlerPlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; + } + + internal partial class BoundDeconstructValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; // Preserving old behavior + } + + internal partial class BoundTupleOperandPlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + } + + internal partial class BoundAwaitableValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; // Preserving old behavior + } + + internal partial class BoundDisposableValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; + } + + internal partial class BoundObjectOrCollectionValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; + } + + internal partial class BoundImplicitIndexerValuePlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + } + + internal partial class BoundListPatternReceiverPlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; + } + + internal partial class BoundListPatternIndexPlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + } + + internal partial class BoundSlicePatternReceiverPlaceholder + { + public sealed override bool IsEquivalentToThisReference => false; + } + + internal partial class BoundSlicePatternRangePlaceholder + { + public sealed override bool IsEquivalentToThisReference => throw ExceptionUtilities.Unreachable; + } + + internal partial class BoundThisReference + { + public sealed override bool IsEquivalentToThisReference => true; } internal partial class BoundPassByCopy diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs index e86bc8d6e47b7..ae4f2da2ff75e 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundImplicitIndexerAccess.cs @@ -11,26 +11,10 @@ internal partial class BoundImplicitIndexerAccess { internal BoundImplicitIndexerAccess WithLengthOrCountAccess(BoundExpression lengthOrCountAccess) { - return this.Update(this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, + return this.Update(this.Receiver, this.Argument, lengthOrCountAccess, this.ReceiverPlaceholder, this.IndexerOrSliceAccess, this.ArgumentPlaceholders, this.Type); } - // The receiver expression is the receiver of IndexerAccess. - // The LengthOrCountAccess uses a placeholder as receiver. - internal BoundExpression GetReceiver() - { - var receiver = this.IndexerOrSliceAccess switch - { - BoundIndexerAccess { ReceiverOpt: var r } => r, - BoundCall { ReceiverOpt: var r } => r, - BoundArrayAccess { Expression: var r } => r, - _ => throw ExceptionUtilities.UnexpectedValue(this.IndexerOrSliceAccess.Kind) - }; - - Debug.Assert(receiver is not null); - return receiver; - } - private partial void Validate() { Debug.Assert(LengthOrCountAccess is BoundPropertyAccess or BoundArrayLength or BoundLocal or BoundBadExpression); diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs deleted file mode 100644 index cb3134de33b20..0000000000000 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundIndexerAccess.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.CodeAnalysis.CSharp -{ - internal partial class BoundIndexerAccess - { - internal BoundIndexerAccess WithReceiver(BoundExpression receiver) - { - return this.Update(receiver, this.Indexer, this.Arguments, this.ArgumentNamesOpt, - this.ArgumentRefKindsOpt, this.Expanded, this.ArgsToParamsOpt, this.DefaultArguments, this.Type); - } - } -} diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs index 56043a700a00c..377d2e008b8b5 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundInterpolatedStringArgumentPlaceholder.cs @@ -9,5 +9,7 @@ internal partial class BoundInterpolatedStringArgumentPlaceholder public const int InstanceParameter = -1; public const int TrailingConstructorValidityParameter = -2; public const int UnspecifiedParameter = -3; + + public sealed override bool IsEquivalentToThisReference => throw Roslyn.Utilities.ExceptionUtilities.Unreachable; } } diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index f1da70d126e61..a41f9b11afcd0 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -147,6 +147,7 @@ + @@ -2069,21 +2070,21 @@ + + + - + - + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs index c4e7e4e5e9d4e..2c81e32a64bc4 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Expression.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Expression.cs @@ -177,7 +177,7 @@ internal partial class BoundPassByCopy internal partial class BoundImplicitIndexerAccess { - protected override ImmutableArray Children => ImmutableArray.Create(this.GetReceiver(), Argument); + protected override ImmutableArray Children => ImmutableArray.Create(this.Receiver, Argument); } internal partial class BoundFunctionPointerInvocation : IBoundInvalidNode diff --git a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs index 0b6e7ba72394c..db5389f0b60c1 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/NullabilityRewriter.cs @@ -164,6 +164,7 @@ Symbol remapLocal(SourceLocalSymbol local) public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { + BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; BoundExpression indexerAccess = (BoundExpression)this.Visit(node.IndexerOrSliceAccess); @@ -171,12 +172,12 @@ Symbol remapLocal(SourceLocalSymbol local) if (_updatedNullabilities.TryGetValue(node, out (NullabilityInfo Info, TypeSymbol? Type) infoAndType)) { - updatedNode = node.Update(argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, infoAndType.Type!); + updatedNode = node.Update(receiver, argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, infoAndType.Type!); updatedNode.TopLevelNullability = infoAndType.Info; } else { - updatedNode = node.Update(argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, node.Type); + updatedNode = node.Update(receiver, argument, lengthOrCountAccess, node.ReceiverPlaceholder, indexerAccess, node.ArgumentPlaceholders, node.Type); } return updatedNode; } diff --git a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs index 5af55ba42c494..fe0f0f661c74a 100644 --- a/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs +++ b/src/Compilers/CSharp/Portable/Compilation/MemberSemanticModel.NodeMapBuilder.cs @@ -258,14 +258,6 @@ public override BoundNode VisitQueryClause(BoundQueryClause node) return null; } - public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) - { - // We only need to visit receiver and the argument. SemanticModel isn't interested in anything else. - Visit(node.GetReceiver()); - this.Visit(node.Argument); - return null; - } - public override BoundNode VisitRangeVariable(BoundRangeVariable node) { return null; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs index a06dd736cfd54..a6cebb4193042 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/AbstractFlowPass.cs @@ -1393,7 +1393,7 @@ public override BoundNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess // 2. The argument to the access // 3. The Count or Length method off the receiver // 4. The underlying indexer access or method call - VisitRvalue(node.GetReceiver()); + VisitRvalue(node.Receiver); VisitRvalue(node.Argument); return null; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs index a1a719d0dc63c..d116bb638d3e6 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.DebugVerifier.cs @@ -276,6 +276,7 @@ private void VisitBinaryOperatorChildren(BoundBinaryOperatorBase node) public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { + Visit(node.Receiver); Visit(node.Argument); Visit(node.IndexerOrSliceAccess); return null; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index b9896e44917db..a7b61b3ff3d17 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -187,16 +187,7 @@ public VisitArgumentResult(VisitResult visitResult, Optional stateFo /// private PooledDictionary? _methodGroupReceiverMapOpt; - /// - /// State of awaitable expressions, for substitution in placeholders within GetAwaiter calls. - /// - private PooledDictionary? _awaitablePlaceholdersOpt; - - // Note: at the moment, we have two kinds of placeholders. Those that we visit first (and store the result from and substitute later) - // and those that we visit after subsitution. - // Ideally, we should be able to align on the first kind of design, then we could remove the second kind. - // Removing this map is tracked as part of https://github.com/dotnet/roslyn/issues/57855 - private PooledDictionary? _placeholdersToUnvisitedExpressionOpt; + private PooledDictionary? _resultForPlaceholdersOpt; /// /// Variables instances for each lambda or local function defined within the analyzed region. @@ -377,8 +368,7 @@ protected override void Free() AssertNoPlaceholderReplacements(); _nestedFunctionVariables?.Free(); - _awaitablePlaceholdersOpt?.Free(); - _placeholdersToUnvisitedExpressionOpt?.Free(); + _resultForPlaceholdersOpt?.Free(); _methodGroupReceiverMapOpt?.Free(); _placeholderLocalsOpt?.Free(); _variables.Free(); @@ -463,49 +453,12 @@ protected override int AddVariable(VariableIdentifier identifier) return _variables.Add(identifier); } - /// - /// This is for placeholders to unvisited expressions - /// - private void AddPlaceholder(BoundValuePlaceholderBase placeholder, BoundExpression unvisited) - { - _placeholdersToUnvisitedExpressionOpt ??= PooledDictionary.GetInstance(); - _placeholdersToUnvisitedExpressionOpt.Add(placeholder, unvisited); - } - - /// - /// This is for placeholders to unvisited expressions - /// - private void RemovePlaceholder(BoundValuePlaceholderBase placeholder) - { - Debug.Assert(_placeholdersToUnvisitedExpressionOpt is not null); - _placeholdersToUnvisitedExpressionOpt.Remove(placeholder); - } - - /// - /// This is for placeholders to unvisited expressions - /// - private bool TryReplacePlaceholder(BoundValuePlaceholderBase placeholder, [NotNullWhen(true)] out BoundExpression? unvisited) - { - if (_placeholdersToUnvisitedExpressionOpt is not null && - _placeholdersToUnvisitedExpressionOpt.TryGetValue(placeholder, out unvisited)) - { - return true; - } - - unvisited = null; - return false; - } - [Conditional("DEBUG")] private void AssertNoPlaceholderReplacements() { - if (_awaitablePlaceholdersOpt is not null) + if (_resultForPlaceholdersOpt is not null) { - Debug.Assert(_awaitablePlaceholdersOpt.Count == 0); - } - if (_placeholdersToUnvisitedExpressionOpt is not null) - { - Debug.Assert(_placeholdersToUnvisitedExpressionOpt.Count == 0); + Debug.Assert(_resultForPlaceholdersOpt.Count == 0); } } @@ -4222,11 +4175,11 @@ private static void MarkSlotsAsNotNull(ArrayBuilder slots, ref LocalState s private void LearnFromNonNullTest(BoundExpression expression, ref LocalState state) { - if (expression.Kind == BoundKind.AwaitableValuePlaceholder) + if (expression is BoundValuePlaceholderBase placeholder) { - if (_awaitablePlaceholdersOpt != null && _awaitablePlaceholdersOpt.TryGetValue((BoundAwaitableValuePlaceholder)expression, out var value)) + if (_resultForPlaceholdersOpt != null && _resultForPlaceholdersOpt.TryGetValue(placeholder, out var value)) { - expression = value.AwaitableExpression; + expression = value.Replacement; } else { @@ -8753,11 +8706,14 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { - // The argument will be visited as part of VisitImplicitIndexerValuePlaceholder (for the first argument) - // to maintain proper order of evaluation - AddPlaceholder(node.ArgumentPlaceholders[0], node.Argument); + VisitRvalue(node.Receiver); + var receiverResult = _visitResult; + VisitRvalue(node.Argument); + + EnsurePlaceholdersToResultMap(); + _resultForPlaceholdersOpt.Add(node.ReceiverPlaceholder, (node.Receiver, receiverResult)); VisitRvalue(node.IndexerOrSliceAccess); - RemovePlaceholder(node.ArgumentPlaceholders[0]); + _resultForPlaceholdersOpt.Remove(node.ReceiverPlaceholder); SetResult(node, ResultType, LvalueResultType); return null; @@ -8765,19 +8721,13 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitImplicitIndexerValuePlaceholder(BoundImplicitIndexerValuePlaceholder node) { - // We use this placeholder as trigger to visit the Argument of implicit indexer access (after its Receiver) - if (TryReplacePlaceholder(node, out var unvisited)) - { - VisitRvalue(unvisited); - } - SetNotNullResult(node); return null; } public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { - SetNotNullResult(node); + VisitplaceholderWithReplacement(node); return null; } @@ -9030,11 +8980,11 @@ protected override void VisitForEachExpression(BoundForEachStatement node) { var moveNextAsyncMethod = (MethodSymbol)AsMemberOfType(reinferredGetEnumeratorMethod.ReturnType, node.EnumeratorInfoOpt.MoveNextInfo.Method); - EnsureAwaitablePlaceholdersInitialized(); + EnsurePlaceholdersToResultMap(); var result = new VisitResult(GetReturnTypeWithState(moveNextAsyncMethod), moveNextAsyncMethod.ReturnTypeWithAnnotations); - _awaitablePlaceholdersOpt.Add(moveNextPlaceholder, (moveNextPlaceholder, result)); + _resultForPlaceholdersOpt.Add(moveNextPlaceholder, (moveNextPlaceholder, result)); Visit(awaitMoveNextInfo); - _awaitablePlaceholdersOpt.Remove(moveNextPlaceholder); + _resultForPlaceholdersOpt.Remove(moveNextPlaceholder); } // Analyze `await DisposeAsync()` @@ -9046,9 +8996,9 @@ protected override void VisitForEachExpression(BoundForEachStatement node) { Debug.Assert(disposalPlaceholder is not null); var disposeAsyncMethod = (MethodSymbol)AsMemberOfType(reinferredGetEnumeratorMethod.ReturnType, originalDisposeMethod); - EnsureAwaitablePlaceholdersInitialized(); + EnsurePlaceholdersToResultMap(); var result = new VisitResult(GetReturnTypeWithState(disposeAsyncMethod), disposeAsyncMethod.ReturnTypeWithAnnotations); - _awaitablePlaceholdersOpt.Add(disposalPlaceholder, (disposalPlaceholder, result)); + _resultForPlaceholdersOpt.Add(disposalPlaceholder, (disposalPlaceholder, result)); addedPlaceholder = true; } @@ -9056,7 +9006,7 @@ protected override void VisitForEachExpression(BoundForEachStatement node) if (addedPlaceholder) { - _awaitablePlaceholdersOpt!.Remove(disposalPlaceholder!); + _resultForPlaceholdersOpt!.Remove(disposalPlaceholder!); } } } @@ -9426,10 +9376,10 @@ private TypeWithState InferResultNullabilityOfBinaryLogicalOperator(BoundExpress var placeholder = awaitableInfo.AwaitableInstancePlaceholder; Debug.Assert(placeholder is object); - EnsureAwaitablePlaceholdersInitialized(); - _awaitablePlaceholdersOpt.Add(placeholder, (node.Expression, _visitResult)); + EnsurePlaceholdersToResultMap(); + _resultForPlaceholdersOpt.Add(placeholder, (node.Expression, _visitResult)); Visit(awaitableInfo); - _awaitablePlaceholdersOpt.Remove(placeholder); + _resultForPlaceholdersOpt.Remove(placeholder); if (node.Type.IsValueType || node.HasErrors || awaitableInfo.GetResult is null) { @@ -10045,15 +9995,21 @@ protected override void VisitCatchBlock(BoundCatchBlock node, ref LocalState fin return null; } - [MemberNotNull(nameof(_awaitablePlaceholdersOpt))] - private void EnsureAwaitablePlaceholdersInitialized() + [MemberNotNull(nameof(_resultForPlaceholdersOpt))] + private void EnsurePlaceholdersToResultMap() { - _awaitablePlaceholdersOpt ??= PooledDictionary.GetInstance(); + _resultForPlaceholdersOpt ??= PooledDictionary.GetInstance(); } public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) { - if (_awaitablePlaceholdersOpt != null && _awaitablePlaceholdersOpt.TryGetValue(node, out var value)) + VisitplaceholderWithReplacement(node); + return null; + } + + private void VisitplaceholderWithReplacement(BoundValuePlaceholderBase node) + { + if (_resultForPlaceholdersOpt != null && _resultForPlaceholdersOpt.TryGetValue(node, out var value)) { var result = value.Result; SetResult(node, result.RValueType, result.LValueType); @@ -10062,7 +10018,6 @@ private void EnsureAwaitablePlaceholdersInitialized() { SetNotNullResult(node); } - return null; } public override BoundNode? VisitAwaitableInfo(BoundAwaitableInfo node) diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index 72ea26ad18c28..f0c251aa2dcf0 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -696,36 +696,41 @@ public BoundImplicitIndexerValuePlaceholder Update(TypeSymbol type) internal sealed partial class BoundImplicitIndexerReceiverPlaceholder : BoundValuePlaceholderBase { - public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type, bool hasErrors) + public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, bool isEquivalentToThisReference, TypeSymbol type, bool hasErrors) : base(BoundKind.ImplicitIndexerReceiverPlaceholder, syntax, type, hasErrors) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ValEscape = valEscape; + this._IsEquivalentToThisReference = isEquivalentToThisReference; } - public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, TypeSymbol type) + public BoundImplicitIndexerReceiverPlaceholder(SyntaxNode syntax, uint valEscape, bool isEquivalentToThisReference, TypeSymbol type) : base(BoundKind.ImplicitIndexerReceiverPlaceholder, syntax, type) { RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ValEscape = valEscape; + this._IsEquivalentToThisReference = isEquivalentToThisReference; } public new TypeSymbol Type => base.Type!; public uint ValEscape { get; } + + private readonly bool _IsEquivalentToThisReference; + public override bool IsEquivalentToThisReference { get { return _IsEquivalentToThisReference; } } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitImplicitIndexerReceiverPlaceholder(this); - public BoundImplicitIndexerReceiverPlaceholder Update(uint valEscape, TypeSymbol type) + public BoundImplicitIndexerReceiverPlaceholder Update(uint valEscape, bool isEquivalentToThisReference, TypeSymbol type) { - if (valEscape != this.ValEscape || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (valEscape != this.ValEscape || isEquivalentToThisReference != this.IsEquivalentToThisReference || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundImplicitIndexerReceiverPlaceholder(this.Syntax, valEscape, type, this.HasErrors); + var result = new BoundImplicitIndexerReceiverPlaceholder(this.Syntax, valEscape, isEquivalentToThisReference, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7330,10 +7335,11 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol in internal sealed partial class BoundImplicitIndexerAccess : BoundExpression { - public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) - : base(BoundKind.ImplicitIndexerAccess, syntax, type, hasErrors || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerOrSliceAccess.HasErrors() || argumentPlaceholders.HasErrors()) + public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression receiver, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type, bool hasErrors = false) + : base(BoundKind.ImplicitIndexerAccess, syntax, type, hasErrors || receiver.HasErrors() || argument.HasErrors() || lengthOrCountAccess.HasErrors() || receiverPlaceholder.HasErrors() || indexerOrSliceAccess.HasErrors() || argumentPlaceholders.HasErrors()) { + RoslynDebug.Assert(receiver is object, "Field 'receiver' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(argument is object, "Field 'argument' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(lengthOrCountAccess is object, "Field 'lengthOrCountAccess' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(receiverPlaceholder is object, "Field 'receiverPlaceholder' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); @@ -7341,6 +7347,7 @@ public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression argument, B RoslynDebug.Assert(!argumentPlaceholders.IsDefault, "Field 'argumentPlaceholders' cannot be null (use Null=\"allow\" in BoundNodes.xml to remove this check)"); RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); + this.Receiver = receiver; this.Argument = argument; this.LengthOrCountAccess = lengthOrCountAccess; this.ReceiverPlaceholder = receiverPlaceholder; @@ -7355,6 +7362,8 @@ public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression argument, B public new TypeSymbol Type => base.Type!; + public BoundExpression Receiver { get; } + public BoundExpression Argument { get; } public BoundExpression LengthOrCountAccess { get; } @@ -7367,11 +7376,11 @@ public BoundImplicitIndexerAccess(SyntaxNode syntax, BoundExpression argument, B [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitImplicitIndexerAccess(this); - public BoundImplicitIndexerAccess Update(BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) + public BoundImplicitIndexerAccess Update(BoundExpression receiver, BoundExpression argument, BoundExpression lengthOrCountAccess, BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder, BoundExpression indexerOrSliceAccess, ImmutableArray argumentPlaceholders, TypeSymbol type) { - if (argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerOrSliceAccess != this.IndexerOrSliceAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiver != this.Receiver || argument != this.Argument || lengthOrCountAccess != this.LengthOrCountAccess || receiverPlaceholder != this.ReceiverPlaceholder || indexerOrSliceAccess != this.IndexerOrSliceAccess || argumentPlaceholders != this.ArgumentPlaceholders || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundImplicitIndexerAccess(this.Syntax, argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type, this.HasErrors); + var result = new BoundImplicitIndexerAccess(this.Syntax, receiver, argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -10409,8 +10418,8 @@ internal abstract partial class BoundTreeWalker : BoundTreeVisitor } public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { + this.Visit(node.Receiver); this.Visit(node.Argument); - this.Visit(node.IndexerOrSliceAccess); return null; } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) @@ -10641,7 +10650,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { TypeSymbol? type = this.VisitType(node.Type); - return node.Update(node.ValEscape, type); + return node.Update(node.ValEscape, node.IsEquivalentToThisReference, type); } public override BoundNode? VisitListPatternReceiverPlaceholder(BoundListPatternReceiverPlaceholder node) { @@ -11663,13 +11672,14 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor } public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { + BoundExpression receiver = (BoundExpression)this.Visit(node.Receiver); BoundExpression argument = (BoundExpression)this.Visit(node.Argument); BoundExpression lengthOrCountAccess = node.LengthOrCountAccess; BoundImplicitIndexerReceiverPlaceholder receiverPlaceholder = node.ReceiverPlaceholder; - BoundExpression indexerOrSliceAccess = (BoundExpression)this.Visit(node.IndexerOrSliceAccess); + BoundExpression indexerOrSliceAccess = node.IndexerOrSliceAccess; ImmutableArray argumentPlaceholders = node.ArgumentPlaceholders; TypeSymbol? type = this.VisitType(node.Type); - return node.Update(argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type); + return node.Update(receiver, argument, lengthOrCountAccess, receiverPlaceholder, indexerOrSliceAccess, argumentPlaceholders, type); } public override BoundNode? VisitDynamicIndexerAccess(BoundDynamicIndexerAccess node) { @@ -12038,7 +12048,7 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("implicitIndexerReceiverPlaceholder", null, new TreeDumperNode[] { new TreeDumperNode("valEscape", node.ValEscape, null), + new TreeDumperNode("isEquivalentToThisReference", node.IsEquivalentToThisReference, null), new TreeDumperNode("type", node.Type, null), new TreeDumperNode("isSuppressed", node.IsSuppressed, null), new TreeDumperNode("hasErrors", node.HasErrors, null) @@ -16130,6 +16141,7 @@ private BoundTreeDumperNodeProducer() ); public override TreeDumperNode VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node, object? arg) => new TreeDumperNode("implicitIndexerAccess", null, new TreeDumperNode[] { + new TreeDumperNode("receiver", null, new TreeDumperNode[] { Visit(node.Receiver, null) }), new TreeDumperNode("argument", null, new TreeDumperNode[] { Visit(node.Argument, null) }), new TreeDumperNode("lengthOrCountAccess", null, new TreeDumperNode[] { Visit(node.LengthOrCountAccess, null) }), new TreeDumperNode("receiverPlaceholder", null, new TreeDumperNode[] { Visit(node.ReceiverPlaceholder, null) }), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index c6e9842e1c473..2b8cbd951de12 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -234,7 +234,7 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundImplicitIndexerAcces var locals = ArrayBuilder.GetInstance(2); var sideeffects = ArrayBuilder.GetInstance(2); - var receiver = VisitExpression(node.GetReceiver()); + var receiver = VisitExpression(node.Receiver); // Do not capture receiver if it is a local or parameter and we are evaluating a pattern // If length access is a local, then we are evaluating a pattern @@ -253,13 +253,15 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundImplicitIndexerAcces receiver = receiverLocal; } + AddPlaceholderReplacement(node.ReceiverPlaceholder, receiver); + BoundExpression makeOffsetInput = DetermineMakePatternIndexOffsetExpressionStrategy(node.Argument, out PatternIndexOffsetLoweringStrategy strategy); BoundExpression integerArgument; switch (strategy) { case PatternIndexOffsetLoweringStrategy.SubtractFromLength: - BoundExpression lengthAccess = RewriteLengthAccess(node, receiver); + BoundExpression lengthAccess = VisitExpression(node.LengthOrCountAccess); // ensure we evaluate the input before accessing length, unless it is an array length if (makeOffsetInput.ConstantValue is null && lengthAccess.Kind is not BoundKind.ArrayLength) @@ -277,7 +279,7 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundImplicitIndexerAcces break; case PatternIndexOffsetLoweringStrategy.UseGetOffsetAPI: - integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, RewriteLengthAccess(node, receiver), strategy); + integerArgument = MakePatternIndexOffsetExpression(makeOffsetInput, VisitExpression(node.LengthOrCountAccess), strategy); break; default: @@ -318,15 +320,16 @@ private BoundExpression VisitIndexPatternIndexerAccess(BoundImplicitIndexerAcces } else { - rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess.WithReceiver(receiver), isLeftOfAssignment); + rewrittenIndexerAccess = VisitIndexerAccess(indexerAccess, isLeftOfAssignment); } } else { - rewrittenIndexerAccess = (BoundExpression)VisitArrayAccess(((BoundArrayAccess)node.IndexerOrSliceAccess).WithReceiver(receiver)); + rewrittenIndexerAccess = (BoundExpression)VisitArrayAccess(((BoundArrayAccess)node.IndexerOrSliceAccess)); } RemovePlaceholderReplacement(argumentPlaceholder); + RemovePlaceholderReplacement(node.ReceiverPlaceholder); return F.Sequence( locals.ToImmutableAndFree(), @@ -460,7 +463,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces var F = _factory; - var receiver = VisitExpression(node.GetReceiver()); + var receiver = VisitExpression(node.Receiver); var rangeArg = node.Argument; var localsBuilder = ArrayBuilder.GetInstance(); @@ -478,6 +481,8 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces receiver = receiverLocal; } + AddPlaceholderReplacement(node.ReceiverPlaceholder, receiver); + BoundExpression startExpr; BoundExpression rangeSizeExpr; if (rangeArg is BoundRangeExpression rangeExpr) @@ -602,7 +607,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces if ((rewriteFlags & useLength) != 0) { - lengthAccess = RewriteLengthAccess(node, receiver); + lengthAccess = VisitExpression(node.LengthOrCountAccess); // If length access is a local, then we are evaluating a pattern and don't need to capture the value. if ((rewriteFlags & captureLength) != 0 && lengthAccess.Kind is not BoundKind.Local) @@ -645,7 +650,7 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces localsBuilder.Add(rangeLocal.LocalSymbol); sideEffectsBuilder.Add(rangeStore); - var lengthAccess = RewriteLengthAccess(node, receiver); + var lengthAccess = VisitExpression(node.LengthOrCountAccess); var lengthLocal = F.StoreToTemp(lengthAccess, out var lengthStore); localsBuilder.Add(lengthLocal.LocalSymbol); @@ -681,28 +686,16 @@ private BoundExpression VisitRangePatternIndexerAccess(BoundImplicitIndexerAcces AddPlaceholderReplacement(node.ArgumentPlaceholders[1], rangeSizeExpr); var sliceCall = (BoundCall)node.IndexerOrSliceAccess; - var rewrittenIndexerAccess = VisitExpression(sliceCall.WithReceiver(receiver)); + var rewrittenIndexerAccess = VisitExpression(sliceCall); RemovePlaceholderReplacement(node.ArgumentPlaceholders[0]); RemovePlaceholderReplacement(node.ArgumentPlaceholders[1]); + RemovePlaceholderReplacement(node.ReceiverPlaceholder); return F.Sequence( localsBuilder.ToImmutableAndFree(), sideEffectsBuilder.ToImmutableAndFree(), rewrittenIndexerAccess); } - - private BoundExpression RewriteLengthAccess(BoundImplicitIndexerAccess node, BoundExpression receiver) - { - var receiverPlaceholder = node.ReceiverPlaceholder; - AddPlaceholderReplacement(receiverPlaceholder, receiver); - - Debug.Assert(node.LengthOrCountAccess is not null); - var lengthAccess = VisitExpression(node.LengthOrCountAccess); - - RemovePlaceholderReplacement(receiverPlaceholder); - - return lengthAccess; - } } } From 455e6afb96cf5c31117c39016137c3cbef685dc6 Mon Sep 17 00:00:00 2001 From: AlekseyTs Date: Mon, 29 Nov 2021 09:33:44 -0800 Subject: [PATCH 2/2] Rename --- .../CSharp/Portable/FlowAnalysis/NullableWalker.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index a7b61b3ff3d17..dcd5c3aee1f7f 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -8727,7 +8727,7 @@ private TypeWithAnnotations GetDeclaredParameterResult(ParameterSymbol parameter public override BoundNode? VisitImplicitIndexerReceiverPlaceholder(BoundImplicitIndexerReceiverPlaceholder node) { - VisitplaceholderWithReplacement(node); + VisitPlaceholderWithReplacement(node); return null; } @@ -10003,11 +10003,11 @@ private void EnsurePlaceholdersToResultMap() public override BoundNode? VisitAwaitableValuePlaceholder(BoundAwaitableValuePlaceholder node) { - VisitplaceholderWithReplacement(node); + VisitPlaceholderWithReplacement(node); return null; } - private void VisitplaceholderWithReplacement(BoundValuePlaceholderBase node) + private void VisitPlaceholderWithReplacement(BoundValuePlaceholderBase node) { if (_resultForPlaceholdersOpt != null && _resultForPlaceholdersOpt.TryGetValue(node, out var value)) {