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

Always use a by-value temp to capture a reference type receiver #73631

Merged
merged 7 commits into from
Jun 3, 2024

Conversation

jcouv
Copy link
Member

@jcouv jcouv commented May 22, 2024

Relates to #73501

Baseline for test InterpolatedStringHandlerArgumentAttribute_ThisParameter_ReferenceTypeReferenceReceiver is added in commit 1 and updated in commit 2.
The main change in that test is V_3 is now of type C instead of type C&, and it doesn't need to be dereferenced with ldind.ref when that variable is used, just before the invocation of CustomHandler..ctor(int, int, C). Instead it happens during the initialization of the variable.

Fixes #73667

@jcouv jcouv self-assigned this May 22, 2024
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels May 22, 2024
@jcouv jcouv marked this pull request as ready for review May 23, 2024 06:34
@jcouv jcouv requested a review from a team as a code owner May 23, 2024 06:34
@jcouv jcouv requested a review from AlekseyTs May 23, 2024 06:34
C c = new C(5);
ref C c2 = ref c;
C otherC = null;
c2.M({{expression}}, c2 = ref otherC);
Copy link
Contributor

@AlekseyTs AlekseyTs May 23, 2024

Choose a reason for hiding this comment

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

ref otherC

I do not think this scenario is interesting. I.e. redirecting c2 to a different location is not interesting because the old implementation was capturing the reference and changing that reference couldn't have any effect afterwards. What would be interesting is a change to a value that the reference points to, c2 = otherC. After making this change to the test scenarios in this unit-test and the unit-test below, we will be able to observe that the implementation change is not merely an optimization, but actually a fix for a real problem. Scenario from InterpolatedStringHandlerArgumentAttribute_ThisParameter_ReferenceTypeReferenceReceiver_ReverseOrder is going to crash at runtime without the implementation change and with the implementation change it is going to exhibit expected behavior. Therefore, I suggest opening an issue with a "broken" repro, adjusting description of this PR to say that we are attempting to fix that issue, and adjusting the added unit-tests accordingly. #Closed

Copy link
Contributor

Choose a reason for hiding this comment

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

I would even suggest to simplify the scenario by using c2 = default and getting rid of otherC local

Copy link
Contributor

Choose a reason for hiding this comment

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

Also, please add tests for generic scenarios like:

            var code = $$"""
using System;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()
    {
        C c = new C(5);
        Test(ref c);
    }

    static void Test<T>(ref T c2) where T : I1
    {
        c2.M(c2 = default, {{expression}});
    }
}

public interface I1
{
    int Prop { get; }
    void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c);
}

public class C : I1
{
    public int Prop { get; }
    public C(int i) => Prop = i;
    public void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c)
    {
        Console.WriteLine(cNull is null);
        Console.WriteLine(c.ToString());
    }
}

public partial struct CustomHandler
{
    public CustomHandler(int literalLength, int formattedCount, I1 c) : this(literalLength, formattedCount)
    {
        _builder.AppendLine("c.Prop:" + c.Prop.ToString());
    }
}
""";

and

            var code = $$"""
using System;
using System.Runtime.CompilerServices;

class Program
{
    static void Main()
    {
        C c = new C(5);
        Test(ref c);
    }

    static void Test<T>(ref T c2) where T : class, I1
    {
        c2.M(c2 = default, {{expression}});
    }
}

public interface I1
{
    int Prop { get; }
    void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c);
}

public class C : I1
{
    public int Prop { get; }
    public C(int i) => Prop = i;
    public void M(I1 cNull, [InterpolatedStringHandlerArgumentAttribute("")] CustomHandler c)
    {
        Console.WriteLine(cNull is null);
        Console.WriteLine(c.ToString());
    }
}

public partial struct CustomHandler
{
    public CustomHandler(int literalLength, int formattedCount, I1 c) : this(literalLength, formattedCount)
    {
        _builder.AppendLine("c.Prop:" + c.Prop.ToString());
    }
}
""";

@AlekseyTs
Copy link
Contributor

Done with review pass (commit 2)

@jcouv jcouv requested review from AlekseyTs and jjonescz May 23, 2024 19:04
Copy link
Contributor

@AlekseyTs AlekseyTs 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 4)

@jcouv
Copy link
Member Author

jcouv commented May 23, 2024

@dotnet/roslyn-compiler for second review. Thanks

1 similar comment
@jcouv
Copy link
Member Author

jcouv commented May 30, 2024

@dotnet/roslyn-compiler for second review. Thanks

refKind = rewrittenReceiver.GetRefKind();

if (refKind == RefKind.None &&
!rewrittenReceiver.Type.IsReferenceType &&
Copy link
Member

Choose a reason for hiding this comment

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

!rewrittenReceiver.Type.IsReferenceType &&

Is this always true now?

""");
}

[Theory]
Copy link
Member

@cston cston May 30, 2024

Choose a reason for hiding this comment

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

[Theory]

Consider adding [WorkItem] attribute to this test and the type parameter test that hit the bug. #Resolved

_factory.CurrentFunction,
peVerifyCompatEnabled: false,
stackLocalsOpt: null))
if (rewrittenReceiver.Type.IsReferenceType)
Copy link
Member Author

@jcouv jcouv May 30, 2024

Choose a reason for hiding this comment

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

Copying comment from extensions/receiver PR in case it is useful:

            // If we need to capture a receiver (for compound assignment, null-coalescing assignment, extension receiver adjustment, interpolation `this` capture, ...)
            // we need to make sure the capturing temp preserves the semantics of the receiver.
            //
            // For instance:
            //   Struct s = ...;
            //   s.Method(s.field = 42)
            // must be rewritten as:
            //   ref Struct temp = ref s;
            //   temp.Method(s.field = 42)
            // so that Method can have and observe the proper side-effects on the struct.
            //
            // Similarly:
            //   Class c = ...;
            //   ref Class c2 = ref c;
            //   Class otherC = null;
            //   c2.Method(c2 = ref otherC)
            // must be rewritten as:
            //   Class temp = c2; // dereferences to c
            //   temp.Method(c2 = ref otherC)
``` #Resolved

""");
}

[Theory]
Copy link
Member

@cston cston May 30, 2024

Choose a reason for hiding this comment

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

[Theory]

Consider adding [WorkItem] attribute to this test. #Resolved

Copy link
Member

@cston cston left a comment

Choose a reason for hiding this comment

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

LGTM. Minor suggestions only.

@@ -18539,5 +18537,381 @@ public struct A
// public A(int x, A a = M($"{1}")) { }
Diagnostic(ErrorCode.ERR_BadArgRef, @"$""{1}""").WithArguments("1", "ref").WithLocation(8, 29));
}

[Theory, WorkItem("https://github.com/dotnet/roslyn/pull/73631")]
Copy link
Member

@cston cston May 30, 2024

Choose a reason for hiding this comment

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

WorkItem("#73631")

Should the link be "issues/73667" instead? #Resolved

@jcouv jcouv merged commit c2a1ed2 into dotnet:main Jun 3, 2024
24 checks passed
@jcouv jcouv deleted the receiver-temp branch June 3, 2024 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area-Compilers 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.

Incorrect receiver capture with ref C receiver and interpolation handler
4 participants