It appears roslyn is able to determine when different methods would have identical IL bodies, and just emit one IL stream for the methods. This breaks the recursion detection done in the jit, which relies on the IL address.
Here after inlining A into B, the call to C looks like a recursive call, and so we block inlining:
using System;
class X
{
int A() => C();
int B() => A();
int C() => A();
}
Inlines into 06000002 [via ExtendedDefaultPolicy] X:B():int:this
[1 IL=0001 TR=000001 06000001] [below ALWAYS_INLINE size] X:A():int:this
[0 IL=0001 TR=000004 06000003] [FAILED: recursive] X:C():int:this
category:correctness
theme:inlining
skill-level:intermediate
cost:medium
impact:small
It appears roslyn is able to determine when different methods would have identical IL bodies, and just emit one IL stream for the methods. This breaks the recursion detection done in the jit, which relies on the IL address.
Here after inlining A into B, the call to C looks like a recursive call, and so we block inlining:
category:correctness
theme:inlining
skill-level:intermediate
cost:medium
impact:small