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

Intercepting a struct rvalue instance method call with an extension method trips a debug assertion in the compiler #71657

Closed
RikkiGibson opened this issue Jan 16, 2024 · 1 comment · Fixed by #74509

Comments

@RikkiGibson
Copy link
Contributor

RikkiGibson commented Jan 16, 2024

We should resolve by either emitting an error at the interception (which feels unfortunate, but consistent with extension method calls), or by creating a temp during lowering.

Note that the shipped release-mode behavior here is to create a temp in the emit layer and do the call.
 

Debug.Assert(argument.Type.IsDynamic(), "passing args byref should not clone them into temps");

[Fact]
public void InterceptStructInstanceMethod_RvalueReceiver()
{
    // TODO2: this test crashes in debug mode due to assert in EmitExpression.cs:722.
    var source = ($$"""
        using System;

        struct Program
        {
            public void InterceptableMethod() => Console.Write("Original");

            public static void Main()
            {
                new Program().InterceptableMethod();
            }
        }
        """, "Program.cs");

    var interceptor = ("""
        using System.Runtime.CompilerServices;
        using System;

        static class D
        {
            [InterceptsLocation("Program.cs", 9, 23)]
            public static void Interceptor(this ref Program x) => Console.Write("Intercepted");
        }
        """, "Interceptor.cs");

    var verifier = CompileAndVerify(new[] { source, s_attributesSource }, parseOptions: RegularWithInterceptors);
    verifier.VerifyDiagnostics();
    verifier.VerifyIL("Program.Main", """
        {
            // Code size       15 (0xf)
            .maxstack  2
            .locals init (Program V_0)
            IL_0000:  ldloca.s   V_0
            IL_0002:  dup
            IL_0003:  initobj    "Program"
            IL_0009:  call       "void Program.InterceptableMethod()"
            IL_000e:  ret
        }
        """);

    verifier = CompileAndVerify(new[] { source, interceptor, s_attributesSource }, parseOptions: RegularWithInterceptors);
    verifier.VerifyDiagnostics();
    verifier.VerifyIL("Program.Main", """

        """);
}
@dotnet-issue-labeler dotnet-issue-labeler bot added Area-Compilers untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 16, 2024
@jaredpar jaredpar added Bug and removed untriaged Issues and PRs which have not yet been triaged by a lead labels Jan 18, 2024
@jaredpar jaredpar added this to the 17.10 milestone Jan 18, 2024
@jaredpar jaredpar modified the milestones: 17.10, 17.11 Mar 26, 2024
@jaredpar jaredpar modified the milestones: 17.11, 17.12 Apr 15, 2024
@RikkiGibson
Copy link
Contributor Author

We see a similar case with the following test:

    [Fact]
    public void ConditionalAccess_03()
    {
        // Conditional access on a nullable value type, original and interceptor use non-nullable value type.
        var source = CSharpTestSource.Parse("""
            struct S
            {
                void M() => throw null!;

                static void Main()
                {
                    S? s = null;
                    s?.M();
                }
            }
            """, "Program.cs", RegularWithInterceptors);

        var comp = CreateCompilation(source);
        var model = comp.GetSemanticModel(source);
        var node = source.GetRoot().DescendantNodes().OfType<InvocationExpressionSyntax>().Single();
        var locationSpecifier = model.GetInterceptableLocation(node)!;

        var interceptors = CSharpTestSource.Parse($$"""
            #nullable enable
            using System;

            static class Interceptors
            {
                {{locationSpecifier.GetInterceptsLocationAttributeSyntax()}}
                public static void M1(this ref S s) => Console.Write(1);
            }
            """, "Interceptors.cs", RegularWithInterceptors);

        var verifier = CompileAndVerify([source, interceptors, s_attributesTree], expectedOutput: "1");
        verifier.VerifyDiagnostics();
    }

Found during #72478

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment