Browse Source

Allow inlining value type temporaries into constrained call.

pull/3041/head
Daniel Grunwald 2 years ago
parent
commit
99d5e94a62
  1. 37
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs
  2. 1
      ICSharpCode.Decompiler/DecompilerSettings.cs
  3. 17
      ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

37
ICSharpCode.Decompiler.Tests/TestCases/Correctness/ValueTypeCall.cs

@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -16,6 +16,7 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
public void Dispose()
{
Console.WriteLine("MutValueType disposed on {0}", val);
val = val + 1;
}
public override string ToString()
@ -67,6 +68,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -67,6 +68,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
gvt.Call(ref gvt);
new ValueTypeCall().InstanceFieldTests();
ForEach();
#if CS73
DisposeMultipleTimes(ref m, in m);
ToStringGeneric(ref m, in m);
#endif
}
static void RefParameter(ref MutValueType m)
@ -213,5 +218,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -213,5 +218,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
}
Console.WriteLine("after: " + list[0].val);
}
#if CS73
static void DisposeMultipleTimes<T>(ref T mutRef, in T immutableRef) where T : struct, IDisposable
{
Console.WriteLine("DisposeMultipleTimes:");
mutRef.Dispose();
mutRef.Dispose();
T copyFromMut = mutRef;
copyFromMut.Dispose();
immutableRef.Dispose();
immutableRef.Dispose();
T copyFromImmutable = immutableRef;
copyFromImmutable.Dispose();
mutRef.Dispose();
immutableRef.Dispose();
}
static void ToStringGeneric<T>(ref T mutRef, in T immutableRef) where T : struct
{
Console.WriteLine("ToStringGeneric:");
mutRef.ToString();
mutRef.ToString();
T copyFromMut = mutRef;
copyFromMut.ToString();
immutableRef.ToString();
immutableRef.ToString();
T copyFromImmutable = immutableRef;
copyFromImmutable.ToString();
mutRef.ToString();
immutableRef.ToString();
}
#endif
}
}

1
ICSharpCode.Decompiler/DecompilerSettings.cs

@ -378,6 +378,7 @@ namespace ICSharpCode.Decompiler @@ -378,6 +378,7 @@ namespace ICSharpCode.Decompiler
}
[Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")]
[Browsable(false)]
public bool LifetimeAnnotations {
get { return ScopedRef; }
set { ScopedRef = value; }

17
ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs

@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Thus, we have to ensure we're operating on an r-value.
// Additionally, we cannot inline in cases where the C# compiler prohibits the direct use
// of the rvalue (e.g. M(ref (MyStruct)obj); is invalid).
if (IsUsedAsThisPointerInCall(loadInst, out var method))
if (IsUsedAsThisPointerInCall(loadInst, out var method, out var constrainedTo))
{
if (options.HasFlag(InliningOptions.Aggressive))
{
@ -321,7 +321,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -321,7 +321,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case ExpressionClassification.ReadonlyLValue:
// For struct method calls on readonly lvalues, the C# compiler
// only generates a temporary if it isn't a "readonly struct"
return MethodRequiresCopyForReadonlyLValue(method);
return MethodRequiresCopyForReadonlyLValue(method, constrainedTo);
default:
throw new InvalidOperationException("invalid expression classification");
}
@ -337,11 +337,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -337,11 +337,11 @@ namespace ICSharpCode.Decompiler.IL.Transforms
}
}
internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method)
internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method, IType constrainedTo = null)
{
if (method == null)
return true;
var type = method.DeclaringType;
var type = constrainedTo ?? method.DeclaringType;
if (type.IsReferenceType == true)
return false; // reference types are never implicitly copied
if (method.ThisIsRefReadOnly)
@ -351,12 +351,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -351,12 +351,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca)
{
return IsUsedAsThisPointerInCall(ldloca, out _);
return IsUsedAsThisPointerInCall(ldloca, out _, out _);
}
static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method, out IType constrainedType)
{
method = null;
constrainedType = null;
if (ldloca.Variable.Type.IsReferenceType ?? false)
return false;
ILInstruction inst = ldloca;
@ -370,7 +371,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms @@ -370,7 +371,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
case OpCode.Call:
case OpCode.CallVirt:
method = ((CallInstruction)inst.Parent).Method;
var callInst = (CallInstruction)inst.Parent;
method = callInst.Method;
constrainedType = callInst.ConstrainedTo;
if (method.IsAccessor)
{
if (method.AccessorKind == MethodSemanticsAttributes.Getter)

Loading…
Cancel
Save