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
public void Dispose() public void Dispose()
{ {
Console.WriteLine("MutValueType disposed on {0}", val); Console.WriteLine("MutValueType disposed on {0}", val);
val = val + 1;
} }
public override string ToString() public override string ToString()
@ -67,6 +68,10 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
gvt.Call(ref gvt); gvt.Call(ref gvt);
new ValueTypeCall().InstanceFieldTests(); new ValueTypeCall().InstanceFieldTests();
ForEach(); ForEach();
#if CS73
DisposeMultipleTimes(ref m, in m);
ToStringGeneric(ref m, in m);
#endif
} }
static void RefParameter(ref MutValueType m) static void RefParameter(ref MutValueType m)
@ -213,5 +218,37 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
} }
Console.WriteLine("after: " + list[0].val); 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
} }
[Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")] [Obsolete("Renamed to ScopedRef. This property will be removed in a future version of the decompiler.")]
[Browsable(false)]
public bool LifetimeAnnotations { public bool LifetimeAnnotations {
get { return ScopedRef; } get { return ScopedRef; }
set { ScopedRef = value; } set { ScopedRef = value; }

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

@ -301,7 +301,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Thus, we have to ensure we're operating on an r-value. // 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 // 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). // 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)) if (options.HasFlag(InliningOptions.Aggressive))
{ {
@ -321,7 +321,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
case ExpressionClassification.ReadonlyLValue: case ExpressionClassification.ReadonlyLValue:
// For struct method calls on readonly lvalues, the C# compiler // For struct method calls on readonly lvalues, the C# compiler
// only generates a temporary if it isn't a "readonly struct" // only generates a temporary if it isn't a "readonly struct"
return MethodRequiresCopyForReadonlyLValue(method); return MethodRequiresCopyForReadonlyLValue(method, constrainedTo);
default: default:
throw new InvalidOperationException("invalid expression classification"); throw new InvalidOperationException("invalid expression classification");
} }
@ -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) if (method == null)
return true; return true;
var type = method.DeclaringType; var type = constrainedTo ?? method.DeclaringType;
if (type.IsReferenceType == true) if (type.IsReferenceType == true)
return false; // reference types are never implicitly copied return false; // reference types are never implicitly copied
if (method.ThisIsRefReadOnly) if (method.ThisIsRefReadOnly)
@ -351,12 +351,13 @@ namespace ICSharpCode.Decompiler.IL.Transforms
internal static bool IsUsedAsThisPointerInCall(LdLoca ldloca) 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; method = null;
constrainedType = null;
if (ldloca.Variable.Type.IsReferenceType ?? false) if (ldloca.Variable.Type.IsReferenceType ?? false)
return false; return false;
ILInstruction inst = ldloca; ILInstruction inst = ldloca;
@ -370,7 +371,9 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{ {
case OpCode.Call: case OpCode.Call:
case OpCode.CallVirt: case OpCode.CallVirt:
method = ((CallInstruction)inst.Parent).Method; var callInst = (CallInstruction)inst.Parent;
method = callInst.Method;
constrainedType = callInst.ConstrainedTo;
if (method.IsAccessor) if (method.IsAccessor)
{ {
if (method.AccessorKind == MethodSemanticsAttributes.Getter) if (method.AccessorKind == MethodSemanticsAttributes.Getter)

Loading…
Cancel
Save