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

Make SourceClonedParameterSymbol abstract #54355

Conversation

Youssef1313
Copy link
Member

@Youssef1313 Youssef1313 commented Jun 24, 2021

Builds on #54132 (I'll rebase once the other PR goes in). I think there is no need to wait on the other PR. Going to rebase on the current feature branch.

@AlekseyTs Let me know if you want the other PR to contain everything.

Proposal: dotnet/csharplang#287
Test plan: #52745

@Youssef1313 Youssef1313 marked this pull request as ready for review June 25, 2021 13:59
@Youssef1313 Youssef1313 requested a review from a team as a code owner June 25, 2021 13:59
@Youssef1313
Copy link
Member Author

@AlekseyTs This is ready for review. Thanks!

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 1)


internal override bool IsCallerMemberName => _originalParam.IsCallerMemberName;

internal override int CallerArgumentExpressionParameterIndex => throw ExceptionUtilities.Unreachable;
Copy link
Member Author

@Youssef1313 Youssef1313 Jun 25, 2021

Choose a reason for hiding this comment

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

@AlekseyTs The created parameter may not be optional as you stated in the other PR. However, I think this is still unreachable. We attempt to bind default arguments only if overload resolution succeeds.

If overload resolution succeeds, then an explicit value was passed to the parameter, meaning we are not going to access this property (I'm open to adding any test cases you'd like if the existing tests are not enough). #Resolved

Copy link
Contributor

Choose a reason for hiding this comment

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

If overload resolution succeeds, then an explicit value was passed to the parameter, meaning we are not going to access this property.

That maybe true for EndEnvoke (I assume because parameters are ref/out/in), but why this is true for BeginInvoke is not obvious to me.

I'm open to adding any test cases you'd like if the existing tests are not enough

I think we should have targeted test for Begin/EndInvoke invocations for scenarios when cloned parameters are optional and have the attribute on them.

Copy link
Member Author

Choose a reason for hiding this comment

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

Both BeginInvoke and EndInvoke has extra parameter(s) at the end (AsyncCallBack and object for BeginInvoke, and IAsyncResult for EndInvoke). If overload resolution succeeds, then these parameters must have been supplied, meaning all the previous parameters has explicitly been passed.

I'll add some tests.

Copy link
Member Author

Choose a reason for hiding this comment

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

Both BeginInvoke and EndInvoke has extra parameter(s) at the end (AsyncCallBack and object for BeginInvoke, and IAsyncResult for EndInvoke). If overload resolution succeeds, then these parameters must have been supplied, meaning all the previous parameters has explicitly been passed.

I see now why this is wrong.

@Youssef1313
Copy link
Member Author

@AlekseyTs This is ready for another look. Thanks!

@@ -17,6 +17,142 @@ namespace Microsoft.CodeAnalysis.CSharp.UnitTests
{
public class AttributeTests_CallerInfoAttributes : WellKnownAttributesTestBase
{
[ConditionalFact(typeof(CoreClrOnly))]
public void TestBeginInvoke()
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 28, 2021

Choose a reason for hiding this comment

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

TestBeginInvoke

Do we have similar tests for EndInvoke? Even though they are likely going to be error scenarios, we want to make sure that binding doesn't fail in an unexpected way (a crash/assert, etc. ) #Closed

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 2)

@jcouv jcouv added the Community The pull request was submitted by a contributor who is not a Microsoft employee. label Jun 29, 2021
class Program
{
const string s1 = nameof(s1);
delegate void D(ref string s1, [CallerArgumentExpression(s1)] [Optional] [DefaultParameterValue(""default"")] string s2);
Copy link
Contributor

@AlekseyTs AlekseyTs Jun 29, 2021

Choose a reason for hiding this comment

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

[CallerArgumentExpression(s1)] [Optional] [DefaultParameterValue(""default"")] string s2

The interesting scenario is when this parameter is getting cloned and the one that it references isn't and probably follows it. Is this the case in this unit-test. We want to test scenario where simply reusing the index from the original parameter will be observably wrong. #Closed

Copy link
Member Author

@Youssef1313 Youssef1313 Jun 30, 2021

Choose a reason for hiding this comment

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

The interesting scenario is when this parameter is getting cloned and the one that it references isn't

For the parameter to get cloned, it must be ref if I understand correctly. ref parameters seem to have a false IsOptional. Hence, the test won't hit calling CallerArgumentExpressionIndex. This is regardless we're in error case or not.

I think the test you're asking for should hit CallerArgumentExpressionIndex. I'm unable to find a test case that fulfills all the following:

  1. The CallerArgumentExpression parameter is cloned.
  2. The parameter it refers to isn't cloned.
  3. CallerArgumentExpressionIndex gets hit.

I added a test that fulfills 1 and 2.

Copy link
Contributor

@AlekseyTs AlekseyTs Jun 30, 2021

Choose a reason for hiding this comment

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

For the parameter to get cloned, it must be ref if I understand correctly. ref parameters seem to have a false IsOptional. Hence, the test won't hit calling CallerArgumentExpressionIndex. This is regardless we're in error case or not.

We still can have a test attempting to do that. Trying all possible ways marking the parameter optional, attempting to ommit it at the call-site. It is fine if we will be unable to hit the API. We still need tests that attempt to do that.

Copy link
Member Author

Choose a reason for hiding this comment

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

@AlekseyTs I added a test in 25dd0ff, and added two more now in 8ee88e0. Let me know if there are any other scenarios :)

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 6)

class Program
{
const string s2 = nameof(s2);
delegate void D([CallerArgumentExpression(s2)] [Optional] [DefaultParameterValue(""default"")] ref string s1, string s2);
Copy link
Contributor

@AlekseyTs AlekseyTs Jul 1, 2021

Choose a reason for hiding this comment

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

s2

Let's add a couple of more parameters to the end and change this arttribute to refer to the last parameter. The goal is to make the correspnding index to go out range of ordinals of parameters in EndInvoke. #Closed

{
D d = M;
string s = string.Empty;
d.EndInvoke(ref s, null);
Copy link
Contributor

@AlekseyTs AlekseyTs Jul 1, 2021

Choose a reason for hiding this comment

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

d.EndInvoke(ref s, null);

Are we omitting the argument for the "interesting" parameter (parameter that was clned from the one with CallerArgumentExpression attribute)? #Resolved

{
D d = M;
string s = string.Empty;
d.EndInvoke(ref s, null);
Copy link
Contributor

@AlekseyTs AlekseyTs Jul 1, 2021

Choose a reason for hiding this comment

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

d.EndInvoke(ref s, null);

Are we omitting the argument for the "interesting" parameter (parameter that was clned from the one with CallerArgumentExpression attribute)? #Resolved

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 10)

@Youssef1313
Copy link
Member Author

.tools\msbuild\16.8.0-preview2.1\tools\Common7\IDE\CommonExtensions\Microsoft\NuGet\NuGet.RestoreEx.targets(10,5): error : (NETCORE_ENGINEERING_TELEMETRY=Restore) Failed to download package 'Microsoft.VisualStudio.Shell.Interop.14.0.DesignTime.16.10.31225.38' from 'https://pkgs.dev.azure.com/azure-public/3ccf6661-f8ce-4e8a-bb2e-eff943ddd3c7/_packaging/2a239fd0-3e21-40b0-b9d6-bc122fec7eb2/nuget/v3/flat2/microsoft.visualstudio.shell.interop.14.0.designtime/16.10.31225.38/microsoft.visualstudio.shell.interop.14.0.designtime.16.10.31225.38.nupkg'.

@Youssef1313 Youssef1313 closed this Jul 3, 2021
@Youssef1313 Youssef1313 reopened this Jul 3, 2021
@Youssef1313
Copy link
Member Author

Pinging @AlekseyTs and @333fred for review. Thanks!

";

var compilation = CreateCompilation(source, targetFramework: TargetFramework.NetCoreApp, options: TestOptions.ReleaseExe, parseOptions: TestOptions.RegularPreview);
// Begin/EndInvoke are not currently supported.
Copy link
Member

Choose a reason for hiding this comment

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

supported

Do you mean at runtime? Supported where?

Copy link
Member Author

Choose a reason for hiding this comment

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

@333fred No, I meant "supported by the compiler". ie, the compiler doesn't do anything for caller argument expressions for cloned begin/end invoke parameters.

@333fred
Copy link
Member

333fred commented Jul 7, 2021

Done review pass (commit 11). Only a few small comments for clarifications.

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 13). @dotnet/roslyn-compiler for a second review.

@Youssef1313
Copy link
Member Author

@jcouv Can you review please? Thanks!

@Youssef1313
Copy link
Member Author

Ping @jcouv

@jcouv jcouv self-assigned this Jul 13, 2021
{
}

internal override bool IsCallerFilePath => _originalParam.IsCallerFilePath;
Copy link
Member

Choose a reason for hiding this comment

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

I didn't understand why properties that are implemented the same way in SourceDelegateClonedParameterSymbolForBeginAndEndInvoke and SourcePropertyClonedParameterSymbolForAccessors were moved out of the base type.

Copy link
Member Author

Choose a reason for hiding this comment

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

@jcouv This was requested by @AlekseyTs. The reasoning is to force new types to implement it. i.e, avoid future mistakes if a new type should implement it another way.

IL_001e: ret
}
");
}
Copy link
Member

Choose a reason for hiding this comment

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

It looks like the added tests cover delegate scenarios. Do we already have scenarios for properties/indexers, since that's another scenario that uses SourceClonedParameterSymbol?

Copy link
Member Author

Choose a reason for hiding this comment

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

@jcouv There is a test for indexer:

Let me know if you want to extend the tests with any scenarios.

Copy link
Member

Choose a reason for hiding this comment

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

Awesome, thanks

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.

Done with review pass (iteration 13). Just a couple of questions

@jcouv
Copy link
Member

jcouv commented Jul 13, 2021

Also it occurred to me that we'll want to think of possible interactions between CallerArgumentExpression and interpolated string handlers.

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 13)

@jcouv jcouv merged commit b688438 into dotnet:features/caller-argument-expression Jul 13, 2021
@Youssef1313 Youssef1313 deleted the caller-arg-cloned-param branch July 13, 2021 20:38
@jcouv
Copy link
Member

jcouv commented Jul 13, 2021

possible interactions between CallerArgumentExpression and interpolated string handlers.

Clarified from chat with Fred. Here's possible scenario (may just fall out):
What if I put CallerArgumentExpression on a parameter of handler constructor, then use the argument attribute to copy an argument from invocation and pass it to that constructor?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers Community The pull request was submitted by a contributor who is not a Microsoft employee. Feature - CallerArgumentExpressionAttribute
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants