Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Enable basic end-to-end scenarios of extension method invocation. #77320

Merged
merged 6 commits into from
Feb 25, 2025

Conversation

AlekseyTs
Copy link
Contributor

No description provided.

@AlekseyTs AlekseyTs added Area-Compilers Feature - Extension Everything The extension everything feature labels Feb 23, 2025
@AlekseyTs AlekseyTs requested review from jjonescz and jcouv February 23, 2025 20:26
@AlekseyTs AlekseyTs requested a review from a team as a code owner February 23, 2025 20:26
@dotnet-issue-labeler dotnet-issue-labeler bot added the untriaged Issues and PRs which have not yet been triaged by a lead label Feb 23, 2025
@AlekseyTs
Copy link
Contributor Author

@jcouv, @jjonescz, @dotnet/roslyn-compiler Please review


return rewrittenCall;

BoundExpression visitArgumentsAndFinishRewrite(BoundCall node, BoundExpression? rewrittenReceiver)
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider passing rewriter explicitly and marking local function as static to avoid capture #Resolved

}
else
{
argumentRefKinds = argumentRefKinds.Insert(0, SyntheticBoundNodeFactory.ArgumentRefKindFromParameterRefKind(receiverRefKind, useStrictArgumentRefKinds: false));
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

false

Consider adding a test for this value #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.

Consider adding a test for this value

Testing for ref receivers is to be done, I added a couple of tests with prototype comment. I am not planning to test more in this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

BTW, the added tests break for true, but quite possibly they take the other branch that has the same logic.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the added tests take the other branch


return uncommon.LazyExtensionParameter.Value;

ParameterSymbol makeExtensionParameter()
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider making static to avoid captures, or limiting capture to this. Same for getMarkerMethodSymbol below #Resolved

{
foreach (var member in @this.ContainingType.GetMembers(SourceExtensionImplementationMethodSymbol.GetImplementationName(method)))
{
if (member is not MethodSymbol { HasSpecialName: true, IsStatic: true } candidate)
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check the accessibility of the implementation method (should match the skeleton method's)? #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 check the accessibility of the implementation method (should match the skeleton method's)?

I'll add a PROTOTYPE comment.


return uncommon.LazyImplementationMap.GetOrAdd(
method,
static (method, @this) =>
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: consider making this a local function #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.

consider making this a local function

What would be the motivation?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code with long embedded lambdas is not as readable. The last argument of GetOrAdd would not be lost after the long lambda

/// <summary>
/// The name of marker method for an extension type.
/// </summary>
internal const string ExtensionMarkerMethodName = "<Extension>$"; // PROTOTYPE confirm name with WG
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

// PROTOTYPE confirm name with WG

nit: we could remove the comment #Resolved

@jcouv jcouv self-assigned this Feb 24, 2025
Copy link
Member

@jcouv jcouv left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM Thanks (iteration 4)

// Code size 1 (0x1)
.maxstack 8
IL_0000: ret
} // end of method '<>E__0'::'<Extension>$'
Copy link
Member

@jcouv jcouv Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding a LangVer test (you can consume extension methods from metadata from any language version) #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.

Consider adding a LangVer test (you can consume extension methods from metadata from any language version)

  1. I think the behavior should be different, the suggested behavior goes against the spirit of the language version feature, I think.
  2. This level of testing is outside of scope of the PR. It is not about binding call-sites. While the language check should be performed for a call-site during binding.

@AlekseyTs
Copy link
Contributor Author

@jjonescz, @dotnet/roslyn-compiler For the second review

else
{
loweredBody = ExtensionMethodReferenceRewriter.Rewrite(loweredBody);
}
Copy link
Member

@jjonescz jjonescz Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add if (loweredBody.HasErrors) { return loweredBody; } after this "rewriting stage" similarly to the other stages around here? #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.

These visitors have no way of reporting errors and are not expected to produce bad nodes.

@@ -366,8 +377,174 @@ public override ExtendedSpecialType ExtendedSpecialType
}
}

// PROTOTYPE metadata is undone
internal sealed override ParameterSymbol ExtensionParameter => throw new NotImplementedException();
internal sealed override ParameterSymbol ExtensionParameter
Copy link
Member

@jjonescz jjonescz Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider wrapping this bulk of new code in #nullable enable #Resolved

}
}

public override MethodSymbol TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
Copy link
Member

@jjonescz jjonescz Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public override MethodSymbol TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
public sealed override MethodSymbol TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
``` #Resolved

@@ -84,5 +103,49 @@ internal sealed override ParameterSymbol? ExtensionParameter
}
}
}

public override MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
Copy link
Member

@jjonescz jjonescz Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public override MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
public sealed override MethodSymbol? TryGetCorrespondingExtensionImplementationMethod(MethodSymbol method)
``` #Resolved

/// <summary>
/// Replaces references to extension methods with references to their implementation methods
/// </summary>
internal class ExtensionMethodReferenceRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
Copy link
Member

@jjonescz jjonescz Feb 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
internal class ExtensionMethodReferenceRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
internal sealed class ExtensionMethodReferenceRewriter : BoundTreeRewriterWithStackGuardWithoutRecursionOnTheLeftOfBinaryOperator
``` #Resolved

@AlekseyTs AlekseyTs enabled auto-merge (squash) February 25, 2025 14:49
@AlekseyTs AlekseyTs merged commit 74d162a into dotnet:features/extensions Feb 25, 2025
25 of 28 checks passed
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
Area-Compilers Feature - Extension Everything The extension everything feature untriaged Issues and PRs which have not yet been triaged by a lead
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants