diff --git a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs index 5ee582057a54b..d86454a74ee02 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs @@ -356,6 +356,7 @@ private BoundIndexerAccess BindIndexerDefaultArguments(BoundIndexerAccess indexe indexerAccess = indexerAccess.Update( indexerAccess.ReceiverOpt, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerAccess.Indexer, argumentsBuilder.ToImmutableAndFree(), indexerAccess.ArgumentNamesOpt, @@ -1779,6 +1780,7 @@ internal uint GetInterpolatedStringHandlerConversionEscapeScope( private uint GetInvocationEscapeScope( Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -1793,7 +1795,7 @@ bool isRefEscape if (UseUpdatedEscapeRulesForInvocation(symbol)) { - return GetInvocationEscapeWithUpdatedRules(symbol, receiver, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, scopeOfTheContainingExpression, isRefEscape); + return GetInvocationEscapeWithUpdatedRules(symbol, receiver, receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, scopeOfTheContainingExpression, isRefEscape); } // SPEC: (also applies to the CheckInvocationEscape counterpart) @@ -1821,6 +1823,7 @@ bool isRefEscape GetInvocationArgumentsForEscape( symbol, receiver: null, // receiver handled explicitly below + receiverIsSubjectToCloning: ThreeState.Unknown, parameters, argsOpt, argRefKindsOpt, @@ -1872,6 +1875,7 @@ bool isRefEscape private uint GetInvocationEscapeWithUpdatedRules( Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -1886,6 +1890,7 @@ private uint GetInvocationEscapeWithUpdatedRules( GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( symbol, receiver, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, @@ -1946,6 +1951,7 @@ private bool CheckInvocationEscape( SyntaxNode syntax, Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -1963,7 +1969,7 @@ bool isRefEscape if (UseUpdatedEscapeRulesForInvocation(symbol)) { - return CheckInvocationEscapeWithUpdatedRules(syntax, symbol, receiver, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, checkingReceiver, escapeFrom, escapeTo, diagnostics, isRefEscape); + return CheckInvocationEscapeWithUpdatedRules(syntax, symbol, receiver, receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, checkingReceiver, escapeFrom, escapeTo, diagnostics, isRefEscape); } // SPEC: @@ -1982,6 +1988,7 @@ bool isRefEscape GetInvocationArgumentsForEscape( symbol, receiver: null, // receiver handled explicitly below + receiverIsSubjectToCloning: ThreeState.Unknown, parameters, argsOpt, argRefKindsOpt, @@ -2036,6 +2043,7 @@ private bool CheckInvocationEscapeWithUpdatedRules( SyntaxNode syntax, Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2052,6 +2060,7 @@ private bool CheckInvocationEscapeWithUpdatedRules( GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( symbol, receiver, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, @@ -2095,7 +2104,7 @@ private bool CheckInvocationEscapeWithUpdatedRules( /// /// Returns the set of arguments to be considered for escape analysis of a method invocation. - /// Each argument is returned with the correponding parameter and ref kind. Arguments are not + /// Each argument is returned with the corresponding parameter and ref kind. Arguments are not /// filtered - all arguments are included exactly once in the array, and the caller is responsible for /// determining which arguments affect escape analysis. This method is used for method invocation /// analysis, regardless of whether UseUpdatedEscapeRules is set. @@ -2103,6 +2112,7 @@ private bool CheckInvocationEscapeWithUpdatedRules( private void GetInvocationArgumentsForEscape( Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2121,22 +2131,15 @@ private void GetInvocationArgumentsForEscape( }; Debug.Assert(receiver.Type is { }); - if (receiver is not BoundValuePlaceholderBase && method is not null && receiver.Type?.IsValueType == true) + Debug.Assert(receiverIsSubjectToCloning != ThreeState.Unknown); + if (receiverIsSubjectToCloning == ThreeState.True) { - var receiverAddressKind = method.IsEffectivelyReadOnly ? Binder.AddressKind.ReadOnly : Binder.AddressKind.Writeable; - - if (!Binder.HasHome(receiver, - receiverAddressKind, - _symbol, - _compilation.IsPeVerifyCompatEnabled, - stackLocalsOpt: null)) - { + Debug.Assert(receiver is not BoundValuePlaceholderBase && method is not null && receiver.Type?.IsValueType == true); #if DEBUG - AssertVisited(receiver); + AssertVisited(receiver); #endif - // Equivalent to a non-ref local with the underlying receiver as an initializer provided at declaration - receiver = new BoundCapturedReceiverPlaceholder(receiver.Syntax, receiver, _localScopeDepth, receiver.Type).MakeCompilerGenerated(); - } + // Equivalent to a non-ref local with the underlying receiver as an initializer provided at declaration + receiver = new BoundCapturedReceiverPlaceholder(receiver.Syntax, receiver, _localScopeDepth, receiver.Type).MakeCompilerGenerated(); } var tuple = getReceiver(method, receiver); @@ -2260,6 +2263,7 @@ static void getArgList( private void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2294,6 +2298,7 @@ private void GetFilteredInvocationArgumentsForEscapeWithUpdatedRules( GetEscapeValuesForUpdatedRules( symbol, receiver, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, @@ -2336,6 +2341,7 @@ static bool hasRefLikeReturn(Symbol symbol) private void GetEscapeValuesForUpdatedRules( Symbol symbol, BoundExpression? receiver, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2354,6 +2360,7 @@ private void GetEscapeValuesForUpdatedRules( GetInvocationArgumentsForEscape( symbol, receiver, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, @@ -2466,6 +2473,7 @@ private bool CheckInvocationArgMixing( SyntaxNode syntax, Symbol symbol, BoundExpression? receiverOpt, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2475,7 +2483,7 @@ private bool CheckInvocationArgMixing( { if (UseUpdatedEscapeRulesForInvocation(symbol)) { - return CheckInvocationArgMixingWithUpdatedRules(syntax, symbol, receiverOpt, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, scopeOfTheContainingExpression, diagnostics); + return CheckInvocationArgMixingWithUpdatedRules(syntax, symbol, receiverOpt, receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, argsToParamsOpt, scopeOfTheContainingExpression, diagnostics); } // SPEC: @@ -2503,6 +2511,7 @@ private bool CheckInvocationArgMixing( GetInvocationArgumentsForEscape( symbol, receiverOpt, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt: default, @@ -2568,6 +2577,7 @@ private bool CheckInvocationArgMixingWithUpdatedRules( SyntaxNode syntax, Symbol symbol, BoundExpression? receiverOpt, + ThreeState receiverIsSubjectToCloning, ImmutableArray parameters, ImmutableArray argsOpt, ImmutableArray argRefKindsOpt, @@ -2580,6 +2590,7 @@ private bool CheckInvocationArgMixingWithUpdatedRules( GetEscapeValuesForUpdatedRules( symbol, receiverOpt, + receiverIsSubjectToCloning, parameters, argsOpt, argRefKindsOpt, @@ -3072,6 +3083,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( call.Method, call.ReceiverOpt, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3093,6 +3105,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( methodSymbol, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, methodSymbol.Parameters, ptrInvocation.Arguments, ptrInvocation.ArgumentRefKindsOpt, @@ -3109,6 +3122,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( indexerSymbol, indexerAccess.ReceiverOpt, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3130,6 +3144,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( indexerSymbol, implicitIndexerAccess.Receiver, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3151,6 +3166,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( call.Method, implicitIndexerAccess.Receiver, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3182,6 +3198,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, arguments, refKinds, @@ -3197,6 +3214,7 @@ internal uint GetRefEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( propertyAccess.PropertySymbol, propertyAccess.ReceiverOpt, + propertyAccess.InitialBindingReceiverIsSubjectToCloning, default, default, default, @@ -3349,6 +3367,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF call.Syntax, methodSymbol, call.ReceiverOpt, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3374,6 +3393,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF indexerAccess.Syntax, indexerSymbol, indexerAccess.ReceiverOpt, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3404,6 +3424,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF indexerAccess.Syntax, indexerSymbol, implicitIndexerAccess.Receiver, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3429,6 +3450,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF call.Syntax, methodSymbol, implicitIndexerAccess.Receiver, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3464,6 +3486,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF elementAccess.Syntax, equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, argsOpt: arguments, argRefKindsOpt: refKinds, @@ -3488,6 +3511,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF functionPointerInvocation.Syntax, signature, functionPointerInvocation.InvokedExpression, + receiverIsSubjectToCloning: ThreeState.False, signature.Parameters, functionPointerInvocation.Arguments, functionPointerInvocation.ArgumentRefKindsOpt, @@ -3512,6 +3536,7 @@ internal bool CheckRefEscape(SyntaxNode node, BoundExpression expr, uint escapeF propertyAccess.Syntax, propertySymbol, propertyAccess.ReceiverOpt, + propertyAccess.InitialBindingReceiverIsSubjectToCloning, default, default, default, @@ -3702,6 +3727,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( call.Method, call.ReceiverOpt, + call.InitialBindingReceiverIsSubjectToCloning, call.Method.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3717,6 +3743,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( ptrSymbol, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, ptrSymbol.Parameters, ptrInvocation.Arguments, ptrInvocation.ArgumentRefKindsOpt, @@ -3732,6 +3759,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( indexerSymbol, indexerAccess.ReceiverOpt, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3753,6 +3781,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( indexerSymbol, implicitIndexerAccess.Receiver, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -3768,6 +3797,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( call.Method, implicitIndexerAccess.Receiver, + call.InitialBindingReceiverIsSubjectToCloning, call.Method.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -3790,6 +3820,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, arguments, refKinds, @@ -3805,6 +3836,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( propertyAccess.PropertySymbol, propertyAccess.ReceiverOpt, + propertyAccess.InitialBindingReceiverIsSubjectToCloning, default, default, default, @@ -3818,7 +3850,8 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres var escape = GetInvocationEscapeScope( constructorSymbol, - null, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, constructorSymbol.Parameters, objectCreation.Arguments, objectCreation.ArgumentRefKindsOpt, @@ -3861,6 +3894,7 @@ internal uint GetValEscape(BoundExpression expr, uint scopeOfTheContainingExpres return GetInvocationEscapeScope( equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, arguments, refKinds, @@ -4172,6 +4206,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF call.Syntax, methodSymbol, call.ReceiverOpt, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -4191,6 +4226,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF ptrInvocation.Syntax, ptrSymbol, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, ptrSymbol.Parameters, ptrInvocation.Arguments, ptrInvocation.ArgumentRefKindsOpt, @@ -4210,6 +4246,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF indexerAccess.Syntax, indexerSymbol, indexerAccess.ReceiverOpt, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -4235,6 +4272,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF indexerAccess.Syntax, indexerSymbol, implicitIndexerAccess.Receiver, + indexerAccess.InitialBindingReceiverIsSubjectToCloning, indexerSymbol.Parameters, indexerAccess.Arguments, indexerAccess.ArgumentRefKindsOpt, @@ -4256,6 +4294,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF call.Syntax, methodSymbol, implicitIndexerAccess.Receiver, + call.InitialBindingReceiverIsSubjectToCloning, methodSymbol.Parameters, call.Arguments, call.ArgumentRefKindsOpt, @@ -4282,6 +4321,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF elementAccess.Syntax, equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, argsOpt: arguments, argRefKindsOpt: refKinds, @@ -4301,6 +4341,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF propertyAccess.Syntax, propertyAccess.PropertySymbol, propertyAccess.ReceiverOpt, + propertyAccess.InitialBindingReceiverIsSubjectToCloning, default, default, default, @@ -4319,7 +4360,8 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF var escape = CheckInvocationEscape( objectCreation.Syntax, constructorSymbol, - null, + receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, constructorSymbol.Parameters, objectCreation.Arguments, objectCreation.ArgumentRefKindsOpt, @@ -4384,6 +4426,7 @@ internal bool CheckValEscape(SyntaxNode node, BoundExpression expr, uint escapeF conversion.Syntax, equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, argsOpt: arguments, argRefKindsOpt: refKinds, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs index 85c3ea7ab1d23..62ccf762a4f62 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs @@ -566,7 +566,7 @@ private BoundAssignmentOperator BindNamedAttributeArgument(AttributeArgumentSynt var propertySymbol = namedArgumentNameSymbol as PropertySymbol; if (propertySymbol is object) { - lvalue = new BoundPropertyAccess(nameSyntax, null, propertySymbol, resultKind, namedArgumentType); + lvalue = new BoundPropertyAccess(nameSyntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, propertySymbol, resultKind, namedArgumentType); } else { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs index 3f939a1ff44b6..0ad59beb6a851 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs @@ -4547,6 +4547,7 @@ private BoundExpression BindConstructorInitializerCore( return new BoundCall( nonNullSyntax, receiver, + initialBindingReceiverIsSubjectToCloning: ReceiverIsSubjectToCloning(receiver, resultMember), resultMember, arguments, analyzedArguments.GetNames(), @@ -7976,7 +7977,7 @@ private BoundExpression BindPropertyAccess( WarnOnAccessOfOffDefault(node, receiver, diagnostics); } - return new BoundPropertyAccess(node, receiver, propertySymbol, lookupResult, propertySymbol.Type, hasErrors: (hasErrors || hasError)); + return new BoundPropertyAccess(node, receiver, initialBindingReceiverIsSubjectToCloning: ReceiverIsSubjectToCloning(receiver, propertySymbol), propertySymbol, lookupResult, propertySymbol.Type, hasErrors: (hasErrors || hasError)); } #nullable disable @@ -9125,6 +9126,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess( propertyAccess = new BoundIndexerAccess( syntax, receiver, + initialBindingReceiverIsSubjectToCloning: ReceiverIsSubjectToCloning(receiver, property), property, arguments, argumentNames, diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs index 89812ebcc2f76..77ed60f2a7abb 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs @@ -1189,13 +1189,30 @@ private BoundCall BindInvocationExpressionContinued( } } - return new BoundCall(node, receiver, method, args, argNames, argRefKinds, isDelegateCall: isDelegateCall, + return new BoundCall(node, receiver, initialBindingReceiverIsSubjectToCloning: ReceiverIsSubjectToCloning(receiver, method), method, args, argNames, argRefKinds, isDelegateCall: isDelegateCall, expanded: expanded, invokedAsExtensionMethod: invokedAsExtensionMethod, argsToParamsOpt: argsToParams, defaultArguments, resultKind: LookupResultKind.Viable, type: returnType, hasErrors: gotError); } #nullable enable + internal ThreeState ReceiverIsSubjectToCloning(BoundExpression? receiver, PropertySymbol property) + => ReceiverIsSubjectToCloning(receiver, property.GetMethod ?? property.SetMethod); + + internal ThreeState ReceiverIsSubjectToCloning(BoundExpression? receiver, MethodSymbol method) + { + if (receiver is BoundValuePlaceholderBase || receiver?.Type?.IsValueType != true) + { + return ThreeState.False; + } + + var valueKind = method.IsEffectivelyReadOnly + ? BindValueKind.RefersToLocation + : BindValueKind.RefersToLocation | BindValueKind.Assignable; + var result = !CheckValueKind(receiver.Syntax, receiver, valueKind, checkingReceiver: true, BindingDiagnosticBag.Discarded); + return result.ToThreeState(); + } + private static SourceLocation GetCallerLocation(SyntaxNode syntax) { var token = syntax switch diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs index 56977f6a79f24..554105cdcfcf5 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs @@ -363,7 +363,7 @@ private bool BindLengthAndIndexerForListPattern(SyntaxNode node, TypeSymbol inpu hasErrors |= !TryGetSpecialTypeMember(Compilation, SpecialMember.System_Array__Length, node, diagnostics, out PropertySymbol lengthProperty); if (lengthProperty is not null) { - lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, lengthProperty, LookupResultKind.Viable, lengthProperty.Type) { WasCompilerGenerated = true }; + lengthAccess = new BoundPropertyAccess(node, receiverPlaceholder, initialBindingReceiverIsSubjectToCloning: ThreeState.False, lengthProperty, LookupResultKind.Viable, lengthProperty.Type) { WasCompilerGenerated = true }; } else { diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs index 1b4e98e15e444..91200a736e053 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Query.cs @@ -325,7 +325,7 @@ private static BoundCall ReverseLastTwoParameterOrder(BoundCall result) (defaultArguments[n - 1], defaultArguments[n - 2]) = (defaultArguments[n - 2], defaultArguments[n - 1]); return result.Update( - result.ReceiverOpt, result.Method, arguments.ToImmutableAndFree(), argumentNamesOpt: default, + result.ReceiverOpt, result.InitialBindingReceiverIsSubjectToCloning, result.Method, arguments.ToImmutableAndFree(), argumentNamesOpt: default, argumentRefKindsOpt: default, result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, argsToParams.ToImmutableAndFree(), defaultArguments, result.ResultKind, result.OriginalMethodsOpt, result.Type); } @@ -469,7 +469,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); - invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); + invocation = invocation.Update(invocation.ReceiverOpt, invocation.InitialBindingReceiverIsSubjectToCloning, invocation.Method, arguments); } state.Clear(); // this completes the whole query @@ -538,7 +538,7 @@ private void ReduceJoin(JoinClauseSyntax join, QueryTranslationState state, Bind var arguments = invocation.Arguments; arguments = arguments.SetItem(arguments.Length - 1, MakeQueryClause(join.Into, arguments[arguments.Length - 1], g)); - invocation = invocation.Update(invocation.ReceiverOpt, invocation.Method, arguments); + invocation = invocation.Update(invocation.ReceiverOpt, invocation.InitialBindingReceiverIsSubjectToCloning, invocation.Method, arguments); } state.fromExpression = MakeQueryClause(join, invocation, x2, invocation, castInvocation); @@ -628,6 +628,7 @@ private void ReduceFrom(FromClauseSyntax from, QueryTranslationState state, Bind var arguments = invocation.Arguments; invocation = invocation.Update( invocation.ReceiverOpt, + invocation.InitialBindingReceiverIsSubjectToCloning, invocation.Method, arguments.SetItem(arguments.Length - 2, MakeQueryClause(from, arguments[arguments.Length - 2], x2, invocation, castInvocation))); diff --git a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs index 7614c81b52a0e..e61f2bf342696 100644 --- a/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs +++ b/src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs @@ -3875,6 +3875,7 @@ private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode cont return new BoundCall( syntax: syntax, receiverOpt: receiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, method: baseConstructor, arguments: ImmutableArray.Empty, argumentNamesOpt: ImmutableArray.Empty, @@ -3921,6 +3922,7 @@ private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode cont return new BoundCall( syntax: syntax, receiverOpt: receiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, method: baseConstructor, arguments: ImmutableArray.Create(argument), argumentNamesOpt: default, diff --git a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs index 536cd9a45b2f4..e2d5fb982c5ff 100644 --- a/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs +++ b/src/Compilers/CSharp/Portable/Binder/ForEachLoopBinder.cs @@ -711,6 +711,7 @@ private BoundExpression UnwrapCollectionExpressionIfNullable(BoundExpression col return BoundCall.Synthesized( syntax: exprSyntax, receiverOpt: collectionExpr, + initialBindingReceiverIsSubjectToCloning: ReceiverIsSubjectToCloning(collectionExpr, nullableValueGetter), method: nullableValueGetter); } else diff --git a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs index 534b328284053..6d6e983a1ef12 100644 --- a/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs +++ b/src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs @@ -634,6 +634,7 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO protected override void VisitArguments(BoundCall node) { + Debug.Assert(node.InitialBindingReceiverIsSubjectToCloning != ThreeState.Unknown); VisitArgumentsAndGetArgumentPlaceholders(node.ReceiverOpt, node.Arguments); if (!node.HasErrors) @@ -643,6 +644,7 @@ protected override void VisitArguments(BoundCall node) node.Syntax, method, node.ReceiverOpt, + node.InitialBindingReceiverIsSubjectToCloning, method.Parameters, node.Arguments, node.ArgumentRefKindsOpt, @@ -748,6 +750,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase node.Syntax, constructor, receiverOpt: null, + receiverIsSubjectToCloning: ThreeState.Unknown, constructor.Parameters, node.Arguments, node.ArgumentRefKindsOpt, @@ -758,8 +761,15 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase } } + public override BoundNode? VisitPropertyAccess(BoundPropertyAccess node) + { + Debug.Assert(node.InitialBindingReceiverIsSubjectToCloning != ThreeState.Unknown); + return base.VisitPropertyAccess(node); + } + public override BoundNode? VisitIndexerAccess(BoundIndexerAccess node) { + Debug.Assert(node.InitialBindingReceiverIsSubjectToCloning != ThreeState.Unknown); Visit(node.ReceiverOpt); VisitArgumentsAndGetArgumentPlaceholders(node.ReceiverOpt, node.Arguments); @@ -770,6 +780,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase node.Syntax, indexer, node.ReceiverOpt, + node.InitialBindingReceiverIsSubjectToCloning, indexer.Parameters, node.Arguments, node.ArgumentRefKindsOpt, @@ -792,6 +803,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase node.Syntax, method, receiverOpt: null, + receiverIsSubjectToCloning: ThreeState.Unknown, method.Parameters, node.Arguments, node.ArgumentRefKindsOpt, @@ -892,6 +904,7 @@ private void VisitDeconstructionArguments(ArrayBuilder v syntax, deconstructMethod, invocation.ReceiverOpt, + invocation.InitialBindingReceiverIsSubjectToCloning, parameters, invocation.Arguments, invocation.ArgumentRefKindsOpt, @@ -982,6 +995,7 @@ private static ImmutableArray GetDeconstructionRightParts(Bound collectionEscape = GetInvocationEscapeScope( equivalentSignatureMethod, receiver: null, + receiverIsSubjectToCloning: ThreeState.Unknown, equivalentSignatureMethod.Parameters, arguments, refKinds, diff --git a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml index 880528b447fe1..02d74f70ecc62 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml +++ b/src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml @@ -24,6 +24,7 @@ + @@ -1786,6 +1787,8 @@ + + @@ -2142,6 +2145,8 @@ + + @@ -2161,6 +2166,8 @@ + + diff --git a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs index c76b619a2dfa7..d2081e0c577a5 100644 --- a/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs +++ b/src/Compilers/CSharp/Portable/BoundTree/Constructors.cs @@ -85,6 +85,7 @@ internal partial class BoundCall public BoundCall( SyntaxNode syntax, BoundExpression? receiverOpt, + ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, @@ -97,11 +98,12 @@ public BoundCall( LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) : - this(syntax, receiverOpt, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, originalMethodsOpt: default, type, hasErrors) + this(syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, originalMethodsOpt: default, type: type, hasErrors: hasErrors) { } public BoundCall Update(BoundExpression? receiverOpt, + ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, @@ -113,7 +115,7 @@ public BoundCall Update(BoundExpression? receiverOpt, BitVector defaultArguments, LookupResultKind resultKind, TypeSymbol type) - => Update(receiverOpt, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, this.OriginalMethodsOpt, type); + => Update(receiverOpt, initialBindingReceiverIsSubjectToCloning, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, this.OriginalMethodsOpt, type); public static BoundCall ErrorCall( SyntaxNode node, @@ -136,6 +138,7 @@ public static BoundCall ErrorCall( return new BoundCall( syntax: node, receiverOpt: binder.BindToTypeForErrorRecovery(receiverOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.False, method: method, arguments: arguments.SelectAsArray((e, binder) => binder.BindToTypeForErrorRecovery(e), binder), argumentNamesOpt: namedArguments, @@ -153,30 +156,30 @@ public static BoundCall ErrorCall( public BoundCall Update(ImmutableArray arguments) { - return this.Update(ReceiverOpt, Method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type); + return this.Update(ReceiverOpt, InitialBindingReceiverIsSubjectToCloning, Method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type); } - public BoundCall Update(BoundExpression? receiverOpt, MethodSymbol method, ImmutableArray arguments) + public BoundCall Update(BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments) { - return this.Update(receiverOpt, method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type); + return this.Update(receiverOpt, initialBindingReceiverIsSubjectToCloning, method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type); } - public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method) + public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method) { - return Synthesized(syntax, receiverOpt, method, ImmutableArray.Empty); + return Synthesized(syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning: initialBindingReceiverIsSubjectToCloning, method, ImmutableArray.Empty); } - public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, BoundExpression arg0) + public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, BoundExpression arg0) { - return Synthesized(syntax, receiverOpt, method, ImmutableArray.Create(arg0)); + return Synthesized(syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning: initialBindingReceiverIsSubjectToCloning, method, ImmutableArray.Create(arg0)); } - public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, BoundExpression arg0, BoundExpression arg1) + public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, BoundExpression arg0, BoundExpression arg1) { - return Synthesized(syntax, receiverOpt, method, ImmutableArray.Create(arg0, arg1)); + return Synthesized(syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning: initialBindingReceiverIsSubjectToCloning, method, ImmutableArray.Create(arg0, arg1)); } - public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentRefKindsOpt = default) + public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentRefKindsOpt = default) { argumentRefKindsOpt = argumentRefKindsOpt.IsDefault ? getArgumentRefKinds(method) : argumentRefKindsOpt; @@ -206,8 +209,9 @@ public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiver return new BoundCall(syntax, receiverOpt, - method, - arguments, + initialBindingReceiverIsSubjectToCloning: initialBindingReceiverIsSubjectToCloning, + method: method, + arguments: arguments, argumentNamesOpt: default(ImmutableArray), argumentRefKindsOpt: argumentRefKindsOpt, isDelegateCall: false, @@ -269,6 +273,7 @@ public static BoundIndexerAccess ErrorAccess( return new BoundIndexerAccess( node, receiverOpt, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, indexer, arguments, namedArguments, @@ -283,6 +288,7 @@ public static BoundIndexerAccess ErrorAccess( public BoundIndexerAccess( SyntaxNode syntax, BoundExpression? receiverOpt, + ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, @@ -292,10 +298,11 @@ public BoundIndexerAccess( BitVector defaultArguments, TypeSymbol type, bool hasErrors = false) : - this(syntax, receiverOpt, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, originalIndexersOpt: default, type, hasErrors) + this(syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, originalIndexersOpt: default, type, hasErrors) { } public BoundIndexerAccess Update(BoundExpression? receiverOpt, + ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, @@ -304,7 +311,7 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt, ImmutableArray argsToParamsOpt, BitVector defaultArguments, TypeSymbol type) - => Update(receiverOpt, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, this.OriginalIndexersOpt, type); + => Update(receiverOpt, initialBindingReceiverIsSubjectToCloning, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, this.OriginalIndexersOpt, type); } internal sealed partial class BoundConversion diff --git a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs index 9d7dfc4b3d99b..0c886bd35f0d2 100644 --- a/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs +++ b/src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs @@ -1206,7 +1206,7 @@ BoundExpression visitReceiver(BoundCall node) BoundCall visitArgumentsAndUpdateCall(BoundCall node, BoundExpression receiver) { var rewrittenArguments = VisitArguments(node.Arguments, node.Method.Parameters, node.ArgumentRefKindsOpt); - return node.Update(receiver, node.Method, rewrittenArguments); + return node.Update(receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, node.Method, rewrittenArguments); } } @@ -2294,7 +2294,7 @@ BoundExpression visitArgumentsAndUpdateCall(BoundCall node, BoundExpression? rec { ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); } } #nullable disable diff --git a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs index ab95aec4a2fc1..e30b7da894f22 100644 --- a/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs +++ b/src/Compilers/CSharp/Portable/Compiler/MethodBodySynthesizer.cs @@ -43,6 +43,7 @@ internal static ImmutableArray ConstructScriptConstructorBody( new BoundExpressionStatement(syntax, new BoundCall(syntax, receiverOpt: receiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: objectType.InstanceConstructors[0], arguments: ImmutableArray.Empty, argumentNamesOpt: ImmutableArray.Empty, @@ -285,6 +286,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent BoundCall getOrCreateCall = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: getOrCreateMethod, arg0: fieldAccess); @@ -297,6 +299,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_WinRT(SourceEvent BoundCall processHandlerCall = BoundCall.Synthesized( syntax, receiverOpt: getOrCreateCall, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: processHandlerMethod, arg0: parameterAccess); @@ -394,6 +397,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEve delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: updateMethod, arguments: ImmutableArray.Create(boundBackingField, boundParameter)), conversion: Conversion.ExplicitReference, @@ -458,6 +462,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEve delegateUpdate = BoundConversion.SynthesizedNonUserDefined(syntax, operand: BoundCall.Synthesized(syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: updateMethod, arguments: ImmutableArray.Create(boundTmps[1], boundParameter)), conversion: Conversion.ExplicitReference, @@ -475,6 +480,7 @@ internal static BoundBlock ConstructFieldLikeEventAccessorBody_Regular(SourceEve // Interlocked.CompareExchange(ref _event, tmp2, tmp1) BoundExpression compareExchange = BoundCall.Synthesized(syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: compareExchangeMethod, arguments: ImmutableArray.Create(boundBackingField, boundTmps[2], boundTmps[1])); @@ -541,6 +547,7 @@ internal static BoundBlock ConstructDestructorBody(MethodSymbol method, BoundBlo syntax, method.ContainingType) { WasCompilerGenerated = true }, + initialBindingReceiverIsSubjectToCloning: ThreeState.False, baseTypeFinalize)) { WasCompilerGenerated = true }; diff --git a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs index cb21d97170d7e..2e74e27b2a659 100644 --- a/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs +++ b/src/Compilers/CSharp/Portable/FlowAnalysis/NullableWalker.cs @@ -9235,7 +9235,7 @@ private void VisitThisOrBaseReference(BoundExpression node) // when binding initializers, we treat assignments to auto-properties or field-like events as direct assignments to the underlying field. // in order to track member state based on these initializers, we need to see the assignment in terms of the associated member case BoundFieldAccess { ExpressionSymbol: FieldSymbol { AssociatedSymbol: PropertySymbol autoProperty } } fieldAccess: - left = new BoundPropertyAccess(fieldAccess.Syntax, fieldAccess.ReceiverOpt, autoProperty, LookupResultKind.Viable, autoProperty.Type, fieldAccess.HasErrors); + left = new BoundPropertyAccess(fieldAccess.Syntax, fieldAccess.ReceiverOpt, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, autoProperty, LookupResultKind.Viable, autoProperty.Type, fieldAccess.HasErrors); break; case BoundFieldAccess { ExpressionSymbol: FieldSymbol { AssociatedSymbol: EventSymbol @event } } fieldAccess: left = new BoundEventAccess(fieldAccess.Syntax, fieldAccess.ReceiverOpt, @event, isUsableAsField: true, LookupResultKind.Viable, @event.Type, fieldAccess.HasErrors); diff --git a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs index a80983f7ab02c..7e1b6bb1e4fd1 100644 --- a/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs +++ b/src/Compilers/CSharp/Portable/Generated/BoundNodes.xml.Generated.cs @@ -267,6 +267,7 @@ internal enum BoundKind : byte + internal abstract partial class BoundInitializer : BoundNode { protected BoundInitializer(BoundKind kind, SyntaxNode syntax, bool hasErrors) @@ -6036,7 +6037,7 @@ public BoundPropertyGroup Update(ImmutableArray properties, Boun internal sealed partial class BoundCall : BoundExpression { - public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool isDelegateCall, bool expanded, bool invokedAsExtensionMethod, ImmutableArray argsToParamsOpt, BitVector defaultArguments, LookupResultKind resultKind, ImmutableArray originalMethodsOpt, TypeSymbol type, bool hasErrors = false) + public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool isDelegateCall, bool expanded, bool invokedAsExtensionMethod, ImmutableArray argsToParamsOpt, BitVector defaultArguments, LookupResultKind resultKind, ImmutableArray originalMethodsOpt, TypeSymbol type, bool hasErrors = false) : base(BoundKind.Call, syntax, type, hasErrors || receiverOpt.HasErrors() || arguments.HasErrors()) { @@ -6045,6 +6046,7 @@ public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol m RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ReceiverOpt = receiverOpt; + this.InitialBindingReceiverIsSubjectToCloning = initialBindingReceiverIsSubjectToCloning; this.Method = method; this.Arguments = arguments; this.ArgumentNamesOpt = argumentNamesOpt; @@ -6060,6 +6062,7 @@ public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol m public new TypeSymbol Type => base.Type!; public BoundExpression? ReceiverOpt { get; } + public ThreeState InitialBindingReceiverIsSubjectToCloning { get; } public MethodSymbol Method { get; } public ImmutableArray Arguments { get; } public ImmutableArray ArgumentNamesOpt { get; } @@ -6075,11 +6078,11 @@ public BoundCall(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol m [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitCall(this); - public BoundCall Update(BoundExpression? receiverOpt, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool isDelegateCall, bool expanded, bool invokedAsExtensionMethod, ImmutableArray argsToParamsOpt, BitVector defaultArguments, LookupResultKind resultKind, ImmutableArray originalMethodsOpt, TypeSymbol type) + public BoundCall Update(BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, MethodSymbol method, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool isDelegateCall, bool expanded, bool invokedAsExtensionMethod, ImmutableArray argsToParamsOpt, BitVector defaultArguments, LookupResultKind resultKind, ImmutableArray originalMethodsOpt, TypeSymbol type) { - if (receiverOpt != this.ReceiverOpt || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(method, this.Method) || arguments != this.Arguments || argumentNamesOpt != this.ArgumentNamesOpt || argumentRefKindsOpt != this.ArgumentRefKindsOpt || isDelegateCall != this.IsDelegateCall || expanded != this.Expanded || invokedAsExtensionMethod != this.InvokedAsExtensionMethod || argsToParamsOpt != this.ArgsToParamsOpt || defaultArguments != this.DefaultArguments || resultKind != this.ResultKind || originalMethodsOpt != this.OriginalMethodsOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiverOpt != this.ReceiverOpt || initialBindingReceiverIsSubjectToCloning != this.InitialBindingReceiverIsSubjectToCloning || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(method, this.Method) || arguments != this.Arguments || argumentNamesOpt != this.ArgumentNamesOpt || argumentRefKindsOpt != this.ArgumentRefKindsOpt || isDelegateCall != this.IsDelegateCall || expanded != this.Expanded || invokedAsExtensionMethod != this.InvokedAsExtensionMethod || argsToParamsOpt != this.ArgsToParamsOpt || defaultArguments != this.DefaultArguments || resultKind != this.ResultKind || originalMethodsOpt != this.OriginalMethodsOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundCall(this.Syntax, receiverOpt, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, originalMethodsOpt, type, this.HasErrors); + var result = new BoundCall(this.Syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, originalMethodsOpt, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7177,7 +7180,7 @@ public BoundHoistedFieldAccess Update(FieldSymbol fieldSymbol, TypeSymbol type) internal sealed partial class BoundPropertyAccess : BoundExpression { - public BoundPropertyAccess(SyntaxNode syntax, BoundExpression? receiverOpt, PropertySymbol propertySymbol, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) + public BoundPropertyAccess(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol propertySymbol, LookupResultKind resultKind, TypeSymbol type, bool hasErrors = false) : base(BoundKind.PropertyAccess, syntax, type, hasErrors || receiverOpt.HasErrors()) { @@ -7185,23 +7188,25 @@ public BoundPropertyAccess(SyntaxNode syntax, BoundExpression? receiverOpt, Prop RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ReceiverOpt = receiverOpt; + this.InitialBindingReceiverIsSubjectToCloning = initialBindingReceiverIsSubjectToCloning; this.PropertySymbol = propertySymbol; this.ResultKind = resultKind; } public new TypeSymbol Type => base.Type!; public BoundExpression? ReceiverOpt { get; } + public ThreeState InitialBindingReceiverIsSubjectToCloning { get; } public PropertySymbol PropertySymbol { get; } public override LookupResultKind ResultKind { get; } [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitPropertyAccess(this); - public BoundPropertyAccess Update(BoundExpression? receiverOpt, PropertySymbol propertySymbol, LookupResultKind resultKind, TypeSymbol type) + public BoundPropertyAccess Update(BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol propertySymbol, LookupResultKind resultKind, TypeSymbol type) { - if (receiverOpt != this.ReceiverOpt || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(propertySymbol, this.PropertySymbol) || resultKind != this.ResultKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiverOpt != this.ReceiverOpt || initialBindingReceiverIsSubjectToCloning != this.InitialBindingReceiverIsSubjectToCloning || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(propertySymbol, this.PropertySymbol) || resultKind != this.ResultKind || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundPropertyAccess(this.Syntax, receiverOpt, propertySymbol, resultKind, type, this.HasErrors); + var result = new BoundPropertyAccess(this.Syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning, propertySymbol, resultKind, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -7247,7 +7252,7 @@ public BoundEventAccess Update(BoundExpression? receiverOpt, EventSymbol eventSy internal sealed partial class BoundIndexerAccess : BoundExpression { - public BoundIndexerAccess(SyntaxNode syntax, BoundExpression? receiverOpt, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool expanded, ImmutableArray argsToParamsOpt, BitVector defaultArguments, ImmutableArray originalIndexersOpt, TypeSymbol type, bool hasErrors = false) + public BoundIndexerAccess(SyntaxNode syntax, BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool expanded, ImmutableArray argsToParamsOpt, BitVector defaultArguments, ImmutableArray originalIndexersOpt, TypeSymbol type, bool hasErrors = false) : base(BoundKind.IndexerAccess, syntax, type, hasErrors || receiverOpt.HasErrors() || arguments.HasErrors()) { @@ -7256,6 +7261,7 @@ public BoundIndexerAccess(SyntaxNode syntax, BoundExpression? receiverOpt, Prope RoslynDebug.Assert(type is object, "Field 'type' cannot be null (make the type nullable in BoundNodes.xml to remove this check)"); this.ReceiverOpt = receiverOpt; + this.InitialBindingReceiverIsSubjectToCloning = initialBindingReceiverIsSubjectToCloning; this.Indexer = indexer; this.Arguments = arguments; this.ArgumentNamesOpt = argumentNamesOpt; @@ -7268,6 +7274,7 @@ public BoundIndexerAccess(SyntaxNode syntax, BoundExpression? receiverOpt, Prope public new TypeSymbol Type => base.Type!; public BoundExpression? ReceiverOpt { get; } + public ThreeState InitialBindingReceiverIsSubjectToCloning { get; } public PropertySymbol Indexer { get; } public ImmutableArray Arguments { get; } public ImmutableArray ArgumentNamesOpt { get; } @@ -7280,11 +7287,11 @@ public BoundIndexerAccess(SyntaxNode syntax, BoundExpression? receiverOpt, Prope [DebuggerStepThrough] public override BoundNode? Accept(BoundTreeVisitor visitor) => visitor.VisitIndexerAccess(this); - public BoundIndexerAccess Update(BoundExpression? receiverOpt, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool expanded, ImmutableArray argsToParamsOpt, BitVector defaultArguments, ImmutableArray originalIndexersOpt, TypeSymbol type) + public BoundIndexerAccess Update(BoundExpression? receiverOpt, ThreeState initialBindingReceiverIsSubjectToCloning, PropertySymbol indexer, ImmutableArray arguments, ImmutableArray argumentNamesOpt, ImmutableArray argumentRefKindsOpt, bool expanded, ImmutableArray argsToParamsOpt, BitVector defaultArguments, ImmutableArray originalIndexersOpt, TypeSymbol type) { - if (receiverOpt != this.ReceiverOpt || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(indexer, this.Indexer) || arguments != this.Arguments || argumentNamesOpt != this.ArgumentNamesOpt || argumentRefKindsOpt != this.ArgumentRefKindsOpt || expanded != this.Expanded || argsToParamsOpt != this.ArgsToParamsOpt || defaultArguments != this.DefaultArguments || originalIndexersOpt != this.OriginalIndexersOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) + if (receiverOpt != this.ReceiverOpt || initialBindingReceiverIsSubjectToCloning != this.InitialBindingReceiverIsSubjectToCloning || !Symbols.SymbolEqualityComparer.ConsiderEverything.Equals(indexer, this.Indexer) || arguments != this.Arguments || argumentNamesOpt != this.ArgumentNamesOpt || argumentRefKindsOpt != this.ArgumentRefKindsOpt || expanded != this.Expanded || argsToParamsOpt != this.ArgsToParamsOpt || defaultArguments != this.DefaultArguments || originalIndexersOpt != this.OriginalIndexersOpt || !TypeSymbol.Equals(type, this.Type, TypeCompareKind.ConsiderEverything)) { - var result = new BoundIndexerAccess(this.Syntax, receiverOpt, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, originalIndexersOpt, type, this.HasErrors); + var result = new BoundIndexerAccess(this.Syntax, receiverOpt, initialBindingReceiverIsSubjectToCloning, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, originalIndexersOpt, type, this.HasErrors); result.CopyAttributes(this); return result; } @@ -11585,7 +11592,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type); } public override BoundNode? VisitEventAssignmentOperator(BoundEventAssignmentOperator node) { @@ -11777,7 +11784,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor { BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.PropertySymbol, node.ResultKind, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.PropertySymbol, node.ResultKind, type); } public override BoundNode? VisitEventAccess(BoundEventAccess node) { @@ -11790,7 +11797,7 @@ internal abstract partial class BoundTreeRewriter : BoundTreeVisitor BoundExpression? receiverOpt = (BoundExpression?)this.Visit(node.ReceiverOpt); ImmutableArray arguments = this.VisitList(node.Arguments); TypeSymbol? type = this.VisitType(node.Type); - return node.Update(receiverOpt, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); + return node.Update(receiverOpt, node.InitialBindingReceiverIsSubjectToCloning, node.Indexer, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.Expanded, node.ArgsToParamsOpt, node.DefaultArguments, node.OriginalIndexersOpt, type); } public override BoundNode? VisitImplicitIndexerAccess(BoundImplicitIndexerAccess node) { @@ -13745,12 +13752,12 @@ public NullabilityRewriter(ImmutableDictionary new TreeDumperNode("call", null, new TreeDumperNode[] { new TreeDumperNode("receiverOpt", null, new TreeDumperNode[] { Visit(node.ReceiverOpt, null) }), + new TreeDumperNode("initialBindingReceiverIsSubjectToCloning", node.InitialBindingReceiverIsSubjectToCloning, null), new TreeDumperNode("method", node.Method, null), new TreeDumperNode("arguments", null, from x in node.Arguments select Visit(x, null)), new TreeDumperNode("argumentNamesOpt", node.ArgumentNamesOpt, null), @@ -16518,6 +16526,7 @@ private BoundTreeDumperNodeProducer() public override TreeDumperNode VisitPropertyAccess(BoundPropertyAccess node, object? arg) => new TreeDumperNode("propertyAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiverOpt", null, new TreeDumperNode[] { Visit(node.ReceiverOpt, null) }), + new TreeDumperNode("initialBindingReceiverIsSubjectToCloning", node.InitialBindingReceiverIsSubjectToCloning, null), new TreeDumperNode("propertySymbol", node.PropertySymbol, null), new TreeDumperNode("resultKind", node.ResultKind, null), new TreeDumperNode("type", node.Type, null), @@ -16539,6 +16548,7 @@ private BoundTreeDumperNodeProducer() public override TreeDumperNode VisitIndexerAccess(BoundIndexerAccess node, object? arg) => new TreeDumperNode("indexerAccess", null, new TreeDumperNode[] { new TreeDumperNode("receiverOpt", null, new TreeDumperNode[] { Visit(node.ReceiverOpt, null) }), + new TreeDumperNode("initialBindingReceiverIsSubjectToCloning", node.InitialBindingReceiverIsSubjectToCloning, null), new TreeDumperNode("indexer", node.Indexer, null), new TreeDumperNode("arguments", null, from x in node.Arguments select Visit(x, null)), new TreeDumperNode("argumentNamesOpt", node.ArgumentNamesOpt, null), diff --git a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs index 54f9258965eb1..925513e53c794 100644 --- a/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/ClosureConversion/ClosureConversion.cs @@ -1071,6 +1071,7 @@ public override BoundNode VisitCall(BoundCall node) return node.Update( receiver, + node.InitialBindingReceiverIsSubjectToCloning, method, args, node.ArgumentNamesOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs index 785b34a087f2d..e61ab79f30682 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_AssignmentOperator.cs @@ -344,6 +344,7 @@ private BoundExpression MakePropertyAssignment( BoundExpression setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, setMethod, AppendToPossibleNull(arguments, rhsAssignment)); @@ -359,6 +360,7 @@ private BoundExpression MakePropertyAssignment( BoundCall setterCall = BoundCall.Synthesized( syntax, rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, setMethod, AppendToPossibleNull(arguments, rewrittenRight)); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs index 0fd96f0aaf08d..0256a889c5372 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_BinaryOperator.cs @@ -84,6 +84,7 @@ public override BoundNode VisitUserDefinedConditionalLogicalOperator(BoundUserDe // T.false(temp) var falseOperatorCall = BoundCall.Synthesized(syntax, receiverOpt: node.ConstrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, node.ConstrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, operatorKind.Operator() == BinaryOperatorKind.And ? node.FalseOperator : node.TrueOperator, boundTemp); // T.&(temp, y) @@ -754,7 +755,12 @@ private BoundExpression MakeTruthTestForDynamicLogicalOperator(SyntaxNode syntax } Debug.Assert(leftTruthOperator != null); - return BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), leftTruthOperator, loweredLeft); + return BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + leftTruthOperator, + loweredLeft); } private BoundExpression LowerUserDefinedBinaryOperator( @@ -776,7 +782,13 @@ private BoundExpression LowerUserDefinedBinaryOperator( // Otherwise, nothing special here. Debug.Assert(method is { }); Debug.Assert(TypeSymbol.Equals(method.ReturnType, type, TypeCompareKind.ConsiderEverything2)); - return BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), method, loweredLeft, loweredRight); + return BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + method, + loweredLeft, + loweredRight); } private BoundExpression? TrivialLiftedComparisonOperatorOptimizations( @@ -880,7 +892,11 @@ private BoundExpression MakeOptimizedGetValueOrDefault(SyntaxNode syntax, BoundE if (expression.Type.IsNullableType()) { - return BoundCall.Synthesized(syntax, expression, UnsafeGetNullableMethod(syntax, expression.Type, SpecialMember.System_Nullable_T_GetValueOrDefault)); + return BoundCall.Synthesized( + syntax, + expression, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + UnsafeGetNullableMethod(syntax, expression.Type, SpecialMember.System_Nullable_T_GetValueOrDefault)); } return expression; @@ -908,7 +924,11 @@ private BoundExpression MakeOptimizedHasValue(SyntaxNode syntax, BoundExpression private BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression expression) { Debug.Assert(expression.Type is { }); - return BoundCall.Synthesized(syntax, expression, UnsafeGetNullableMethod(syntax, expression.Type, SpecialMember.System_Nullable_T_get_HasValue)); + return BoundCall.Synthesized( + syntax, + expression, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + UnsafeGetNullableMethod(syntax, expression.Type, SpecialMember.System_Nullable_T_get_HasValue)); } private BoundExpression LowerLiftedBuiltInComparisonOperator( @@ -1756,9 +1776,9 @@ private BoundExpression LowerLiftedBooleanOperator( MethodSymbol getValueOrDefaultY = UnsafeGetNullableMethod(syntax, boundTempY.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // tempx.GetValueOrDefault() - BoundExpression callX_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTempX, getValueOrDefaultX); + BoundExpression callX_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTempX, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefaultX); // tempy.GetValueOrDefault() - BoundExpression callY_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTempY, getValueOrDefaultY); + BoundExpression callY_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTempY, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefaultY); // tempx.HasValue BoundExpression callX_HasValue = _factory.MakeNullableHasValue(syntax, boundTempX); @@ -1921,7 +1941,7 @@ private BoundExpression RewriteStringEquality(BoundBinaryOperator? oldNode, Synt var method = UnsafeGetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, loweredLeft, loweredRight); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredLeft, loweredRight); } private BoundExpression RewriteDelegateOperation(SyntaxNode syntax, BinaryOperatorKind operatorKind, BoundExpression loweredLeft, BoundExpression loweredRight, TypeSymbol type, SpecialMember member) @@ -1947,7 +1967,7 @@ private BoundExpression RewriteDelegateOperation(SyntaxNode syntax, BinaryOperat Debug.Assert((object)method != null); BoundExpression call = _inExpressionLambda ? new BoundBinaryOperator(syntax, operatorKind, null, method, constrainedToTypeOpt: null, default(LookupResultKind), loweredLeft, loweredRight, method.ReturnType) - : (BoundExpression)BoundCall.Synthesized(syntax, receiverOpt: null, method, loweredLeft, loweredRight); + : (BoundExpression)BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredLeft, loweredRight); BoundExpression result = method.ReturnType.SpecialType == SpecialType.System_Delegate ? MakeConversionNode(syntax, call, Conversion.ExplicitReference, type, @checked: false) : call; @@ -1982,7 +2002,7 @@ private BoundExpression RewriteDecimalBinaryOperation(SyntaxNode syntax, BoundEx var method = UnsafeGetSpecialTypeMethod(syntax, member); Debug.Assert((object)method != null); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, loweredLeft, loweredRight); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredLeft, loweredRight); } private BoundExpression MakeNullCheck(SyntaxNode syntax, BoundExpression rewrittenExpr, BinaryOperatorKind operatorKind) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs index abe9d14bb34ab..190d6b1692879 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Call.cs @@ -432,6 +432,7 @@ private BoundExpression MakeCall( rewrittenBoundCall = new BoundCall( syntax, rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, rewrittenArguments, argumentNamesOpt: default(ImmutableArray), @@ -448,6 +449,7 @@ private BoundExpression MakeCall( { rewrittenBoundCall = node.Update( rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, rewrittenArguments, argumentNamesOpt: default(ImmutableArray), @@ -1522,7 +1524,8 @@ private BoundExpression CreateEmptyArray(SyntaxNode syntax, ArrayTypeSymbol arra arrayEmpty = arrayEmpty.Construct(ImmutableArray.Create(elementType)); return new BoundCall( syntax, - null, + receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, arrayEmpty, ImmutableArray.Empty, default(ImmutableArray), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs index dc0b3bfa36ace..65274135476a4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CollectionExpression.cs @@ -222,6 +222,7 @@ private BoundExpression VisitCollectionBuilderCollectionExpression(BoundCollecti var call = new BoundCall( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: constructMethod, arguments: ImmutableArray.Create(span), argumentNamesOpt: default, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs index f80ed9fa146f9..b357e6a484254 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_CompoundAssignmentOperator.cs @@ -385,6 +385,7 @@ private BoundIndexerAccess TransformIndexerAccessContinued( return new BoundIndexerAccess( syntax, transformedReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, indexer, rewrittenArguments, argumentNamesOpt: default(ImmutableArray), @@ -591,7 +592,7 @@ private BoundExpression TransformCompoundAssignmentLHS(BoundExpression originalL // This is a temporary object that will be rewritten away before the lowering completes. return propertyAccess.Update(TransformPropertyOrEventReceiver(propertyAccess.PropertySymbol, propertyAccess.ReceiverOpt, isRegularCompoundAssignment, stores, temps), - propertyAccess.PropertySymbol, propertyAccess.ResultKind, propertyAccess.Type); + propertyAccess.InitialBindingReceiverIsSubjectToCloning, propertyAccess.PropertySymbol, propertyAccess.ResultKind, propertyAccess.Type); } } break; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs index c74cd4f9f5e8a..a5a26ff0f5475 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Conversion.cs @@ -966,7 +966,7 @@ private BoundExpression RewriteNullableConversion( // (If the source is known to be possibly null then we need to keep the call to get Value // in place so that it throws at runtime.) MethodSymbol get_Value = UnsafeGetNullableMethod(syntax, rewrittenOperandType, SpecialMember.System_Nullable_T_get_Value); - value = BoundCall.Synthesized(syntax, rewrittenOperand, get_Value); + value = BoundCall.Synthesized(syntax, rewrittenOperand, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, get_Value); } conversion.AssertUnderlyingConversionsChecked(); @@ -1066,7 +1066,7 @@ private BoundExpression RewriteFullyLiftedBuiltInConversion( UnsafeGetNullableMethod(syntax, type, SpecialMember.System_Nullable_T__ctor), MakeConversionNode( syntax, - BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault), + BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault), conversion.UnderlyingConversions[0], type.GetNullableUnderlyingType(), @checked)); @@ -1109,7 +1109,12 @@ private BoundExpression RewriteFullyLiftedBuiltInConversion( { Debug.Assert(conversion.Method is { }); var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; - return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, nonNullValue), type); + return MakeLiftedUserDefinedConversionConsequence(BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + conversion.Method, + nonNullValue), type); } return DistributeLiftedConversionIntoLiftedOperand(syntax, operand, conversion, false, type); @@ -1249,7 +1254,12 @@ private BoundExpression RewriteUserDefinedConversion( } var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; - BoundExpression result = BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, rewrittenOperand); + BoundExpression result = BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + conversion.Method, + rewrittenOperand); Debug.Assert(TypeSymbol.Equals(result.Type, rewrittenType, TypeCompareKind.ConsiderEverything2)); return result; } @@ -1316,12 +1326,17 @@ private BoundExpression RewriteLiftedUserDefinedConversion( BoundExpression condition = _factory.MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() - BoundCall callGetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); + BoundCall callGetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); // op_Whatever(temp.GetValueOrDefault()) Debug.Assert(conversion.Method is { }); var constrainedToTypeOpt = conversion.ConstrainedToTypeOpt; - BoundCall userDefinedCall = BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), conversion.Method, callGetValueOrDefault); + BoundCall userDefinedCall = BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + conversion.Method, + callGetValueOrDefault); // new R?(op_Whatever(temp.GetValueOrDefault()) BoundExpression consequence = MakeLiftedUserDefinedConversionConsequence(userDefinedCall, rewrittenType); @@ -1619,7 +1634,7 @@ private BoundExpression RewriteDecimalConversionCore(SyntaxNode syntax, BoundExp else { Debug.Assert(TypeSymbol.Equals(method.ReturnType, toType, TypeCompareKind.ConsiderEverything2)); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, operand); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, operand); } } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs index db02f764d57a9..9493816f5d235 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_Event.cs @@ -247,6 +247,7 @@ private BoundExpression MakeEventAccess( getOrCreateCall = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: getOrCreateMethod, arg0: fieldAccess); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs index 8eb1a3aae1b5f..6a6203f3c642c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ForEachStatement.cs @@ -191,6 +191,7 @@ private BoundStatement RewriteForEachEnumerator( BoundCall.Synthesized( syntax: forEachSyntax, receiverOpt: boundEnumeratorVar, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: enumeratorInfo.CurrentPropertyGetter))); // V v = (V)(T)e.Current; -OR- (D1 d1, ...) = (V)(T)e.Current; @@ -407,7 +408,7 @@ private BoundStatement WrapWithTryFinallyDispose( BoundStatement disposableVarDecl = MakeLocalDeclaration(forEachSyntax, disposableVar, disposableVarInitValue); // d.Dispose() - BoundExpression disposeCall = BoundCall.Synthesized(syntax: forEachSyntax, receiverOpt: boundDisposableVar, method: disposeMethod); + BoundExpression disposeCall = BoundCall.Synthesized(syntax: forEachSyntax, receiverOpt: boundDisposableVar, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: disposeMethod); BoundStatement disposeCallStatement = new BoundExpressionStatement(forEachSyntax, expression: disposeCall); // if (d != null) d.Dispose(); @@ -527,7 +528,7 @@ private BoundExpression SynthesizeCall(MethodArgumentInfo methodArgumentInfo, CS // Generate a call with literally zero arguments Debug.Assert(methodArgumentInfo.Arguments.IsEmpty); - return BoundCall.Synthesized(syntax, receiver, methodArgumentInfo.Method, arguments: ImmutableArray.Empty); + return BoundCall.Synthesized(syntax, receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, methodArgumentInfo.Method, arguments: ImmutableArray.Empty); } /// @@ -665,6 +666,7 @@ private BoundStatement RewriteForEachStatementAsFor(BoundForEachStatement node, return BoundCall.Synthesized( syntax: node.Syntax, receiverOpt: boundArrayVar, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, arg.indexerGet, boundPositionVar); }, @@ -673,6 +675,7 @@ private BoundStatement RewriteForEachStatementAsFor(BoundForEachStatement node, return BoundCall.Synthesized( syntax: node.Syntax, receiverOpt: boundArrayVar, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, arg.lengthGet); }, arg: (indexerGet, lengthGet)); @@ -1035,7 +1038,7 @@ private BoundStatement RewriteMultiDimensionalArrayForEachEnumerator( type: intType)); // a.GetUpperBound(dimension) - BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getUpperBoundMethod, dimensionArgument); + BoundExpression currentDimensionUpperBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getUpperBoundMethod, dimensionArgument); // int q_dimension = a.GetUpperBound(dimension); upperVarDecl[dimension] = MakeLocalDeclaration(forEachSyntax, upperVar[dimension], currentDimensionUpperBound); @@ -1089,7 +1092,7 @@ private BoundStatement RewriteMultiDimensionalArrayForEachEnumerator( type: intType)); // a.GetLowerBound(dimension) - BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, getLowerBoundMethod, dimensionArgument); + BoundExpression currentDimensionLowerBound = BoundCall.Synthesized(forEachSyntax, boundArrayVar, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getLowerBoundMethod, dimensionArgument); // int p_dimension = a.GetLowerBound(dimension); BoundStatement positionVarDecl = MakeLocalDeclaration(forEachSyntax, positionVar[dimension], currentDimensionLowerBound); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs index 8f1aa1221f27e..d250240529515 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_IndexerAccess.cs @@ -120,8 +120,8 @@ private BoundExpression MakeIndexerAccess( // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. return oldNodeOpt != null ? - oldNodeOpt.Update(rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : - new BoundIndexerAccess(syntax, rewrittenReceiver, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); + oldNodeOpt.Update(rewrittenReceiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type) : + new BoundIndexerAccess(syntax, rewrittenReceiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, type); } else { @@ -547,7 +547,7 @@ private BoundExpression GetUnderlyingIndexerOrSliceAccess( Debug.Assert(locals is not null); rewrittenIndexerAccess = indexerAccess.Update( - receiver, indexerAccess.Indexer, rewrittenArguments, + receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, indexerAccess.Indexer, rewrittenArguments, indexerAccess.ArgumentNamesOpt, indexerAccess.ArgumentRefKindsOpt, indexerAccess.Expanded, indexerAccess.ArgsToParamsOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs index 39e1cc4e5dc2d..a12604751d8b0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_LockStatement.cs @@ -64,6 +64,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node) exitCallExpr = BoundCall.Synthesized( lockSyntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, exitMethod, boundLockTemp); } @@ -109,6 +110,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node) BoundCall.Synthesized( lockSyntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, enterMethod, boundLockTemp, boundLockTakenTemp)); @@ -158,6 +160,7 @@ public override BoundNode VisitLockStatement(BoundLockStatement node) enterCallExpr = BoundCall.Synthesized( lockSyntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, enterMethod, boundLockTemp); } diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs index 533fd22262312..851da6ffc4147 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingAssignmentOperator.cs @@ -100,7 +100,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() } // tmp = lhsRead.GetValueOrDefault(); - var tmp = _factory.StoreToTemp(BoundCall.Synthesized(leftOperand.Syntax, lhsRead, getValueOrDefault), + var tmp = _factory.StoreToTemp(BoundCall.Synthesized(leftOperand.Syntax, lhsRead, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault), out var getValueOrDefaultStore); stores.Add(getValueOrDefaultStore); @@ -123,7 +123,7 @@ BoundExpression rewriteNullCoalescingAssignmentForValueType() isCompoundAssignment: false); // lhsRead.HasValue - var lhsReadHasValue = BoundCall.Synthesized(leftOperand.Syntax, lhsRead, hasValue); + var lhsReadHasValue = BoundCall.Synthesized(leftOperand.Syntax, lhsRead, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, hasValue); // { tmp = b; transformedLhs = tmp; tmp } var alternative = _factory.Sequence(ImmutableArray.Empty, ImmutableArray.Create(tmpAssignment, transformedLhsAssignment), tmp); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingOperator.cs index 2e2a3d49d965c..d7f633f666616 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_NullCoalescingOperator.cs @@ -138,7 +138,7 @@ private BoundExpression MakeNullCoalescingOperator( && rewrittenRight.Type.Equals(rewrittenLeft.Type.GetNullableUnderlyingType(), TypeCompareKind.AllIgnoreOptions) && TryGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeft.Type, SpecialMember.System_Nullable_T_GetValueOrDefault, out MethodSymbol getValueOrDefault)) { - return BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault); + return BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); } // We lower left ?? right to @@ -246,7 +246,7 @@ private BoundExpression GetConvertedLeftForNullCoalescingOperator(BoundExpressio { TypeSymbol strippedLeftType = rewrittenLeftType.GetNullableUnderlyingType(); MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(rewrittenLeft.Syntax, rewrittenLeftType, SpecialMember.System_Nullable_T_GetValueOrDefault); - rewrittenLeft = BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, getValueOrDefault); + rewrittenLeft = BoundCall.Synthesized(rewrittenLeft.Syntax, rewrittenLeft, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); if (TypeSymbol.Equals(strippedLeftType, rewrittenResultType, TypeCompareKind.ConsiderEverything2)) { return rewrittenLeft; diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs index b5632a1b8bd2e..d89d2ebd350d0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_ObjectCreationExpression.cs @@ -335,7 +335,8 @@ private BoundExpression MakeNewT(SyntaxNode syntax, TypeParameterSymbol typePara var createInstanceCall = new BoundCall( syntax, - null, + receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, ImmutableArray.Empty, default(ImmutableArray), diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PropertyAccess.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PropertyAccess.cs index f5082b336447e..4d99b1d246af4 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PropertyAccess.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_PropertyAccess.cs @@ -55,8 +55,8 @@ private BoundExpression MakePropertyAccess( // This node will be rewritten with MakePropertyAssignment when rewriting the enclosing BoundAssignmentOperator. return oldNodeOpt != null ? - oldNodeOpt.Update(rewrittenReceiverOpt, propertySymbol, resultKind, type) : - new BoundPropertyAccess(syntax, rewrittenReceiverOpt, propertySymbol, resultKind, type); + oldNodeOpt.Update(rewrittenReceiverOpt, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, propertySymbol, resultKind, type) : + new BoundPropertyAccess(syntax, rewrittenReceiverOpt, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, propertySymbol, resultKind, type); } else { @@ -83,8 +83,8 @@ private BoundExpression MakePropertyGetAccess( { Debug.Assert(argumentRefKindsOpt.IsDefaultOrEmpty); return oldNodeOpt != null ? - oldNodeOpt.Update(rewrittenReceiver, property, LookupResultKind.Viable, property.Type) : - new BoundPropertyAccess(syntax, rewrittenReceiver, property, LookupResultKind.Viable, property.Type); + oldNodeOpt.Update(rewrittenReceiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, property, LookupResultKind.Viable, property.Type) : + new BoundPropertyAccess(syntax, rewrittenReceiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, property, LookupResultKind.Viable, property.Type); } else { @@ -97,6 +97,7 @@ private BoundExpression MakePropertyGetAccess( return BoundCall.Synthesized( syntax, rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getMethod, rewrittenArguments, argumentRefKindsOpt); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs index 6d74825e5eed1..62ac0d538b0b0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringConcat.cs @@ -305,7 +305,7 @@ private BoundExpression RewriteStringConcatenationTwoExprs(SyntaxNode syntax, Bo var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringString); Debug.Assert((object)method != null); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, loweredLeft, loweredRight); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredLeft, loweredRight); } private BoundExpression RewriteStringConcatenationThreeExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird) @@ -317,7 +317,7 @@ private BoundExpression RewriteStringConcatenationThreeExprs(SyntaxNode syntax, var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringStringString); Debug.Assert((object)method != null); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird)); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird)); } private BoundExpression RewriteStringConcatenationFourExprs(SyntaxNode syntax, BoundExpression loweredFirst, BoundExpression loweredSecond, BoundExpression loweredThird, BoundExpression loweredFourth) @@ -330,7 +330,7 @@ private BoundExpression RewriteStringConcatenationFourExprs(SyntaxNode syntax, B var method = UnsafeGetSpecialTypeMethod(syntax, SpecialMember.System_String__ConcatStringStringStringString); Debug.Assert((object)method != null); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird, loweredFourth)); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, ImmutableArray.Create(loweredFirst, loweredSecond, loweredThird, loweredFourth)); } private BoundExpression RewriteStringConcatenationManyExprs(SyntaxNode syntax, ImmutableArray loweredArgs) @@ -343,7 +343,7 @@ private BoundExpression RewriteStringConcatenationManyExprs(SyntaxNode syntax, I var array = _factory.ArrayOrEmpty(_factory.SpecialType(SpecialType.System_String), loweredArgs); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, array); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, array); } /// @@ -431,7 +431,7 @@ private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpres // types to all special value types. if (structToStringMethod != null && (expr.Type.SpecialType != SpecialType.None && !isFieldOfMarshalByRef(expr, _compilation))) { - return BoundCall.Synthesized(expr.Syntax, expr, structToStringMethod); + return BoundCall.Synthesized(expr.Syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, structToStringMethod); } // - It's a reference type (excluding unconstrained generics): no copy @@ -456,7 +456,7 @@ private BoundExpression ConvertConcatExprToString(SyntaxNode syntax, BoundExpres { expr = new BoundPassByCopy(expr.Syntax, expr, expr.Type); } - return BoundCall.Synthesized(expr.Syntax, expr, objectToStringMethod); + return BoundCall.Synthesized(expr.Syntax, expr, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, objectToStringMethod); } if (callWithoutCopy) @@ -487,6 +487,7 @@ BoundExpression makeConditionalAccess(BoundExpression receiver) whenNotNull: BoundCall.Synthesized( syntax, new BoundConditionalReceiver(syntax, currentConditionalAccessID, expr.Type), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, objectToStringMethod), whenNullOpt: null, id: currentConditionalAccessID, diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs index a35c09294453d..291ca6e5308a2 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_StringInterpolation.cs @@ -373,7 +373,7 @@ private BoundExpression LowerPartsToString(InterpolatedStringHandlerData data, I // resultTemp = builderTemp.ToStringAndClear(); var toStringAndClear = (MethodSymbol)Binder.GetWellKnownTypeMember(_compilation, WellKnownMember.System_Runtime_CompilerServices_DefaultInterpolatedStringHandler__ToStringAndClear, _diagnostics, syntax: syntax); BoundExpression toStringAndClearCall = toStringAndClear is not null - ? BoundCall.Synthesized(syntax, result.HandlerTemp, toStringAndClear) + ? BoundCall.Synthesized(syntax, result.HandlerTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, toStringAndClear) : new BoundBadExpression(syntax, LookupResultKind.Empty, symbols: ImmutableArray.Empty, childBoundNodes: ImmutableArray.Empty, type); return result.WithFinalResult(toStringAndClearCall); diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs index 76904e3cef75d..5e3384761eba7 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UnaryOperator.cs @@ -113,7 +113,12 @@ private BoundExpression MakeUnaryOperator( Debug.Assert(TypeSymbol.Equals(type, method.ReturnType, TypeCompareKind.ConsiderEverything2)); if (!_inExpressionLambda || kind == UnaryOperatorKind.UserDefinedTrue || kind == UnaryOperatorKind.UserDefinedFalse) { - return BoundCall.Synthesized(syntax, receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), method, loweredOperand); + return BoundCall.Synthesized( + syntax, + receiverOpt: constrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, constrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + method, + loweredOperand); } } else if (kind.Operator() == UnaryOperatorKind.UnaryPlus) @@ -161,7 +166,7 @@ private BoundExpression MakeUnaryOperator( method = (MethodSymbol)_compilation.Assembly.GetSpecialTypeMember(SpecialMember.System_Decimal__op_UnaryNegation); if (!_inExpressionLambda) { - return BoundCall.Synthesized(syntax, receiverOpt: null, method, loweredOperand); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, loweredOperand); } } @@ -202,7 +207,7 @@ private BoundExpression LowerLiftedUnaryOperator( BoundExpression condition = _factory.MakeNullableHasValue(syntax, boundTemp); // temp.GetValueOrDefault() - BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); + BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); // new R?(temp.GetValueOrDefault()) BoundExpression consequence = GetLiftedUnaryOperatorConsequence(kind, syntax, method, constrainedToTypeOpt, type, call_GetValueOrDefault); @@ -641,7 +646,12 @@ private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator if (!isLifted) { - return BoundCall.Synthesized(syntax, receiverOpt: node.ConstrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, node.ConstrainedToTypeOpt), node.MethodOpt, rewrittenArgument); + return BoundCall.Synthesized( + syntax, + receiverOpt: node.ConstrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, node.ConstrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + node.MethodOpt, + rewrittenArgument); } // S? temp = operand; @@ -663,10 +673,15 @@ private BoundExpression MakeUserDefinedIncrementOperator(BoundIncrementOperator BoundExpression condition = _factory.MakeNullableHasValue(node.Syntax, boundTemp); // temp.GetValueOrDefault() - BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, getValueOrDefault); + BoundExpression call_GetValueOrDefault = BoundCall.Synthesized(syntax, boundTemp, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); // op_Increment(temp.GetValueOrDefault()) - BoundExpression userDefinedCall = BoundCall.Synthesized(syntax, receiverOpt: node.ConstrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, node.ConstrainedToTypeOpt), node.MethodOpt, call_GetValueOrDefault); + BoundExpression userDefinedCall = BoundCall.Synthesized( + syntax, + receiverOpt: node.ConstrainedToTypeOpt is null ? null : new BoundTypeExpression(syntax, aliasOpt: null, node.ConstrainedToTypeOpt), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + node.MethodOpt, + call_GetValueOrDefault); // new S?(op_Increment(temp.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, userDefinedCall); @@ -808,7 +823,7 @@ private BoundExpression MakeDecimalIncDecOperator(SyntaxNode syntax, BinaryOpera { Debug.Assert(operand.Type is { SpecialType: SpecialType.System_Decimal }); MethodSymbol method = GetDecimalIncDecOperator(oper); - return BoundCall.Synthesized(syntax, receiverOpt: null, method, operand); + return BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, operand); } private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, BinaryOperatorKind oper, BoundExpression operand) @@ -823,9 +838,9 @@ private BoundExpression MakeLiftedDecimalIncDecOperator(SyntaxNode syntax, Binar // x.HasValue BoundExpression condition = _factory.MakeNullableHasValue(syntax, operand); // x.GetValueOrDefault() - BoundExpression getValueCall = BoundCall.Synthesized(syntax, operand, getValueOrDefault); + BoundExpression getValueCall = BoundCall.Synthesized(syntax, operand, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); // op_Inc(x.GetValueOrDefault()) - BoundExpression methodCall = BoundCall.Synthesized(syntax, receiverOpt: null, method, getValueCall); + BoundExpression methodCall = BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, getValueCall); // new decimal?(op_Inc(x.GetValueOrDefault())) BoundExpression consequence = new BoundObjectCreationExpression(syntax, ctor, methodCall); // default(decimal?) diff --git a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs index 23d48da46afdb..eb1915e4adda0 100644 --- a/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs +++ b/src/Compilers/CSharp/Portable/Lowering/LocalRewriter/LocalRewriter_UsingStatement.cs @@ -361,7 +361,7 @@ private BoundStatement RewriteUsingStatementTryFinally( { MethodSymbol getValueOrDefault = UnsafeGetNullableMethod(resourceTypeSyntax, local.Type, SpecialMember.System_Nullable_T_GetValueOrDefault); // local.GetValueOrDefault() - disposedExpression = BoundCall.Synthesized(resourceSyntax, local, getValueOrDefault); + disposedExpression = BoundCall.Synthesized(resourceSyntax, local, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, getValueOrDefault); } else { diff --git a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs index 6f6ab63d0bc45..1e9e6a192844c 100644 --- a/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs +++ b/src/Compilers/CSharp/Portable/Lowering/MethodToClassRewriter.cs @@ -223,7 +223,7 @@ public override BoundNode VisitPropertyAccess(BoundPropertyAccess node) { var rewrittenPropertySymbol = VisitPropertySymbol(node.PropertySymbol); var rewrittenReceiver = (BoundExpression?)Visit(node.ReceiverOpt); - return node.Update(rewrittenReceiver, rewrittenPropertySymbol, node.ResultKind, VisitType(node.Type)); + return node.Update(rewrittenReceiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, rewrittenPropertySymbol, node.ResultKind, VisitType(node.Type)); } public override BoundNode VisitCall(BoundCall node) @@ -244,6 +244,7 @@ public override BoundNode VisitCall(BoundCall node) return node.Update( rewrittenReceiver, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, rewrittenMethodSymbol, rewrittenArguments, node.ArgumentNamesOpt, diff --git a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs index 99ac3c71125c0..9acb45ac30e19 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SpillSequenceSpiller.cs @@ -451,6 +451,7 @@ private BoundExpression Spill( { Debug.Assert(call.Arguments.Length == 1); return call.Update(Spill(builder, call.ReceiverOpt, ReceiverSpillRefKind(call.ReceiverOpt)), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, call.Method, ImmutableArray.Create(Spill(builder, call.Arguments[0]))); } @@ -464,6 +465,7 @@ private BoundExpression Spill( { Debug.Assert(call.Arguments.Length == 2); return call.Update(Spill(builder, call.ReceiverOpt, ReceiverSpillRefKind(call.ReceiverOpt)), + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, call.Method, ImmutableArray.Create(Spill(builder, call.Arguments[0]), Spill(builder, call.Arguments[1]))); } @@ -1052,7 +1054,7 @@ public override BoundNode VisitCall(BoundCall node) builder = receiverBuilder; } - return UpdateExpression(builder, node.Update(receiver, node.Method, arguments)); + return UpdateExpression(builder, node.Update(receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, node.Method, arguments)); } private static RefKind ReceiverSpillRefKind(BoundExpression receiver) diff --git a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs index 6db5d2f55bf29..c12732a9f4676 100644 --- a/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs +++ b/src/Compilers/CSharp/Portable/Lowering/SyntheticBoundNodeFactory.cs @@ -811,7 +811,7 @@ public BoundCall Call(BoundExpression? receiver, MethodSymbol method, ImmutableA Debug.Assert(method.ParameterCount == args.Length); return new BoundCall( - Syntax, receiver, method, args, + Syntax, receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, args, argumentNamesOpt: default(ImmutableArray), argumentRefKindsOpt: getArgumentRefKinds(method, useStrictArgumentRefKinds), isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: default(ImmutableArray), defaultArguments: default(BitVector), resultKind: LookupResultKind.Viable, type: method.ReturnType, hasErrors: method.OriginalDefinition is ErrorMethodSymbol) @@ -847,7 +847,7 @@ public BoundCall Call(BoundExpression? receiver, MethodSymbol method, ImmutableA { Debug.Assert(method.ParameterCount == args.Length); return new BoundCall( - Syntax, receiver, method, args, + Syntax, receiver, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method, args, argumentNamesOpt: default(ImmutableArray), argumentRefKindsOpt: refKinds, isDelegateCall: false, expanded: false, invokedAsExtensionMethod: false, argsToParamsOpt: ImmutableArray.Empty, defaultArguments: default(BitVector), resultKind: LookupResultKind.Viable, type: method.ReturnType) { WasCompilerGenerated = true }; @@ -1694,7 +1694,11 @@ internal BoundExpression MakeNullCheck(SyntaxNode syntax, BoundExpression rewrit internal BoundExpression MakeNullableHasValue(SyntaxNode syntax, BoundExpression expression) { // https://github.com/dotnet/roslyn/issues/58335: consider restoring the 'private' accessibility of 'static LocalRewriter.UnsafeGetNullableMethod()' - return BoundCall.Synthesized(syntax, expression, LocalRewriter.UnsafeGetNullableMethod(syntax, expression.Type, CodeAnalysis.SpecialMember.System_Nullable_T_get_HasValue, Compilation, Diagnostics)); + return BoundCall.Synthesized( + syntax, + expression, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, + LocalRewriter.UnsafeGetNullableMethod(syntax, expression.Type, CodeAnalysis.SpecialMember.System_Nullable_T_get_HasValue, Compilation, Diagnostics)); } internal BoundExpression RewriteNullableNullEquality( diff --git a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs index e69f0c4e5eba5..9a9fc89a5e4db 100644 --- a/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/Synthesized/SynthesizedEntryPointSymbol.cs @@ -288,11 +288,12 @@ private static CSharpSyntaxNode DummySyntax() return (CSharpSyntaxNode)syntaxTree.GetRoot(); } - private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundExpression receiver, MethodSymbol method) + private static BoundCall CreateParameterlessCall(CSharpSyntaxNode syntax, BoundExpression receiver, ThreeState receiverIsSubjectToCloning, MethodSymbol method) { return new BoundCall( syntax, receiver, + initialBindingReceiverIsSubjectToCloning: receiverIsSubjectToCloning, method, ImmutableArray.Empty, default(ImmutableArray), @@ -344,6 +345,7 @@ internal AsyncForwardEntryPoint(CSharpCompilation compilation, NamedTypeSymbol c BoundCall userMainInvocation = new BoundCall( syntax: _userMainReturnTypeSyntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: userMain, arguments: arguments, argumentNamesOpt: default(ImmutableArray), @@ -474,7 +476,7 @@ internal override BoundBlock CreateBody(BindingDiagnosticBag diagnostics) { WasCompilerGenerated = true }; Debug.Assert(!initializer.ReturnType.IsDynamic()); - var initializeCall = CreateParameterlessCall(syntax, scriptLocal, initializer); + var initializeCall = CreateParameterlessCall(syntax, scriptLocal, receiverIsSubjectToCloning: ThreeState.False, initializer); BoundExpression getAwaiterGetResultCall; if (!binder.GetAwaitableExpressionInfo(initializeCall, out getAwaiterGetResultCall, syntax, diagnostics)) { @@ -592,6 +594,7 @@ internal override BoundBlock CreateBody(BindingDiagnosticBag diagnostics) var initializeResult = CreateParameterlessCall( syntax, submissionLocal, + receiverIsSubjectToCloning: ThreeState.False, initializer); Debug.Assert(TypeSymbol.Equals(initializeResult.Type, _returnType.Type, TypeCompareKind.ConsiderEverything2)); var returnStatement = new BoundReturnStatement( diff --git a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs index 473f0e887e8ef..edf2936c1c7e4 100644 --- a/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Semantics/RefFieldTests.cs @@ -25671,6 +25671,225 @@ class B2 : A Diagnostic(ErrorCode.ERR_UnscopedRefAttributeUnsupportedMemberTarget, "UnscopedRef").WithLocation(17, 6)); } + [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/67697")] + public void UnscopedRefAttribute_NestedAccess_MethodOrProperty(bool firstIsMethod, bool secondIsMethod) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + var c = new C(); + c.Value() = 12; + System.Console.WriteLine(c.Value()); + + class C + { + private S1 s1; + public ref int Value() => ref s1.S2{{csharp(firstIsMethod)}}.Value{{csharp(secondIsMethod)}}; + } + + struct S1 + { + private S2 s2; + [UnscopedRef] public ref S2 S2{{csharp(firstIsMethod)}} => ref s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int Value{{csharp(secondIsMethod)}} => ref value; + } + """; + var verifier = CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, expectedOutput: "12", verify: Verification.Fails); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.Value", $$""" + { + // Code size 17 (0x11) + .maxstack 1 + // sequence point: s1.S2{{csharp(firstIsMethod)}}.Value{{csharp(secondIsMethod)}} + IL_0000: ldarg.0 + IL_0001: ldflda "S1 C.s1" + IL_0006: call "ref S2 S1.S2{{il(firstIsMethod)}}" + IL_000b: call "ref int S2.Value{{il(secondIsMethod)}}" + IL_0010: ret + } + """); + + static string csharp(bool method) => method ? "()" : ""; + static string il(bool method) => method ? "()" : ".get"; + } + + [Fact] + public void UnscopedRefAttribute_NestedAccess_Properties_Invalid() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + class C + { + private S1 s1; + public ref int Value() => ref s1.S2.Value; + } + + struct S1 + { + private S2 s2; + public S2 S2 => s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int Value => ref value; + } + """; + CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics( + // 0.cs(6,35): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // public ref int Value() => ref s1.S2.Value; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1.S2").WithLocation(6, 35), + // 0.cs(11,16): warning CS0649: Field 'S1.s2' is never assigned to, and will always have its default value + // private S2 s2; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s2").WithArguments("S1.s2", "").WithLocation(11, 16)); + } + + [Theory, CombinatorialData] + public void UnscopedRefAttribute_NestedAccess_MethodOrProperty_Readonly( + [CombinatorialValues("", "()")] string op1, [CombinatorialValues("", "()")] string op2) + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + class C + { + private S1 s1; + public ref int Value() => ref s1.S2{{op1}}.Value{{op2}}; + } + + struct S1 + { + private readonly S2 s2; + [UnscopedRef] public ref readonly S2 S2{{op1}} => ref s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int Value{{op2}} => ref value; + } + """; + CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics( + // 0.cs(6,35): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // public ref int Value() => ref s1.S2.Value; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, $"s1.S2{op1}").WithLocation(6, 35)); + } + + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/67626")] + public void UnscopedRefAttribute_NestedAccess_Indexer() + { + var source = $$""" + using System.Diagnostics.CodeAnalysis; + + var c = new C(); + c.Value(0) = 12; + System.Console.WriteLine(c.Value(0)); + + class C + { + private S1 s1; + public ref int Value(int i) => ref s1[i].Value; + } + + struct S1 + { + private S2 s2; + [UnscopedRef] public ref S2 this[int i] => ref s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int Value => ref value; + } + """; + var verifier = CompileAndVerify(new[] { source, UnscopedRefAttributeDefinition }, expectedOutput: "12", verify: Verification.Fails); + verifier.VerifyDiagnostics(); + verifier.VerifyMethodBody("C.Value", """ + { + // Code size 18 (0x12) + .maxstack 2 + // sequence point: s1[i].Value + IL_0000: ldarg.0 + IL_0001: ldflda "S1 C.s1" + IL_0006: ldarg.1 + IL_0007: call "ref S2 S1.this[int].get" + IL_000c: call "ref int S2.Value.get" + IL_0011: ret + } + """); + } + + [Fact] + public void UnscopedRefAttribute_NestedAccess_Indexer_Invalid() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + class C + { + private S1 s1; + public ref int Value(int i) => ref s1[i].Value; + } + + struct S1 + { + private S2 s2; + public S2 this[int i] => s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int Value => ref value; + } + """; + CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics( + // 0.cs(6,40): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // public ref int Value(int i) => ref s1[i].Value; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1[i]").WithLocation(6, 40), + // 0.cs(11,16): warning CS0649: Field 'S1.s2' is never assigned to, and will always have its default value + // private S2 s2; + Diagnostic(ErrorCode.WRN_UnassignedInternalField, "s2").WithArguments("S1.s2", "").WithLocation(11, 16)); + } + + [Fact] + public void UnscopedRefAttribute_NestedAccess_Indexer_Readonly() + { + var source = """ + using System.Diagnostics.CodeAnalysis; + + class C + { + private S1 s1; + public ref int Value(int i) => ref s1[i][i]; + } + + struct S1 + { + private readonly S2 s2; + [UnscopedRef] public ref readonly S2 this[int i] => ref s2; + } + + struct S2 + { + private int value; + [UnscopedRef] public ref int this[int i] => ref value; + } + """; + CreateCompilation(new[] { source, UnscopedRefAttributeDefinition }).VerifyDiagnostics( + // 0.cs(6,40): error CS8156: An expression cannot be used in this context because it may not be passed or returned by reference + // public ref int Value(int i) => ref s1[i].Value; + Diagnostic(ErrorCode.ERR_RefReturnLvalueExpected, "s1[i]").WithLocation(6, 40)); + } + [WorkItem(64507, "https://github.com/dotnet/roslyn/issues/64507")] [Theory] [InlineData(0)] diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs index 74b148894d774..3ea66b14fe8f6 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Rewriters/LocalDeclarationRewriter.cs @@ -114,6 +114,7 @@ private static void CreateLocal( var call = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: method, arguments: ImmutableArray.Create(type, name, customTypeInfoPayloadId, customTypeInfoPayload)); statements.Add(new BoundExpressionStatement(syntax, call)); diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ExceptionLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ExceptionLocalSymbol.cs index 1089f8c563fc5..5745ab3d97999 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ExceptionLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ExceptionLocalSymbol.cs @@ -26,7 +26,7 @@ internal override bool IsWritableVariable internal override BoundExpression RewriteLocal(CSharpCompilation compilation, SyntaxNode syntax, DiagnosticBag diagnostics) { var method = GetIntrinsicMethod(compilation, _getExceptionMethodName); - var call = BoundCall.Synthesized(syntax, receiverOpt: null, method: method); + var call = BoundCall.Synthesized(syntax, receiverOpt: null, initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: method); return ConvertToLocalType(compilation, call, this.Type, diagnostics); } } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectAddressLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectAddressLocalSymbol.cs index 2d50b09c5f797..23cf037b0702d 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectAddressLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectAddressLocalSymbol.cs @@ -38,6 +38,7 @@ internal override BoundExpression RewriteLocal(CSharpCompilation compilation, Sy var call = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: method, arguments: ImmutableArray.Create(argument)); Debug.Assert(TypeSymbol.Equals(call.Type, this.Type, TypeCompareKind.ConsiderEverything2)); diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs index db9ca64429935..1e343bdf15b89 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ObjectIdLocalSymbol.cs @@ -91,6 +91,7 @@ private static BoundExpression InvokeGetMethod(MethodSymbol method, SyntaxNode s return BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: method, arguments: ImmutableArray.Create(argument)); } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs index 8c7680dc0482a..ccb2c046a763f 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/PlaceholderLocalSymbol.cs @@ -166,6 +166,7 @@ internal static BoundExpression ConvertToLocalType(CSharpCompilation compilation expr = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: conversionMethod, arg0: temp); } diff --git a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ReturnValueLocalSymbol.cs b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ReturnValueLocalSymbol.cs index 02b5c8b1ac788..e58a04ff4bd9b 100644 --- a/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ReturnValueLocalSymbol.cs +++ b/src/ExpressionEvaluator/CSharp/Source/ExpressionCompiler/Symbols/ReturnValueLocalSymbol.cs @@ -35,6 +35,7 @@ internal override BoundExpression RewriteLocal(CSharpCompilation compilation, Sy var call = BoundCall.Synthesized( syntax, receiverOpt: null, + initialBindingReceiverIsSubjectToCloning: ThreeState.Unknown, method: method, arguments: ImmutableArray.Create(argument)); return ConvertToLocalType(compilation, call, this.Type, diagnostics);