From c4e181505cb9d5e03acbcbd1dce9a78ddaf3e64d Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Sun, 30 Aug 2026 22:25:23 +0200 Subject: [PATCH] Prepare for a split of StackType.O into StackType.Obj and StackType.VT: the easy cases --- .../CSharp/ExpressionBuilder.cs | 6 +-- ICSharpCode.Decompiler/IL/ILReader.cs | 36 +++++++++-------- ICSharpCode.Decompiler/IL/ILTypeExtensions.cs | 3 +- ICSharpCode.Decompiler/IL/Instructions.cs | 20 +++++----- .../Instructions/BinaryNumericInstruction.cs | 2 +- .../IL/Instructions/CallInstruction.cs | 4 +- .../IL/Instructions/Comp.cs | 8 ++-- .../CompoundAssignmentInstruction.cs | 2 +- .../IL/Instructions/Conv.cs | 10 ++--- .../IL/Instructions/DynamicInstructions.cs | 18 ++++----- .../IL/Instructions/LdFlda.cs | 5 +-- .../IL/Instructions/LogicInstructions.cs | 8 ++-- .../IL/Instructions/NullableInstructions.cs | 2 +- .../IL/Instructions/SwitchInstruction.cs | 2 +- .../IL/Instructions/UnaryInstruction.cs | 2 +- .../IL/Transforms/ExpressionTransforms.cs | 4 +- .../IL/Transforms/PatternMatchingTransform.cs | 4 +- .../IL/Transforms/TransformExpressionTrees.cs | 2 +- .../TypeSystem/TypeUtils.cs | 39 +++++++------------ 19 files changed, 84 insertions(+), 93 deletions(-) diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 58546162b..deab86cdb 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1089,7 +1089,7 @@ namespace ICSharpCode.Decompiler.CSharp || !rr.Type.IsKnownType(KnownTypeCode.Boolean)) { IType targetType; - if (inst.InputType == StackType.O) + if (inst.InputType == StackType.Obj) { targetType = compilation.FindType(KnownTypeCode.Object); } @@ -1227,7 +1227,7 @@ namespace ICSharpCode.Decompiler.CSharp left = left.ConvertTo(inputType, this); right = right.ConvertTo(inputType, this); } - else if (inst.InputType == StackType.O) + else if (inst.InputType == StackType.Obj) { // Unsafe.As(ref left) op Unsafe.As(ref right) // TTo Unsafe.As(ref TFrom source) @@ -4313,7 +4313,7 @@ namespace ICSharpCode.Decompiler.CSharp } else { - Debug.Assert(inst.Value.ResultType == StackType.O); + Debug.Assert(inst.Value.ResultType == StackType.VT); Debug.Assert(inst.IsLifted); Debug.Assert(inst.Type == governingType); } diff --git a/ICSharpCode.Decompiler/IL/ILReader.cs b/ICSharpCode.Decompiler/IL/ILReader.cs index 365dffaed..b9986e30e 100644 --- a/ICSharpCode.Decompiler/IL/ILReader.cs +++ b/ICSharpCode.Decompiler/IL/ILReader.cs @@ -1122,7 +1122,7 @@ namespace ICSharpCode.Decompiler.IL case ILOpCode.Stind_i: return new StObj(value: Pop(StackType.I), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.IntPtr)); case ILOpCode.Stind_ref: - return new StObj(value: Pop(StackType.O), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Object)); + return new StObj(value: Pop(StackType.Obj), target: PopStObjTarget(), type: compilation.FindType(KnownTypeCode.Object)); case ILOpCode.Stloc: case ILOpCode.Stloc_s: return Stloc(ILParser.DecodeIndex(ref reader, opCode)); @@ -1150,7 +1150,7 @@ namespace ICSharpCode.Decompiler.IL return Push(new Box(Pop(type.GetStackType()), type)); } case ILOpCode.Castclass: - return Push(new CastClass(Pop(StackType.O), ReadAndDecodeTypeReference())); + return Push(new CastClass(Pop(StackType.Obj), ReadAndDecodeTypeReference())); case ILOpCode.Cpobj: { var type = ReadAndDecodeTypeReference(); @@ -1167,7 +1167,7 @@ namespace ICSharpCode.Decompiler.IL { FlushExpressionStack(); // value-type isinst has inlining restrictions } - return Push(new IsInst(Pop(StackType.O), type)); + return Push(new IsInst(Pop(StackType.Obj), type)); } case ILOpCode.Ldelem: return LdElem(ReadAndDecodeTypeReference()); @@ -1212,7 +1212,7 @@ namespace ICSharpCode.Decompiler.IL return new StObj(value: Pop(field.Type.GetStackType()), target: new LdFlda(PopFieldTarget(field), field) { DelayExceptions = true }, type: field.Type); } case ILOpCode.Ldlen: - return Push(new LdLen(StackType.I, Pop(StackType.O))); + return Push(new LdLen(StackType.I, Pop(StackType.Obj))); case ILOpCode.Ldobj: return Push(new LdObj(PopPointer(), ReadAndDecodeTypeReference())); case ILOpCode.Ldsfld: @@ -1330,9 +1330,13 @@ namespace ICSharpCode.Decompiler.IL { v2.Name = $"S_{variables.Count - 1}"; } - if (v1 != v2 && !v1.Type.Equals(v2.Type) && !v2.Type.CannotBeReconstructedFromStackType()) + Debug.Assert(v1.StackType == v2.StackType); + // When branches with unequal types are merged, go back to the raw stack type, + // to ensure that the variable can accept all possible values across all branches. + // Exception: don't do this for value types, as FindType(stackType) wouldn't work for them. + if (v1 != v2 && !v1.Type.Equals(v2.Type) && !(v2.StackType == StackType.O && v2.Type.IsReferenceType != true)) { - v2.Type = compilation.FindType(v1.StackType); + v2.Type = compilation.FindType(v2.StackType); } return v2; } @@ -1464,7 +1468,7 @@ namespace ICSharpCode.Decompiler.IL else if (expectedType == StackType.Ref) { // implicitly start GC tracking / object to interior - if (!inst.ResultType.IsIntegerType() && inst.ResultType != StackType.O) + if (!inst.ResultType.IsIntegerType() && inst.ResultType != StackType.Obj) { // We also handle the invalid to-ref cases here because the else case // below uses expectedType.ToKnownTypeCode(), which doesn't work for Ref. @@ -1533,13 +1537,13 @@ namespace ICSharpCode.Decompiler.IL switch (field.DeclaringType.IsReferenceType) { case true: - return Pop(StackType.O); + return Pop(StackType.Obj); case false: return PopPointer(); default: // field in unresolved type var stackType = PeekStackType(); - if (stackType == StackType.O || stackType == StackType.Unknown) + if (stackType is StackType.Obj or StackType.VT or StackType.Unknown) return Pop(); else return PopPointer(); @@ -1554,16 +1558,16 @@ namespace ICSharpCode.Decompiler.IL switch (field.DeclaringType.IsReferenceType) { case true: - return Pop(StackType.O); + return Pop(StackType.Obj); case false: // field of value type: ldfld can handle temporaries - if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) + if (PeekStackType() is StackType.VT or StackType.Unknown) return new AddressOf(Pop(), field.DeclaringType); else return PopPointer(); default: // field in unresolved type - if (PeekStackType() == StackType.O || PeekStackType() == StackType.Unknown) + if (PeekStackType() is StackType.Obj or StackType.VT or StackType.Unknown) return Pop(); else return PopPointer(); @@ -1916,7 +1920,7 @@ namespace ICSharpCode.Decompiler.IL ILInstruction Comparison(ComparisonKind kind, bool un = false) { - if (!kind.IsEqualityOrInequality() && PeekStackType() == StackType.O) + if (!kind.IsEqualityOrInequality() && PeekStackType() == StackType.Obj) { FlushExpressionStack(); } @@ -1925,7 +1929,7 @@ namespace ICSharpCode.Decompiler.IL var left = Pop(); // left will run before right, thus preserving the evaluation order - if ((left.ResultType == StackType.O || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType()) + if ((left.ResultType == StackType.Obj || left.ResultType == StackType.Ref) && right.ResultType.IsIntegerType()) { // C++/CLI sometimes compares object references with integers. // Also happens with Ref==I in Unsafe.IsNullRef(). @@ -1936,7 +1940,7 @@ namespace ICSharpCode.Decompiler.IL } left = new Conv(left, right.ResultType.ToPrimitiveType(), false, Sign.None); } - else if ((right.ResultType == StackType.O || right.ResultType == StackType.Ref) && left.ResultType.IsIntegerType()) + else if ((right.ResultType == StackType.Obj || right.ResultType == StackType.Ref) && left.ResultType.IsIntegerType()) { if (left.ResultType == StackType.I4) { @@ -2031,7 +2035,7 @@ namespace ICSharpCode.Decompiler.IL ILInstruction condition = Pop(); switch (condition.ResultType) { - case StackType.O: + case StackType.Obj: // introduce explicit comparison with null condition = new Comp( negate ? ComparisonKind.Equality : ComparisonKind.Inequality, diff --git a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs index 5b6087792..6007913dc 100644 --- a/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs +++ b/ICSharpCode.Decompiler/IL/ILTypeExtensions.cs @@ -17,6 +17,7 @@ // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System; using System.Diagnostics; using System.Linq; @@ -53,7 +54,7 @@ namespace ICSharpCode.Decompiler.IL case PrimitiveType.Unknown: return StackType.Unknown; default: - return StackType.O; + return StackType.Obj; } } diff --git a/ICSharpCode.Decompiler/IL/Instructions.cs b/ICSharpCode.Decompiler/IL/Instructions.cs index 18072653c..0b958ac6f 100644 --- a/ICSharpCode.Decompiler/IL/Instructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions.cs @@ -1147,7 +1147,7 @@ namespace ICSharpCode.Decompiler.IL /// Common instruction for dynamic compound assignments. public sealed partial class DynamicCompoundAssign : CompoundAssignmentInstruction { - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; protected override InstructionFlags ComputeFlags() { @@ -1956,7 +1956,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(onExpression.ResultType == StackType.O); + DebugAssert(onExpression.ResultType == StackType.Obj); } } } @@ -3081,7 +3081,7 @@ namespace ICSharpCode.Decompiler.IL this.Value = value; } public readonly decimal Value; - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.VT; public override IType InferType(ICompilation compilation) => compilation.FindType(KnownTypeCode.Decimal); protected override void WriteToCore(ITextOutput output, ILAstWritingOptions options) { @@ -4928,7 +4928,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(array.ResultType == StackType.O); + DebugAssert(array.ResultType == StackType.Obj); } } } @@ -5302,7 +5302,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(argument.ResultType == StackType.O); + DebugAssert(argument.ResultType == StackType.Obj); } } } @@ -5392,7 +5392,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(argument.ResultType == StackType.O); + DebugAssert(argument.ResultType == StackType.Obj); } } } @@ -5918,7 +5918,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(argument.ResultType == StackType.O); + DebugAssert(argument.ResultType == StackType.Obj); } } } @@ -6006,7 +6006,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(target.ResultType == StackType.O); + DebugAssert(target.ResultType == StackType.Obj); } } } @@ -6111,7 +6111,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(target.ResultType == StackType.O); + DebugAssert(target.ResultType == StackType.Obj); } } } @@ -6554,7 +6554,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - DebugAssert(argument.ResultType == StackType.O); + DebugAssert(argument.ResultType == StackType.Obj); } } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs index fa621f689..5343e773a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/BinaryNumericInstruction.cs @@ -134,7 +134,7 @@ namespace ICSharpCode.Decompiler.IL public StackType UnderlyingResultType => resultType; public sealed override StackType ResultType { - get => IsLifted ? StackType.O : resultType; + get => IsLifted ? StackType.VT : resultType; } public override IType InferType(ICompilation compilation) diff --git a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs index d260cbe12..194d5ff37 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CallInstruction.cs @@ -94,7 +94,7 @@ namespace ICSharpCode.Decompiler.IL /// /// Gets the expected stack type for passing the this pointer in a method call. /// Returns StackType.Ref if constrainedTo is not null, - /// StackType.O for reference types (this pointer passed as object reference), + /// StackType.Obj for reference types (this pointer passed as object reference), /// and StackType.Ref for type parameters and value types (this pointer passed as managed reference). /// /// Returns StackType.Unknown if the input type is unknown. @@ -108,7 +108,7 @@ namespace ICSharpCode.Decompiler.IL switch (declaringType.IsReferenceType) { case true: - return StackType.O; + return StackType.Obj; case false: return StackType.Ref; default: diff --git a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs index da3c5ef09..4615c8aeb 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Comp.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Comp.cs @@ -160,7 +160,7 @@ namespace ICSharpCode.Decompiler.IL this.Sign = sign; } - public override StackType ResultType => LiftingKind == ComparisonLiftingKind.ThreeValuedLogic ? StackType.O : StackType.I4; + public override StackType ResultType => LiftingKind == ComparisonLiftingKind.ThreeValuedLogic ? StackType.VT : StackType.I4; public override IType InferType(ICompilation compilation) { @@ -188,8 +188,8 @@ namespace ICSharpCode.Decompiler.IL } else { - Debug.Assert(Left.ResultType == InputType || Left.ResultType == StackType.O); - Debug.Assert(Right.ResultType == InputType || Right.ResultType == StackType.O); + Debug.Assert(Left.ResultType == InputType || Left.ResultType == StackType.VT); + Debug.Assert(Right.ResultType == InputType || Right.ResultType == StackType.VT); } } @@ -250,7 +250,7 @@ namespace ICSharpCode.Decompiler.IL // Unsafe.As(ref a) op Unsafe.As(ref b), which requires that a and b are variables // and not expressions. Returning false in those cases prevents inlining. // However if one of the arguments is LdNull, then we don't need the Unsafe.As trickery, and can always inline. - if (kind.IsEqualityOrInequality() || this.InputType != StackType.O) + if (kind.IsEqualityOrInequality() || this.InputType != StackType.Obj) { // OK, won't need Unsafe.As. return true; diff --git a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs index 41c7158f4..f350c74b6 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/CompoundAssignmentInstruction.cs @@ -168,7 +168,7 @@ namespace ICSharpCode.Decompiler.IL this.type = type; this.AddILRange(binary); Debug.Assert(evalMode == CompoundEvalMode.EvaluatesToNewValue || Operator == BinaryNumericOperator.Add || Operator == BinaryNumericOperator.Sub); - Debug.Assert(this.ResultType == (IsLifted ? StackType.O : UnderlyingResultType)); + Debug.Assert(this.ResultType == (IsLifted ? StackType.VT : UnderlyingResultType)); } /// diff --git a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs index bce6575f3..96f30d74c 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/Conv.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/Conv.cs @@ -146,7 +146,7 @@ namespace ICSharpCode.Decompiler.IL /// /// For lifted conversions, corresponds to the underlying target type. /// - /// Target type == PrimitiveType.None can happen for implicit conversions to O in invalid IL. + /// Target type == PrimitiveType.None can happen for implicit conversions to Obj in invalid IL. /// public readonly PrimitiveType TargetType; @@ -173,7 +173,7 @@ namespace ICSharpCode.Decompiler.IL { 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(Argument.ResultType == (IsLifted ? StackType.VT : InputType)); Debug.Assert(!(IsLifted && Kind == ConversionKind.StopGCTracking)); } @@ -231,7 +231,7 @@ namespace ICSharpCode.Decompiler.IL case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: - case StackType.O: + case StackType.Obj: return ConversionKind.StopGCTracking; default: return ConversionKind.Invalid; @@ -253,7 +253,7 @@ namespace ICSharpCode.Decompiler.IL case StackType.F8: return ConversionKind.FloatToInt; case StackType.Ref: - case StackType.O: + case StackType.Obj: return ConversionKind.StopGCTracking; default: return ConversionKind.Invalid; @@ -296,7 +296,7 @@ namespace ICSharpCode.Decompiler.IL case StackType.I: case StackType.I8: return ConversionKind.StartGCTracking; - case StackType.O: + case StackType.Obj: return ConversionKind.ObjectInterior; default: return ConversionKind.Invalid; diff --git a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs index 22a4b19d7..3a84b200a 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/DynamicInstructions.cs @@ -223,7 +223,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo.Skip(ArgumentInfoOffset))); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; @@ -259,7 +259,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, (Target, TargetArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) @@ -296,7 +296,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, (Target, TargetArgumentInfo), (Value, ValueArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) @@ -335,7 +335,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) @@ -368,7 +368,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) @@ -448,7 +448,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, (Left, LeftArgumentInfo), (Right, RightArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) @@ -491,7 +491,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, (Left, LeftArgumentInfo), (Right, RightArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; protected override InstructionFlags ComputeFlags() @@ -547,7 +547,7 @@ namespace ICSharpCode.Decompiler.IL case ExpressionType.IsTrue: return StackType.I4; // bool default: - return StackType.O; + return StackType.Obj; // dynamic } } } @@ -597,7 +597,7 @@ namespace ICSharpCode.Decompiler.IL WriteArgumentList(output, options, Arguments.Zip(ArgumentInfo)); } - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.Obj; public override IType InferType(ICompilation compilation) => SpecialType.Dynamic; public override CSharpArgumentInfo GetArgumentInfoOfChild(int index) diff --git a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs index d79170201..ec4780235 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LdFlda.cs @@ -31,7 +31,7 @@ namespace ICSharpCode.Decompiler.IL switch (field.DeclaringType.IsReferenceType) { case true: - Debug.Assert(target.ResultType == StackType.O, + Debug.Assert(target.ResultType == StackType.Obj, "Class fields can only be accessed with an object on the stack"); break; case false: @@ -40,8 +40,7 @@ namespace ICSharpCode.Decompiler.IL break; case null: // field of unresolved type - Debug.Assert(target.ResultType == StackType.O || target.ResultType == StackType.I - || target.ResultType == StackType.Ref || target.ResultType == StackType.Unknown, + Debug.Assert(target.ResultType is StackType.Obj or StackType.VT or StackType.I or StackType.Ref or StackType.Unknown, "Field of unresolved type with invalid target"); break; } diff --git a/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs index 721418769..bf76498d4 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/LogicInstructions.cs @@ -31,13 +31,13 @@ namespace ICSharpCode.Decompiler.IL bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.VT; public override IType InferType(ICompilation compilation) => NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O); + Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.VT); } } @@ -46,13 +46,13 @@ namespace ICSharpCode.Decompiler.IL bool ILiftableInstruction.IsLifted => true; StackType ILiftableInstruction.UnderlyingResultType => StackType.I4; - public override StackType ResultType => StackType.O; + public override StackType ResultType => StackType.VT; public override IType InferType(ICompilation compilation) => NullableType.Create(compilation, compilation.FindType(KnownTypeCode.Boolean)); internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.O); + Debug.Assert(Left.ResultType == StackType.I4 || Left.ResultType == StackType.VT); } } diff --git a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs index 45c30f9ca..16db6f311 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/NullableInstructions.cs @@ -88,7 +88,7 @@ namespace ICSharpCode.Decompiler.IL } else { - Debug.Assert(Argument.ResultType == StackType.O, "nullable.unwrap expects nullable type as input"); + Debug.Assert(Argument.ResultType == StackType.VT, "nullable.unwrap expects nullable type as input"); } Debug.Assert(Ancestors.Any(a => a is NullableRewrap)); } diff --git a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs index 9e7c8a4f3..7ee30090e 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/SwitchInstruction.cs @@ -172,7 +172,7 @@ namespace ICSharpCode.Decompiler.IL } Debug.Assert(sets.SetEquals(LongSet.Universe), "switch does not handle all possible cases"); Debug.Assert(!expectNullSection, "Lifted switch is missing 'case null'"); - Debug.Assert(this.IsLifted ? (value.ResultType == StackType.O) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8)); + Debug.Assert(this.IsLifted ? (value.ResultType == StackType.VT) : (value.ResultType == StackType.I4 || value.ResultType == StackType.I8)); } public SwitchSection GetDefaultSection() diff --git a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs index 0c105608a..a305de637 100644 --- a/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs +++ b/ICSharpCode.Decompiler/IL/Instructions/UnaryInstruction.cs @@ -45,7 +45,7 @@ namespace ICSharpCode.Decompiler.IL internal override void CheckInvariant(ILPhase phase, ICompilation compilation) { base.CheckInvariant(phase, compilation); - Debug.Assert(IsLifted == (ResultType == StackType.O)); + Debug.Assert(IsLifted == (ResultType == StackType.VT)); Debug.Assert(IsLifted || ResultType == UnderlyingResultType); } diff --git a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs index e6d7f242d..5d32f0fa8 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/ExpressionTransforms.cs @@ -152,12 +152,12 @@ namespace ICSharpCode.Decompiler.IL.Transforms inst.Left.ReplaceWith(new LdLen(StackType.I4, array).WithILRange(inst.Left)); inst.Right = rightWithoutConv; } - else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.O) + else if (inst.Left is Conv conv && conv.TargetType == PrimitiveType.I && conv.Argument.ResultType == StackType.Obj) { // C++/CLI sometimes uses this weird comparison with null: context.Step("comp(conv o->i (ldloc obj) == conv i4->i (ldc.i4 0))", inst); // -> comp(ldloc obj == ldnull) - inst.InputType = StackType.O; + inst.InputType = StackType.Obj; inst.Left = conv.Argument; inst.Right = new LdNull().WithILRange(inst.Right); inst.Right.AddILRange(rightWithoutConv); diff --git a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs index ee17128cb..be45ced84 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/PatternMatchingTransform.cs @@ -386,7 +386,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms } context.Step("Nullable.HasValue check -> null pattern", block); - var nullComp = new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull()); + var nullComp = new Comp(ComparisonKind.Equality, ComparisonLiftingKind.CSharp, StackType.VT, Sign.None, varPattern.TestedOperand, new LdNull()); varPattern.ReplaceWith(nullComp); context.EndStep(nullComp); block.Instructions.Clear(); @@ -398,7 +398,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms if (varPattern.Variable.AddressCount == 1 && context.Settings.PatternCombinators) { context.Step("Nullable.HasValue check -> not null pattern", block); - var notNullComp = new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.O, Sign.None, varPattern.TestedOperand, new LdNull()); + var notNullComp = new Comp(ComparisonKind.Inequality, ComparisonLiftingKind.CSharp, StackType.VT, Sign.None, varPattern.TestedOperand, new LdNull()); varPattern.ReplaceWith(notNullComp); context.EndStep(notNullComp); block.Instructions.Clear(); diff --git a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs index 52ed30206..8fa3f1611 100644 --- a/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs +++ b/ICSharpCode.Decompiler/IL/Transforms/TransformExpressionTrees.cs @@ -1350,7 +1350,7 @@ namespace ICSharpCode.Decompiler.IL.Transforms case StackType.F8: left = new LdcF8(0); break; - case StackType.O when underlyingType.IsKnownType(KnownTypeCode.Decimal): + case StackType.VT when underlyingType.IsKnownType(KnownTypeCode.Decimal): left = new LdcDecimal(0); break; default: diff --git a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs index c9e0340bc..61127a6ea 100644 --- a/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs +++ b/ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs @@ -267,7 +267,7 @@ namespace ICSharpCode.Decompiler.TypeSystem case TypeKind.Unknown: if (type.IsReferenceType == true) { - return StackType.O; + return StackType.Obj; } return StackType.Unknown; case TypeKind.ByReference: @@ -278,17 +278,18 @@ namespace ICSharpCode.Decompiler.TypeSystem case TypeKind.FunctionPointer: return StackType.I; case TypeKind.TypeParameter: - // Type parameters are always considered StackType.O, even - // though they might be instantiated with primitive types. - return StackType.O; + // Type parameters are always considered StackType.Obj or StackType.VT, + // even though they might be instantiated with primitive types. + if (type.IsReferenceType == true) + return StackType.Obj; + else + return StackType.VT; case TypeKind.ModOpt: case TypeKind.ModReq: return type.SkipModifiers().GetStackType(); } ITypeDefinition typeDef = type.GetEnumUnderlyingType().GetDefinition(); - if (typeDef == null) - return StackType.O; - switch (typeDef.KnownTypeCode) + switch (typeDef?.KnownTypeCode) { case KnownTypeCode.Boolean: case KnownTypeCode.Char: @@ -312,25 +313,11 @@ namespace ICSharpCode.Decompiler.TypeSystem case KnownTypeCode.UIntPtr: return StackType.I; default: - return StackType.O; - } - } - - /// - /// Returns true for types where compilation.FindType(type.GetStackType()) will - /// be completely unsuitable (e.g. lead to miscompilation if the stack type - /// alone is used for when a variable is created for a stack slot): - /// * managed reference types - /// * value types with StackType.O - /// - public static bool CannotBeReconstructedFromStackType(this IType type) - { - var stackType = type.GetStackType(); - if (stackType == StackType.Ref) - { - return true; + if (type.IsReferenceType == true) + return StackType.Obj; + else + return StackType.VT; } - return stackType == StackType.O && type.IsReferenceType == false; } /// @@ -502,7 +489,7 @@ namespace ICSharpCode.Decompiler.TypeSystem return KnownTypeCode.Single; case StackType.F8: return KnownTypeCode.Double; - case StackType.O: + case StackType.Obj: return KnownTypeCode.Object; case StackType.Void: return KnownTypeCode.Void;