Skip to content
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

Conversation

AlekseyTs
Copy link
Contributor

No description provided.

@AlekseyTs
Copy link
Contributor Author

@333fred, @RikkiGibson, @dotnet/roslyn-compiler Please review

@RikkiGibson RikkiGibson self-assigned this Apr 19, 2021
@@ -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)
Copy link
Contributor

@RikkiGibson RikkiGibson Apr 19, 2021

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

Copy link
Contributor Author

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
{
Copy link
Contributor

@RikkiGibson RikkiGibson Apr 19, 2021

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

Copy link
Contributor Author

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,
Copy link
Contributor

@RikkiGibson RikkiGibson Apr 19, 2021

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

Copy link
Contributor Author

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
Copy link
Contributor

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.

@AlekseyTs
Copy link
Contributor Author

@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,
Copy link
Member

@333fred 333fred Apr 19, 2021

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? #Resolved

Copy link
Contributor Author

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

Copy link
Contributor Author

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

Copy link
Member

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)

Copy link
Contributor Author

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

Copy link
Member

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)

Copy link
Contributor Author

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();
Copy link
Member

@333fred 333fred Apr 19, 2021

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

Copy link
Contributor Author

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?

Copy link
Member

@333fred 333fred Apr 20, 2021

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.

Copy link
Contributor Author

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

Copy link
Member

@333fred 333fred left a 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.

@AlekseyTs AlekseyTs merged commit 18cd442 into dotnet:features/StaticAbstractMembersInInterfaces Apr 20, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants