Skip to content

Commit

Permalink
Allow inlining value type temporaries into constrained call.
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Jul 22, 2023
1 parent 60f614f commit 99d5e94
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public void Increment()
public void Dispose()
{
Console.WriteLine("MutValueType disposed on {0}", val);
val = val + 1;
}

public override string ToString()
Expand Down Expand Up @@ -67,6 +68,10 @@ public static void Main()
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)
Expand Down Expand Up @@ -213,5 +218,37 @@ static void ForEachArray1(MutValueType[] list)
}
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 change: 1 addition & 0 deletions ICSharpCode.Decompiler/DecompilerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,7 @@ public bool ScopedRef {
}

[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; }
Expand Down
17 changes: 10 additions & 7 deletions ICSharpCode.Decompiler/IL/Transforms/ILInlining.cs
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstr
// 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))
{
Expand All @@ -321,7 +321,7 @@ static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstr
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");
}
Expand All @@ -337,11 +337,11 @@ static bool IsGeneratedValueTypeTemporary(LdLoca loadInst, ILVariable v, ILInstr
}
}

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)
Expand All @@ -351,12 +351,13 @@ internal static bool MethodRequiresCopyForReadonlyLValue(IMethod method)

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;
Expand All @@ -370,7 +371,9 @@ static bool IsUsedAsThisPointerInCall(LdLoca ldloca, out IMethod method)
{
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)
Expand Down

0 comments on commit 99d5e94

Please sign in to comment.