Pass the compilation to ILInstruction.CheckInvariant

Invariants that involve types (stack type of a variable against its IType,
the element type of an array access, the operand types of a comparison)
need a type system to resolve them against, and the only correct one is the
type system the instruction tree was decoded with. Until now CheckInvariant
took only the phase, so such a check had no compilation to use:
DeconstructInstruction.CheckInvariant called IsAssignment with a null type
system, which only held up because the targets it sees are ldloc, whose
InferType never touches the compilation; a ldflda-wrapped or pointer target
would have failed inside the invariant instead of reporting a violation.

Every call site already has that type system in scope: the ILReader's
compilation, the ILTransformContext of the running transform, or the
decompiler's own IDecompilerTypeSystem. It is now passed explicitly and the
base implementation asserts it is present, so a future invariant can rely
on it without re-plumbing the callers.

Assisted-by: Claude:claude-fable-5:Claude Code
This commit is contained in:
Siegfried Pammer
2026-08-30 12:43:40 +02:00
parent 19e702cc70
commit cd212ea05f
33 changed files with 129 additions and 103 deletions
@@ -2389,7 +2389,7 @@ namespace ICSharpCode.Decompiler.CSharp
return;
}
function = ilReader.ReadIL((MethodDefinitionHandle)method.MetadataToken, methodBody, cancellationToken: CancellationToken);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, typeSystem);
AddAnnotationsToDeclaration(method, entityDecl, function, parameterOffset);
@@ -143,9 +143,9 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
}
InlineBodyOfMoveNext(function);
function.CheckInvariant(ILPhase.InAsyncAwait);
function.CheckInvariant(ILPhase.InAsyncAwait, context.TypeSystem);
CleanUpBodyOfMoveNext(function);
function.CheckInvariant(ILPhase.InAsyncAwait);
function.CheckInvariant(ILPhase.InAsyncAwait, context.TypeSystem);
AnalyzeStateMachine(function);
DetectAwaitPattern(function);
@@ -189,7 +189,7 @@ namespace ICSharpCode.Decompiler.IL.ControlFlow
// Note: because this only deletes blocks outright, the 'stateChanges' entries remain valid
// (though some may point to now-deleted blocks)
newBody.SortBlocks(deleteUnreachableBlocks: true);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
try
{
+1 -1
View File
@@ -543,7 +543,7 @@ namespace ICSharpCode.Decompiler.IL
var inst = decodedInstruction.Instruction;
if (inst.ResultType == StackType.Unknown && inst.OpCode != OpCode.InvalidBranch && inst.OpCode != OpCode.InvalidExpression)
Warn("Unknown result type (might be due to invalid IL or missing references)");
inst.CheckInvariant(ILPhase.InILReader);
inst.CheckInvariant(ILPhase.InILReader, compilation);
int end = reader.Offset;
inst.AddILRange(new Interval(start, end));
if (!decodedInstruction.PushedOnExpressionStack)
+38 -38
View File
@@ -1038,9 +1038,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as PinnedRegion;
return o != null && variable == o.variable && this.init.PerformMatch(o.init, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
DebugAssert(Variable.Kind == VariableKind.PinnedRegionLocal);
@@ -1940,9 +1940,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LockInstruction;
return o != null && this.onExpression.PerformMatch(o.onExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(onExpression.ResultType == StackType.O);
}
}
@@ -2084,9 +2084,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as UsingInstruction;
return o != null && variable == o.variable && this.resourceExpression.PerformMatch(o.resourceExpression, ref match) && this.body.PerformMatch(o.body, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
DebugAssert(resourceExpression.ResultType == StackType.O);
@@ -2363,9 +2363,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLoc;
return o != null && variable == o.variable;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
}
@@ -2437,9 +2437,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLoca;
return o != null && variable == o.variable;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
}
@@ -3567,9 +3567,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Cpblk;
return o != null && this.destAddress.PerformMatch(o.destAddress, ref match) && this.sourceAddress.PerformMatch(o.sourceAddress, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(destAddress.ResultType == StackType.I || destAddress.ResultType == StackType.Ref);
DebugAssert(sourceAddress.ResultType == StackType.I || sourceAddress.ResultType == StackType.Ref);
DebugAssert(size.ResultType == StackType.I4);
@@ -3718,9 +3718,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as Initblk;
return o != null && this.address.PerformMatch(o.address, ref match) && this.value.PerformMatch(o.value, ref match) && this.size.PerformMatch(o.size, ref match) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(address.ResultType == StackType.I || address.ResultType == StackType.Ref);
DebugAssert(value.ResultType == StackType.I4);
DebugAssert(size.ResultType == StackType.I4);
@@ -4081,9 +4081,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObj;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
}
@@ -4191,9 +4191,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdObjIfRef;
return o != null && this.target.PerformMatch(o.target, ref match) && type.Equals(o.type);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
}
}
@@ -4330,9 +4330,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StObj;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match) && type.Equals(o.type) && IsVolatile == o.IsVolatile && UnalignedPrefix == o.UnalignedPrefix;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.Ref || target.ResultType == StackType.I);
DebugAssert(value.ResultType == type.GetStackType());
CheckTargetSlot();
@@ -4856,9 +4856,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as LdLen;
return o != null && this.array.PerformMatch(o.array, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(array.ResultType == StackType.O);
}
}
@@ -5218,9 +5218,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as GetPinnableReference;
return o != null && this.argument.PerformMatch(o.argument, ref match) && object.Equals(method, o.method);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@@ -5307,9 +5307,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as StringToInt;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@@ -5831,9 +5831,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicConvertInstruction;
return o != null && type.Equals(o.type) && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@@ -5919,9 +5919,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicGetMemberInstruction;
return o != null && this.target.PerformMatch(o.target, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.O);
}
}
@@ -6024,9 +6024,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicSetMemberInstruction;
return o != null && this.target.PerformMatch(o.target, ref match) && this.value.PerformMatch(o.value, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(target.ResultType == StackType.O);
}
}
@@ -6467,9 +6467,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DynamicIsEventInstruction;
return o != null && this.argument.PerformMatch(o.argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(argument.ResultType == StackType.O);
}
}
@@ -6608,9 +6608,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as MatchInstruction;
return o != null && variable == o.variable && object.Equals(method, o.method) && this.IsDeconstructCall == o.IsDeconstructCall && this.IsDeconstructTuple == o.IsDeconstructTuple && this.CheckType == o.CheckType && this.CheckNotNull == o.CheckNotNull && this.testedOperand.PerformMatch(o.testedOperand, ref match) && Patterns.ListMatch.DoMatch(this.SubPatterns, o.SubPatterns, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
DebugAssert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
DebugAssert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
AdditionalInvariants();
@@ -6984,9 +6984,9 @@ namespace ICSharpCode.Decompiler.IL
var o = other as DeconstructResultInstruction;
return o != null && this.Argument.PerformMatch(o.Argument, ref match);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
AdditionalInvariants();
}
}
+2 -2
View File
@@ -455,9 +455,9 @@ namespace <#=opCode.Namespace#>
}
<# } #>
<# if (opCode.Invariants.Count > 0) { #>
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
<# foreach (var invariant in opCode.Invariants) {#>
<#=invariant#>
<# } #>
@@ -129,9 +129,9 @@ namespace ICSharpCode.Decompiler.IL
get => IsLifted ? StackType.O : resultType;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (!IsLifted)
{
Debug.Assert(LeftInputType == Left.ResultType);
@@ -103,9 +103,9 @@ namespace ICSharpCode.Decompiler.IL
return clone;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
for (int i = 0; i < Instructions.Count - 1; i++)
{
// only the last instruction may have an unreachable endpoint
@@ -358,14 +358,14 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public void RunTransforms(IEnumerable<IBlockTransform> transforms, BlockTransformContext context)
{
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
foreach (var transform in transforms)
{
context.CancellationToken.ThrowIfCancellationRequested();
Debug.Assert(context.IndexOfFirstAlreadyTransformedInstruction <= this.Instructions.Count);
context.StepStartGroup(transform.GetType().Name);
transform.Run(this, context);
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
context.StepEndGroup();
}
}
@@ -24,6 +24,7 @@ using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
{
@@ -191,9 +192,9 @@ namespace ICSharpCode.Decompiler.IL
return BlockSlot;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Blocks.Count > 0 && EntryPoint == Blocks[0]);
Debug.Assert(!IsConnected || EntryPoint.IncomingEdgeCount >= 1);
Debug.Assert(Parent is ILFunction || !ILRangeIsEmpty);
@@ -20,6 +20,8 @@
using System;
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@@ -108,9 +110,9 @@ namespace ICSharpCode.Decompiler.IL
return false;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (phase > ILPhase.InILReader)
{
Debug.Assert(targetBlock?.Parent is BlockContainer);
@@ -68,9 +68,9 @@ namespace ICSharpCode.Decompiler.IL
public override StackType ResultType => FunctionPointerType.ReturnType.GetStackType();
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Arguments.Count == FunctionPointerType.ParameterTypes.Length + (IsInstance ? 1 : 0));
}
@@ -121,9 +121,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
int firstArgument = (OpCode != OpCode.NewObj && !Method.IsStatic) ? 1 : 0;
Debug.Assert(Method.Parameters.Count + firstArgument == Arguments.Count);
if (firstArgument == 1)
@@ -164,9 +164,9 @@ namespace ICSharpCode.Decompiler.IL
public bool IsLifted => LiftingKind != ComparisonLiftingKind.None;
public StackType UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (LiftingKind == ComparisonLiftingKind.None)
{
Debug.Assert(Left.ResultType == InputType);
@@ -79,9 +79,9 @@ namespace ICSharpCode.Decompiler.IL
CheckValidTarget();
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
CheckValidTarget();
}
@@ -169,9 +169,9 @@ namespace ICSharpCode.Decompiler.IL
this.IsLifted = isLifted;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
// Debug.Assert(Kind != ConversionKind.Invalid); // invalid conversion can happen with invalid IL/missing references
Debug.Assert(Argument.ResultType == (IsLifted ? StackType.O : InputType));
Debug.Assert(!(IsLifted && Kind == ConversionKind.StopGCTracking));
@@ -296,9 +296,9 @@ namespace ICSharpCode.Decompiler.IL
return null;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
var patternVariables = new HashSet<ILVariable>();
var conversionVariables = new HashSet<ILVariable>();
@@ -323,7 +323,7 @@ namespace ICSharpCode.Decompiler.IL
foreach (var inst in assignments.Instructions)
{
if (!(IsAssignment(inst, typeSystem: null, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
if (!(IsAssignment(inst, compilation, out _, out var value) && value.MatchLdLoc(out var inputVariable)))
throw new InvalidOperationException("inst is not an assignment!");
Debug.Assert(patternVariables.Contains(inputVariable) || conversionVariables.Contains(inputVariable));
}
@@ -230,7 +230,7 @@ namespace ICSharpCode.Decompiler.IL
this.kind = kind;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
switch (kind)
{
@@ -263,7 +263,7 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(Variables[i].IndexInFunction == i);
Variables[i].CheckInvariant();
}
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
}
void CloneVariables()
@@ -401,7 +401,7 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
public void RunTransforms(IEnumerable<IILTransform> transforms, ILTransformContext context)
{
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
bool traceTransforms = DecompilerEventSource.Log.IsTransformTracingEnabled();
foreach (var transform in transforms)
{
@@ -421,7 +421,7 @@ namespace ICSharpCode.Decompiler.IL
transform.Run(this, context);
if (traceTransforms)
DecompilerEventSource.Log.ILTransformExecuted(transform, this, traceStart);
this.CheckInvariant(ILPhase.Normal);
this.CheckInvariant(ILPhase.Normal, context.TypeSystem);
context.StepEndGroup(keepIfEmpty: true);
}
}
@@ -81,9 +81,16 @@ namespace ICSharpCode.Decompiler.IL
Debug.Assert(b, msg);
}
/// <summary>
/// Verifies the invariants of this instruction and its descendants. Only active in debug builds.
/// </summary>
/// <param name="phase">Which set of invariants applies; they tighten as the pipeline progresses.</param>
/// <param name="compilation">The compilation the instruction tree was decoded against, so that
/// invariants involving types can be resolved against the same type system the decompiler uses.</param>
[Conditional("DEBUG")]
internal virtual void CheckInvariant(ILPhase phase)
internal virtual void CheckInvariant(ILPhase phase, ICompilation compilation)
{
Debug.Assert(compilation != null);
foreach (var child in Children)
{
Debug.Assert(child.Parent == this);
@@ -92,7 +99,7 @@ namespace ICSharpCode.Decompiler.IL
// exception: nested ILFunctions (lambdas)
Debug.Assert(this is ILFunction || child.flags != invalidFlags || this.flags == invalidFlags);
Debug.Assert(child.IsConnected == this.IsConnected);
child.CheckInvariant(phase);
child.CheckInvariant(phase, compilation);
}
Debug.Assert((this.DirectFlags & ~this.Flags) == 0, "All DirectFlags must also appear in this.Flags");
}
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>If statement / conditional expression. <c>if (condition) trueExpr else falseExpr</c></summary>
@@ -53,9 +55,9 @@ namespace ICSharpCode.Decompiler.IL
return new IfInstruction(lhs, new LdcI4(1), rhs);
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(condition.ResultType == StackType.I4);
Debug.Assert(trueInst.ResultType == falseInst.ResultType
|| trueInst.HasDirectFlag(InstructionFlags.EndPointUnreachable)
@@ -19,13 +19,15 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
public sealed partial class LdFlda
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
switch (field.DeclaringType.IsReferenceType)
{
case true:
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@@ -106,9 +108,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(targetContainer!));
Debug.Assert(phase <= ILPhase.InILReader || phase == ILPhase.InAsyncAwait || value.ResultType == targetContainer!.ResultType);
}
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
// Note: The comp instruction also supports three-valued logic via ComparisonLiftingKind.ThreeValuedLogic.
@@ -29,9 +31,9 @@ namespace ICSharpCode.Decompiler.IL
bool ILiftableInstruction.IsLifted => true;
StackType ILiftableInstruction.UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O);
}
}
@@ -41,9 +43,9 @@ namespace ICSharpCode.Decompiler.IL
bool ILiftableInstruction.IsLifted => true;
StackType ILiftableInstruction.UnderlyingResultType => StackType.I4;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O);
}
}
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
/// <summary>
@@ -63,9 +65,9 @@ namespace ICSharpCode.Decompiler.IL
this.FallbackInst = fallbackInst;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(valueInst.ResultType == StackType.O); // lhs is reference type or nullable type
Debug.Assert(fallbackInst.ResultType == StackType.O || Kind == NullCoalescingKind.NullableWithValueFallback);
Debug.Assert(ResultType == UnderlyingResultType || Kind == NullCoalescingKind.Nullable);
@@ -21,6 +21,7 @@ using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.IL.Transforms;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
@@ -76,9 +77,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
if (this.RefInput)
{
Debug.Assert(Argument.ResultType == StackType.Ref, "nullable.unwrap expects reference to nullable type as input");
@@ -108,9 +109,9 @@ namespace ICSharpCode.Decompiler.IL
partial class NullableRewrap
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Argument.HasFlag(InstructionFlags.MayUnwrapNull));
}
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class StLoc
@@ -36,9 +38,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
internal bool ILStackWasEmpty;
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(phase <= ILPhase.InILReader || this.IsDescendantOf(variable.Function!));
Debug.Assert(phase <= ILPhase.InILReader || variable.Function!.Variables[variable.IndexInFunction] == variable);
Debug.Assert(value.ResultType == variable.StackType);
@@ -152,9 +152,9 @@ namespace ICSharpCode.Decompiler.IL
this.resultType = resultType;
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
bool expectNullSection = this.IsLifted;
LongSet sets = LongSet.Empty;
foreach (var section in Sections)
@@ -21,6 +21,7 @@ using System;
using System.Diagnostics;
using System.Linq;
using ICSharpCode.Decompiler.TypeSystem;
using ICSharpCode.Decompiler.Util;
namespace ICSharpCode.Decompiler.IL
@@ -140,9 +141,9 @@ namespace ICSharpCode.Decompiler.IL
/// </summary>
partial class TryCatchHandler
{
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(Parent is TryCatch);
Debug.Assert(filter.ResultType == StackType.I4);
Debug.Assert(this.IsDescendantOf(variable.Function!));
@@ -19,6 +19,8 @@
using System.Diagnostics;
using ICSharpCode.Decompiler.TypeSystem;
namespace ICSharpCode.Decompiler.IL
{
partial class BitNot : ILiftableInstruction
@@ -43,9 +45,9 @@ namespace ICSharpCode.Decompiler.IL
}
}
internal override void CheckInvariant(ILPhase phase)
internal override void CheckInvariant(ILPhase phase, ICompilation compilation)
{
base.CheckInvariant(phase);
base.CheckInvariant(phase, compilation);
Debug.Assert(IsLifted == (ResultType == StackType.O));
Debug.Assert(IsLifted || ResultType == UnderlyingResultType);
}
@@ -199,7 +199,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
// Embed the lambda into the parent function's ILAst, so that "Show steps" can show
// how the lambda body is being transformed.
value.ReplaceWith(function);
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
var contextPrefix = targetMethod.Name;
foreach (ILVariable v in function.Variables.Where(v => v.Kind != VariableKind.Parameter))
@@ -28,7 +28,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
/// <summary>
/// If possible, transforms plain ILAst loops into while (condition), do-while and for-loops.
/// For the invariants of the transforms <see cref="BlockContainer.CheckInvariant(ILPhase)"/>.
/// For the invariants of the transforms <see cref="BlockContainer.CheckInvariant(ILPhase, TypeSystem.ICompilation)"/>.
/// </summary>
public class HighLevelLoopTransform : IILTransform
{
@@ -487,7 +487,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
if (hasBody)
{
function.DeclarationScope = (BlockContainer)rootFunction.Body;
function.CheckInvariant(ILPhase.Normal);
function.CheckInvariant(ILPhase.Normal, context.TypeSystem);
var nestedContext = new ILTransformContext(context, function);
function.RunTransforms(CSharpDecompiler.GetILTransforms().TakeWhile(t => !(t is LocalFunctionDecompiler)), nestedContext);
function.DeclarationScope = null;
@@ -139,7 +139,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms
{
transform.Run(block, pos, ctx);
#if DEBUG
block.Instructions[pos].CheckInvariant(ILPhase.Normal);
block.Instructions[pos].CheckInvariant(ILPhase.Normal, context.TypeSystem);
for (int i = Math.Max(0, pos - 100); i < pos; ++i)
{
if (block.Instructions[i].IsDirty)
+1 -1
View File
@@ -751,7 +751,7 @@ matching is cheap. (See also <span class="filecite">doc/ILAst Pattern Matching.m
<h3>5.6 Invariants</h3>
<p><code>CheckInvariant(ILPhase)</code> verifies parent/child consistency, flag correctness, and
<p><code>CheckInvariant(ILPhase, ICompilation)</code> verifies parent/child consistency, flag correctness, and
connectedness. The phase parameter exists because invariants tighten over time: in
<code>ILPhase.InILReader</code>, branches may still point at offsets; from
<code>ILPhase.Normal</code> on, the full rules apply. <code>ILFunction.RunTransforms</code> checks the