-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add IOperation APIs for Dynamic invocations and indexer access #21711
Add IOperation APIs for Dynamic invocations and indexer access #21711
Conversation
"; | ||
string expectedOperationTree = @" | ||
IDynamicInvocationExpression (OperationKind.DynamicInvocationExpression, Type: dynamic) (Syntax: 'c.M2(d)') | ||
Expression: IOperation: (OperationKind.None) (Syntax: 'c.M2') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OperationKind.None
here seems to be due to a BoundMethodGroup
node in the tree - we seem to be losing the IOperation tree for the receiver here ('c' for this example). @heejaechang is going to bring it up for discussion in tomorrow's design meeting. #Pending
boundLateInvocation.ArgumentsOpt.SelectAsArray(Function(n) Create(n))) | ||
End Function) | ||
Dim argumentNames As ImmutableArray(Of String) = boundLateInvocation.ArgumentNamesOpt.NullToEmpty() | ||
Dim argumentRefKinds As ImmutableArray(Of RefKind) = ImmutableArray(Of RefKind).Empty |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BoundLateInvocation
node in VB doesn't seem to be storing the array of RefKinds for each argument. @AlekseyTs, is this expected or should I open a compiler bug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
BoundLateInvocation node in VB doesn't seem to be storing the array of RefKinds for each argument.
I believe ref kinds in C# are coming from syntax, VB doesn't have syntax like that. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That said, if we are using language-agnostic IOperation nodes, we should probably be able to distinguish situations when
- ref kinds are specified - C#, and
- ref kinds are not specified - VB #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs - We can do so by returning a default array for VB. Does that sound fine?
@dotnet retest ubuntu_14_debug_prtest please #Closed |
@@ -12,6 +12,11 @@ namespace Microsoft.CodeAnalysis.Semantics | |||
public interface IDynamicObjectCreationExpression : IHasDynamicArgumentsExpression | |||
{ | |||
/// <summary> | |||
/// Name of the dynamically invoked member. | |||
/// </summary> | |||
string MemberName { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
string MemberName { get; } [](start = 8, length = 26)
I am not sure if this property belongs in IDynamicObjectCreationExpression. Isn't the member is always an instance constructor, which has predefined name? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The doc comment is actually misleading, it should be name of the dynamically created type, and the field name should be "Name".
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it should be name of the dynamically created type
Why is the name of the type interesting? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, users can derive this name from syntax or the child dynamic member reference expression. Let me remove this property.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
users can derive this name from syntax or the child dynamic member reference expression.
I assume the type symbol is available as the Type property of the node itself. Correct? #Closed
/// This interface is reserved for implementation by its associated APIs. We reserve the right to | ||
/// change it in the future. | ||
/// </remarks> | ||
public interface IDynamicInvocationExpression : IHasDynamicArgumentsExpression |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IDynamicInvocationExpression [](start = 21, length = 28)
Should we expose information about type arguments? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Type arguments are stored neither on BoundDynamicInvocation
node in C# nor on BoundLateInvocation
node in VB. A dynamic invocation generally has the dynamic member reference as it's Expression child, which already exposes the type arguments.
Private Function CreateBoundLateInvocationOperation(boundLateInvocation As BoundLateInvocation) As IOperation | ||
Dim expression As Lazy(Of IOperation) = New Lazy(Of IOperation)(Function() Create(boundLateInvocation.Member)) | ||
|
||
Dim isDynamicPropertyReference = boundLateInvocation.MethodOrPropertyGroupOpt?.Kind = BoundKind.PropertyGroup |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
isDynamicPropertyReference [](start = 16, length = 26)
I believe It is not always possible to accurately calculate this value for VB. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@AlekseyTs do you have an example where this could likely be wrong? I was able to write unit tests where this value is correctly computed to true for property reference and false for method reference.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do you have an example where this could likely be wrong?
Aren't there cases when we simply don't know whether the name refers to a property, or to a method? #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I get your point, lets discuss the correct way to represent this in the next design meeting.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe that from VB point of view there is no (o very little) difference between late bound method invocation or late bound property indexing. At the same time, it looks like there is no such thing as dynamic property indexing on C# side. #Closed
Would it make sense to have distinct nodes for VB and C#? #Closed |
@AlekseyTs Are you suggesting this because of differences in the language specs? I am not very clear on why we need the distinction, but we can bring this up at the next design meeting. #Resolved |
Yes, because the difference in the languages. VB late binding is very different from C# dynamic. Semantics is different. #Closed |
1) Use IDynamicInvocationExpression for VB late bound invocation and C# dynamic invocation. 2) Use IDynamicIndexerAccessExpression for C# dynamic indexer access; not used in VB. 3) Remove IDynamicObjectCreationExpression.MemberName property. 4) Add extension methods on IHasDynamicArgumentExpression to get optional argument name and ref kind for an argument at a given index.
This PR has been updated as per last week's design decisions and is ready for review. #Closed |
Ping for reviews. #Closed |
{ | ||
Lazy<IOperation> expression = new Lazy<IOperation>(() => Create(boundDynamicInvocation.Expression)); | ||
ImmutableArray<ISymbol> applicableSymbols = StaticCast<ISymbol>.From(boundDynamicInvocation.ApplicableMethods); | ||
Lazy<ImmutableArray<IOperation>> arguments = new Lazy<ImmutableArray<IOperation>>(() => boundDynamicInvocation.Arguments.SelectAsArray(n => Create(n))); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider extracting a helper method for creating a Lazy<ImmutableArray<IOperation>>
from ImmutableArray<BoundExpression>
since there appear to be many instances of this. #Pending
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good idea, but there are large number of instances of this and I would prefer it to be a separate cleanup change, especially given we want to write an operation generator. I have added this suggestion to the issue tracking implementation of the operation generator: #20204 (comment) #Pending
var x = /*<bind>*/d[i]/*</bind>*/; | ||
} | ||
|
||
public int this[int i] => 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Property is not needed here nor in the test below.
{ | ||
var x = /*<bind>*/c.M2[i]/*</bind>*/; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this test different from the test above? #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch, I intended the parameter to be C c
not dynamic c
. I have fixed it now. #Resolved
|
||
static void M(dynamic d, C c) | ||
{ | ||
// This could be either of the one-parameter overloads so we allow it. | ||
c.Goo(d); | ||
|
||
// Doesn't constraints of generic overloads. | ||
c.Goo<short>(d, d); | ||
/*<bind>*/c.Goo<short>(d, d)/*</bind>*/; // Doesn't constraints of generic overloads. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What does the comment mean?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure - I have just retained the existing comment. I had to move it outside the /*<bind>*/
annotation as otherwise the test framework was unable to find the correct syntax node to fetch operation.
} | ||
|
||
var expression = (HasDynamicArgumentsExpression)dynamicExpression; | ||
if (expression.ArgumentNames.IsDefaultOrEmpty) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please extract a local for expression.ArgumentNames
. #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
} | ||
|
||
var expression = (HasDynamicArgumentsExpression)dynamicExpression; | ||
if (expression.ArgumentRefKinds.IsDefault) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please extract a local for expression.ArgumentRefKinds
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
if (index < 0 || index >= expression.ArgumentNames.Length) | ||
{ | ||
throw new ArgumentOutOfRangeException(nameof(index)); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is unnecessary, ArgumentNames[index]
will throw the same exception. Same comment for GetArgumentRefKind
. #Resolved
|
||
/// <summary> | ||
/// List of applicable symbols that are dynamically bound to the <see cref="Name"/>. | ||
/// List of applicable symbols that are dynamically bound. | ||
/// </summary> | ||
ImmutableArray<ISymbol> ApplicableSymbols { get; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ImmutableArray ApplicableSymbols { get; } [](start = 8, length = 50)
I believe we decided to not expose any applicable symbols. In any case, given the name of the interface, it feels like this property doesn't belong in it. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we remove the property, is there a value in having this interface at all?
In reply to: 137881777 [](ancestors = 137881777)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have removed the interface and moved the Arguments property to each IDynamicXXXExpression interface. #Closed
@AlekseyTs - addressed your feedback. One more round of review please. |
@dotnet-bot retest windows_debug_unit32_prtest please |
… expression for a bound method group that is a child of dynamic invocation
@mavasani Could you please confirm that build failures aren't caused by the changes in this PR? #Closed |
Thanks @AlekseyTs - some failures are irrelevant and some seem to be from tests not marked with IOperation compiler trait attribute. Let me fix this up. |
@dotnet-bot retest windows_debug_unit64_prtest please |
@dotnet-bot retest windows_debug_vs-integration_prtest please |
@dotnet-bot retest windows_release_vs-integration_prtest please |
@dotnet/roslyn-infrastructure All the VSI runs seems to be failing with the following:
Can someone please update the product key on the machines? |
/// </summary> | ||
/// <param name="dynamicExpression">Dynamic or late bound expression.</param> | ||
/// <param name="index">Argument index.</param> | ||
internal static string GetArgumentName(this HasDynamicArgumentsExpression dynamicExpression, int index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
int index [](start = 101, length = 9)
Index should be checked for range even when argumentNames.IsDefaultOrEmpty
. #Closed
return GetArgumentRefKind((HasDynamicArgumentsExpression)dynamicExpression, index); | ||
} | ||
|
||
internal static RefKind? GetArgumentRefKind(this HasDynamicArgumentsExpression dynamicExpression, int index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
index [](start = 110, length = 5)
Index should be checked for range even when argumentRefKinds.IsDefaultOrEmpty #Closed
"; | ||
string expectedOperationTree = @" | ||
IInvocationExpression ( ? A.B.()) (OperationKind.InvocationExpression, Type: ?, IsInvalid) (Syntax: 'M(d)') | ||
Instance Receiver: IOperation: (OperationKind.None, IsInvalid) (Syntax: 'M') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instance Receiver: IOperation: (OperationKind.None, IsInvalid) (Syntax: 'M') [](start = 2, length = 77)
Why are we getting None here? Please open an issue to follow up. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Filed #22121 #Resolved
Instance Receiver: IOperation: (OperationKind.None, IsInvalid) (Syntax: 'M') | ||
Arguments(1): | ||
IArgument (ArgumentKind.Explicit, Matching Parameter: null) (OperationKind.Argument, IsInvalid) (Syntax: 'd') | ||
ILocalReferenceExpression: d (OperationKind.LocalReferenceExpression, Type: dynamic, IsInvalid) (Syntax: 'd') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ILocalReferenceExpression: d (OperationKind.LocalReferenceExpression, Type: dynamic, IsInvalid) (Syntax: 'd') [](start = 8, length = 109)
I am not sure why this node is marked as invalid. Please open an issue to follow up. #Closed
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#22122 #Resolved
/// </summary> | ||
/// <param name="dynamicExpression">Dynamic or late bound expression.</param> | ||
/// <param name="index">Argument index.</param> | ||
public static string GetArgumentName(this IDynamicInvocationExpression dynamicExpression, int index) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GetArgumentName [](start = 29, length = 15)
For all new public methods, please add targeted tests to cover scenarios with an invalid input, i.e. when validation fails and we throw. #Closed
Done with review pass (iteration 10) #Closed |
@AlekseyTs - addressed your feedback. |
@dotnet-bot retest ubuntu_16_debug_prtest please |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Integration tests queues are failing due to an unrelated expired product key issue. |
Fixes #20114 and #20122