roslyn icon indicating copy to clipboard operation
roslyn copied to clipboard

Do not store temp of ref conditional operator by value

Open bernd5 opened this issue 1 year ago • 10 comments

Fixes #74115

bernd5 avatar Jun 23 '24 14:06 bernd5

It looks like there are no tests.

AlekseyTs avatar Jun 24 '24 15:06 AlekseyTs

Done with review pass (commit 2)

AlekseyTs avatar Jun 24 '24 15:06 AlekseyTs

@AlekseyTs do you have an idea for a test?

bernd5 avatar Jun 24 '24 22:06 bernd5

The scenarios in the issue being fixed should be included as tests, for starters. Additional tests depend on the nature of the product change, but it looks like that's not yet finalized, so it's too early to tell.

jcouv avatar Jun 24 '24 23:06 jcouv

Done with review pass (commit 5)

AlekseyTs avatar Jun 25 '24 13:06 AlekseyTs

@AlekseyTs can you write a test or help me to do so for the linked issue.

I explain what I would want to test:

compile and execute the following code:

string str = "a2";
int x = 1;
int y = 2;

ref int r =
      ref str is "whatever" ? ref x
    : ref str is { Length: >= 2 and <= 10 or 22 } ? ref y
    : ref System.Runtime.CompilerServices.Unsafe.NullRef<int>();

r++;
r++;
r++;
int xxx = r;
System.Console.WriteLine(xxx); //5
System.Console.WriteLine(x); //1
System.Console.WriteLine(y); //expected 5 - but we get 2

It should print (because r should be a reference to the local y):

5
1
5

currently it prints:

5
1
2

And it already works if no "Or Pattern" is used (without or 22)

The reason is because the alternative

ref str is { Length: >= 2 and <= 10 or 22 } ? ref y
    : ref System.Runtime.CompilerServices.Unsafe.NullRef<int>()

needs spilling and stores its result in a temporary by value.

bernd5 avatar Jun 25 '24 20:06 bernd5

For writing a test, take a look at TestRefConditionalDifferentTypes3. The broad strokes are CreateCompilation, CompileAndVerify (verifies execution among other things), VerifyDiagnostics and VerifyIL.

jcouv avatar Jun 25 '24 21:06 jcouv

I added the following test method:

        [Fact]
        public void TestRefConditionalWithSpilling()
        {
            var source = """
                class C
                {
                    public static void Main()
                    {
                        string str = "a2";
                        int x = 1;
                        int y = 2;
                        int z = 777;

                        ref int r =
                              ref str is "whatever" ? ref x
                            : ref str is { Length: >= 2 and <= 10 or 22 } ? ref y
                            : ref z;

                        r++;
                        r++;
                        r++;
                        int xxx = r;
                        System.Console.WriteLine(xxx); //5
                        System.Console.WriteLine(x); //1
                        System.Console.WriteLine(y); //expected 5 - but we get 2
                    }
                }
                """;

            var comp = CompileAndVerify(source,
                expectedOutput: """
                5
                1
                5
                """);
            comp.VerifyDiagnostics();
        }

But if I try to execute it, I get:

System.NullReferenceException : Object reference not set to an instance of an object.

C.Main() in :line 10
RuntimeMethodHandle.InvokeMethod(Object target, Void** arguments, Signature sig, Boolean isConstructor)
MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)

Do you know why?


I got it: it must be a ref-assignment...

bernd5 avatar Jun 26 '24 06:06 bernd5

@AlekseyTs can you review again?

bernd5 avatar Jun 26 '24 14:06 bernd5

Done with review pass (commit 11)

AlekseyTs avatar Jun 28 '24 18:06 AlekseyTs