Skip to content

Commit

Permalink
Add flag if receiver needs cloning
Browse files Browse the repository at this point in the history
  • Loading branch information
jjonescz committed Apr 17, 2023
1 parent 8aaf595 commit 2227aaf
Show file tree
Hide file tree
Showing 24 changed files with 163 additions and 68 deletions.
71 changes: 54 additions & 17 deletions src/Compilers/CSharp/Portable/Binder/Binder.ValueChecks.cs

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Attributes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -560,7 +560,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, receiverCloned: false, propertySymbol, resultKind, namedArgumentType);
}
else
{
Expand Down
4 changes: 3 additions & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Expressions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4340,6 +4340,7 @@ private BoundExpression BindConstructorInitializerCore(
isDelegateCall: false,
expanded,
invokedAsExtensionMethod: false,
receiverCloned: ReceiverCloned(receiver, resultMember),
argsToParamsOpt: argsToParamsOpt,
defaultArguments: defaultArguments,
resultKind: LookupResultKind.Viable,
Expand Down Expand Up @@ -7602,7 +7603,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, receiverCloned: ReceiverCloned(receiver, propertySymbol), propertySymbol, lookupResult, propertySymbol.Type, hasErrors: (hasErrors || hasError));
}
#nullable disable

Expand Down Expand Up @@ -8502,6 +8503,7 @@ private BoundExpression BindIndexerOrIndexedPropertyAccess(
propertyAccess = new BoundIndexerAccess(
syntax,
receiver,
receiverCloned: ReceiverCloned(receiver, property),
property,
arguments,
argumentNames,
Expand Down
17 changes: 17 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Invocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1141,11 +1141,28 @@ private BoundCall BindInvocationExpressionContinued(

return new BoundCall(node, receiver, method, args, argNames, argRefKinds, isDelegateCall: isDelegateCall,
expanded: expanded, invokedAsExtensionMethod: invokedAsExtensionMethod,
receiverCloned: ReceiverCloned(receiver, method),
argsToParamsOpt: argsToParams, defaultArguments, resultKind: LookupResultKind.Viable, type: returnType, hasErrors: gotError);
}

#nullable enable

internal bool ReceiverCloned(BoundExpression? receiver, PropertySymbol property)
=> ReceiverCloned(receiver, property.GetMethod ?? property.SetMethod);

internal bool ReceiverCloned(BoundExpression? receiver, MethodSymbol method)
{
if (receiver is BoundValuePlaceholderBase || receiver?.Type?.IsValueType != true)
{
return false;
}

var valueKind = method.IsEffectivelyReadOnly
? BindValueKind.RefersToLocation
: BindValueKind.RefersToLocation | BindValueKind.Assignable;
return !CheckValueKind(receiver.Syntax, receiver, valueKind, checkingReceiver: true, BindingDiagnosticBag.Discarded);
}

private static SourceLocation GetCallerLocation(SyntaxNode syntax)
{
var token = syntax switch
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Patterns.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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, receiverCloned: false, lengthProperty, LookupResultKind.Viable, lengthProperty.Type) { WasCompilerGenerated = true };
}
else
{
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/Binder/Binder_Query.cs
Original file line number Diff line number Diff line change
Expand Up @@ -326,7 +326,7 @@ private static BoundCall ReverseLastTwoParameterOrder(BoundCall result)

return result.Update(
result.ReceiverOpt, result.Method, arguments.ToImmutableAndFree(), argumentNamesOpt: default,
argumentRefKindsOpt: default, result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod,
argumentRefKindsOpt: default, result.IsDelegateCall, result.Expanded, result.InvokedAsExtensionMethod, result.ReceiverCloned,
argsToParams.ToImmutableAndFree(), defaultArguments, result.ResultKind, result.OriginalMethodsOpt, result.Type);
}

Expand Down
2 changes: 2 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/Binder_Statements.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3866,6 +3866,7 @@ private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode cont
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
receiverCloned: false,
argsToParamsOpt: ImmutableArray<int>.Empty,
defaultArguments: BitVector.Empty,
resultKind: resultKind,
Expand Down Expand Up @@ -3912,6 +3913,7 @@ private static SyntaxToken GetImplicitConstructorBodyToken(CSharpSyntaxNode cont
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
receiverCloned: false,
argsToParamsOpt: default,
defaultArguments: default,
resultKind: LookupResultKind.Viable,
Expand Down
5 changes: 5 additions & 0 deletions src/Compilers/CSharp/Portable/Binder/RefSafetyAnalysis.cs
Original file line number Diff line number Diff line change
Expand Up @@ -674,6 +674,7 @@ private void VisitArgumentsAndGetArgumentPlaceholders(BoundExpression? receiverO
node.Syntax,
method,
node.ReceiverOpt,
node.ReceiverCloned,
method.Parameters,
node.Arguments,
node.ArgumentRefKindsOpt,
Expand Down Expand Up @@ -774,6 +775,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase
node.Syntax,
constructor,
receiverOpt: null,
receiverCloned: false,
constructor.Parameters,
node.Arguments,
node.ArgumentRefKindsOpt,
Expand All @@ -795,6 +797,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase
node.Syntax,
indexer,
node.ReceiverOpt,
receiverCloned: false, // PROTOTYPE
indexer.Parameters,
node.Arguments,
node.ArgumentRefKindsOpt,
Expand All @@ -817,6 +820,7 @@ private void VisitObjectCreationExpressionBase(BoundObjectCreationExpressionBase
node.Syntax,
method,
receiverOpt: null,
receiverCloned: false,
method.Parameters,
node.Arguments,
node.ArgumentRefKindsOpt,
Expand Down Expand Up @@ -917,6 +921,7 @@ private void VisitDeconstructionArguments(ArrayBuilder<DeconstructionVariable> v
syntax,
deconstructMethod,
invocation.ReceiverOpt,
invocation.ReceiverCloned,
parameters,
invocation.Arguments,
invocation.ArgumentRefKindsOpt,
Expand Down
6 changes: 6 additions & 0 deletions src/Compilers/CSharp/Portable/BoundTree/BoundNodes.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1791,6 +1791,8 @@
<Field Name="IsDelegateCall" Type="bool"/>
<Field Name="Expanded" Type="bool"/>
<Field Name="InvokedAsExtensionMethod" Type="bool"/>
<!-- Whether receiver will need to be cloned during emit (only valid before lowering) -->
<Field Name="ReceiverCloned" Type="bool"/>
<Field Name="ArgsToParamsOpt" Type="ImmutableArray&lt;int&gt;" Null="allow"/>
<Field Name="DefaultArguments" Type="BitVector" />
<Field Name="ResultKind" PropertyOverrides="true" Type="LookupResultKind"/>
Expand Down Expand Up @@ -2106,6 +2108,8 @@
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>

<Field Name="ReceiverOpt" Type="BoundExpression?"/>
<!-- Whether receiver will need to be cloned during emit (only valid before lowering) -->
<Field Name="ReceiverCloned" Type="bool"/>
<Field Name="PropertySymbol" Type="PropertySymbol"/>
<Field Name="ResultKind" PropertyOverrides="true" Type="LookupResultKind"/>
</Node>
Expand All @@ -2125,6 +2129,8 @@
<Field Name="Type" Type="TypeSymbol" Override="true" Null="disallow"/>

<Field Name="ReceiverOpt" Type="BoundExpression?"/>
<!-- Whether receiver will need to be cloned during emit (only valid before lowering) -->
<Field Name="ReceiverCloned" Type="bool"/>
<Field Name="Indexer" Type="PropertySymbol"/>
<Field Name="Arguments" Type="ImmutableArray&lt;BoundExpression&gt;"/>
<Field Name="ArgumentNamesOpt" Type="ImmutableArray&lt;string&gt;" Null="allow"/>
Expand Down
19 changes: 13 additions & 6 deletions src/Compilers/CSharp/Portable/BoundTree/Constructors.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,12 +91,13 @@ public BoundCall(
bool isDelegateCall,
bool expanded,
bool invokedAsExtensionMethod,
bool receiverCloned,
ImmutableArray<int> argsToParamsOpt,
BitVector defaultArguments,
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, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, receiverCloned, argsToParamsOpt, defaultArguments, resultKind, originalMethodsOpt: default, type, hasErrors)
{
}

Expand All @@ -108,11 +109,12 @@ public BoundCall Update(BoundExpression? receiverOpt,
bool isDelegateCall,
bool expanded,
bool invokedAsExtensionMethod,
bool receiverCloned,
ImmutableArray<int> argsToParamsOpt,
BitVector defaultArguments,
LookupResultKind resultKind,
TypeSymbol type)
=> Update(receiverOpt, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, argsToParamsOpt, defaultArguments, resultKind, this.OriginalMethodsOpt, type);
=> Update(receiverOpt, method, arguments, argumentNamesOpt, argumentRefKindsOpt, isDelegateCall, expanded, invokedAsExtensionMethod, receiverCloned, argsToParamsOpt, defaultArguments, resultKind, this.OriginalMethodsOpt, type);

public static BoundCall ErrorCall(
SyntaxNode node,
Expand Down Expand Up @@ -142,6 +144,7 @@ public static BoundCall ErrorCall(
isDelegateCall: isDelegateCall,
expanded: false,
invokedAsExtensionMethod: invokedAsExtensionMethod,
receiverCloned: false,
argsToParamsOpt: default(ImmutableArray<int>),
defaultArguments: default(BitVector),
resultKind: resultKind,
Expand All @@ -152,12 +155,12 @@ public static BoundCall ErrorCall(

public BoundCall Update(ImmutableArray<BoundExpression> arguments)
{
return this.Update(ReceiverOpt, Method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type);
return this.Update(ReceiverOpt, Method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ReceiverCloned, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type);
}

public BoundCall Update(BoundExpression? receiverOpt, MethodSymbol method, ImmutableArray<BoundExpression> arguments)
{
return this.Update(receiverOpt, method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type);
return this.Update(receiverOpt, method, arguments, ArgumentNamesOpt, ArgumentRefKindsOpt, IsDelegateCall, Expanded, InvokedAsExtensionMethod, ReceiverCloned, ArgsToParamsOpt, DefaultArguments, ResultKind, OriginalMethodsOpt, Type);
}

public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiverOpt, MethodSymbol method)
Expand Down Expand Up @@ -186,6 +189,7 @@ public static BoundCall Synthesized(SyntaxNode syntax, BoundExpression? receiver
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
receiverCloned: false,
argsToParamsOpt: default(ImmutableArray<int>),
defaultArguments: default(BitVector),
resultKind: LookupResultKind.Viable,
Expand Down Expand Up @@ -223,6 +227,7 @@ public static BoundIndexerAccess ErrorAccess(
return new BoundIndexerAccess(
node,
receiverOpt,
receiverCloned: false,
indexer,
arguments,
namedArguments,
Expand All @@ -237,6 +242,7 @@ public static BoundIndexerAccess ErrorAccess(
public BoundIndexerAccess(
SyntaxNode syntax,
BoundExpression? receiverOpt,
bool receiverCloned,
PropertySymbol indexer,
ImmutableArray<BoundExpression> arguments,
ImmutableArray<string> argumentNamesOpt,
Expand All @@ -246,10 +252,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, receiverCloned, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, originalIndexersOpt: default, type, hasErrors)
{ }

public BoundIndexerAccess Update(BoundExpression? receiverOpt,
bool receiverCloned,
PropertySymbol indexer,
ImmutableArray<BoundExpression> arguments,
ImmutableArray<string> argumentNamesOpt,
Expand All @@ -258,7 +265,7 @@ public BoundIndexerAccess Update(BoundExpression? receiverOpt,
ImmutableArray<int> argsToParamsOpt,
BitVector defaultArguments,
TypeSymbol type)
=> Update(receiverOpt, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, this.OriginalIndexersOpt, type);
=> Update(receiverOpt, receiverCloned, indexer, arguments, argumentNamesOpt, argumentRefKindsOpt, expanded, argsToParamsOpt, defaultArguments, this.OriginalIndexersOpt, type);
}

internal sealed partial class BoundConversion
Expand Down
2 changes: 1 addition & 1 deletion src/Compilers/CSharp/Portable/CodeGen/Optimizer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2156,7 +2156,7 @@ public override BoundNode VisitCall(BoundCall node)

ImmutableArray<BoundExpression> 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.Method, arguments, node.ArgumentNamesOpt, node.ArgumentRefKindsOpt, node.IsDelegateCall, node.Expanded, node.InvokedAsExtensionMethod, node.ReceiverCloned, node.ArgsToParamsOpt, node.DefaultArguments, node.ResultKind, node.OriginalMethodsOpt, type);
}
#nullable disable

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ internal static ImmutableArray<BoundStatement> ConstructScriptConstructorBody(
isDelegateCall: false,
expanded: false,
invokedAsExtensionMethod: false,
receiverCloned: false,
argsToParamsOpt: ImmutableArray<int>.Empty,
defaultArguments: BitVector.Empty,
resultKind: LookupResultKind.Viable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9042,7 +9042,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, receiverCloned: false, 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);
Expand Down
Loading

0 comments on commit 2227aaf

Please sign in to comment.