-
Notifications
You must be signed in to change notification settings - Fork 4k
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
Support delegate or function pointer creation based on a static abstract method. #52685
Support delegate or function pointer creation based on a static abstract method. #52685
Conversation
@333fred, @RikkiGibson, @dotnet/roslyn-compiler Please review |
@@ -1830,7 +1830,7 @@ internal static bool MayUseCallForStructMethod(MethodSymbol method) | |||
{ | |||
Debug.Assert(method.ContainingType.IsVerifierValue(), "this is not a value type"); | |||
|
|||
if (!method.IsMetadataVirtual()) | |||
if (!method.IsMetadataVirtual() || method.IsStatic) |
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.
To make sure I understand the change: it looks like this lets us use 'call' instruction when calling a method that implements a static abstract method. Is that correct? #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.
To make sure I understand the change: it looks like this lets us use 'call' instruction when calling a method that implements a static abstract method. Is that correct?
At the moment, this chnage is not observable in any way, but static methods can be invoked only by 'call' instruction.
receiverOpt = (BoundExpression?)this.Visit(receiverOpt); | ||
} | ||
else | ||
{ |
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 looks like this is necessary because this.Visit
is unable to handle a BoundTypeExpression
. Is that right? #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.
It looks like this is necessary because this.Visit is unable to handle a BoundTypeExpression. Is that right?
In genera it can do that. However, since the first pass specially visits BoundCall, we have to mirror that here as well in order to make sure that the count of nodes is in sync across passes.
@@ -379,7 +379,9 @@ private static bool IsFloatingPointExpressionOfUnknownPrecision(BoundExpression | |||
{ | |||
var mg = (BoundMethodGroup)rewrittenOperand; | |||
Debug.Assert(oldNodeOpt.SymbolOpt is { }); | |||
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, type: funcPtrType, hasErrors: false); | |||
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, |
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 made it to lowering with a static abstract
method group without a type parameter receiver, does that mean we failed to give a diagnostic during binding? #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.
If we made it to lowering with a static abstract method group without a type parameter receiver, does that mean we failed to give a diagnostic during binding?
Pretty much.
// (30,31): error CS0176: Member 'I1.M04()' cannot be accessed with an instance reference; qualify it with a type name instead | ||
// _ = new System.Action(x.M04); | ||
Diagnostic(ErrorCode.ERR_ObjectProhibited, "x.M04").WithArguments("I1.M04()").WithLocation(30, 31), | ||
// (35,31): error CS0119: 'T' is a type parameter, which is not valid in the given context |
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.
not for this PR, but I feel like we should look at changing this diagnostic message to indicate that the reason T
can't be used as a receiver is because M03
is not static abstract
.
@333fred, @dotnet/roslyn-compiler For the second review |
@@ -379,7 +379,9 @@ private static bool IsFloatingPointExpressionOfUnknownPrecision(BoundExpression | |||
{ | |||
var mg = (BoundMethodGroup)rewrittenOperand; | |||
Debug.Assert(oldNodeOpt.SymbolOpt is { }); | |||
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, type: funcPtrType, hasErrors: false); | |||
return new BoundFunctionPointerLoad(oldNodeOpt.Syntax, oldNodeOpt.SymbolOpt, | |||
constrainedToTypeOpt: oldNodeOpt.SymbolOpt.IsStatic && oldNodeOpt.SymbolOpt.IsAbstract ? mg.ReceiverOpt?.Type : null, |
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.
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.
Should we just dereference this, or give some kind of an error to handle the case? If we have a static abstract, we need to have a type or further layers are going to crash, right?
I prefer to not surface this as a NullReference exception
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.
An exception in the emit layer is sufficient to deal with this unexpected situation
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 prefer to not surface this as a NullReference exception
That's fine, but we're seriously busted at this point, right? Should we consider throwing an unreachable or something?
In reply to: 616239428 [](ancestors = 616239428)
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's fine, but we're seriously busted at this point, right? Should we consider throwing an unreachable or something?
An emit layer takes care of this
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.
An emit layer takes care of this
I would say consider at least leaving a comment. Explicitly "dealing" with the potential null by doing a conditional access here feels to me like an expression of intent, but I don't read the intent as "let the emit layer throw". I read it as "this should have some meaning", which is not what is meant.
In reply to: 616240220 [](ancestors = 616240220)
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.
My intent is to not perform any validation here. Null reference is not the only thing we should worry about. The code matches the intent.
@" | ||
public interface I1 | ||
{ | ||
abstract static void M01(); |
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.
Doesn't need to be in this PR, but we likely need to have some tests around UnmanagedCallersOnly
. #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.
Doesn't need to be in this PR, but we likely need to have some tests around UnmanagedCallersOnly.
What would be unique about this scenario by comparison to consumprion of a regular static method?
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 would be unique about this scenario by comparison to consumprion of a regular static method?
We don't support UnmanagedCallersOnly
on instance methods at all, nor do we support taking a pointer to instance methods. We probably shouldn't support the attribute on abstract statics either, or we should make sure that users are forced to apply the attribute to implementations of an abstract static member in a way that matches up.
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.
We don't support UnmanagedCallersOnly on instance methods at all, nor do we support taking a pointer to instance methods. We probably shouldn't support them on abstract statics either, or we should make sure that users are forced to apply the attribute to implementations of an abstract static member in a way that matches up.
Recorded in #52221
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 (commit 1). Consider leaving the suggested comment to make intent clearer.
No description provided.