Browse Source

Add support for arithmetic using C# 9 native integer types.

pull/2063/head
Daniel Grunwald 5 years ago
parent
commit
3a4db502bc
  1. 4
      ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs
  2. 25
      ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs
  3. 108
      ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
  4. 81
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs
  5. 21
      ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs
  6. 2
      ICSharpCode.Decompiler/IL/ILTypeExtensions.cs
  7. 16
      ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

4
ICSharpCode.Decompiler.Tests/TestCases/ILPretty/Unsafe.cs

@ -186,7 +186,7 @@ namespace System.Runtime.CompilerServices @@ -186,7 +186,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Add<T>(void* source, int elementOffset)
{
return (byte*)source + (long)elementOffset * (long)sizeof(T);
return (byte*)source + (nint)elementOffset * (nint)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
@ -210,7 +210,7 @@ namespace System.Runtime.CompilerServices @@ -210,7 +210,7 @@ namespace System.Runtime.CompilerServices
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public unsafe static void* Subtract<T>(void* source, int elementOffset)
{
return (byte*)source - (long)elementOffset * (long)sizeof(T);
return (byte*)source - (nint)elementOffset * (nint)sizeof(T);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]

25
ICSharpCode.Decompiler.Tests/TestCases/Pretty/NativeInts.cs

@ -73,5 +73,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty @@ -73,5 +73,30 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty
u64 = (uint)i;
i = (nint)u64;
}
public void Arithmetic()
{
Console.WriteLine((nint)intptr * 2);
Console.WriteLine(i * 2);
Console.WriteLine(i + (nint)u);
Console.WriteLine((nuint)i + u);
}
public object[] Boxing()
{
return new object[10] {
1,
(nint)2,
3L,
4u,
(nuint)5u,
6uL,
int.MaxValue,
(nint)int.MaxValue,
i64,
(nint)i64
};
}
}
}

108
ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs

@ -109,15 +109,10 @@ namespace ICSharpCode.Decompiler.CSharp @@ -109,15 +109,10 @@ namespace ICSharpCode.Decompiler.CSharp
if (!allowImplicitConversion) {
if (expr is NullReferenceExpression && rr.Type.Kind != TypeKind.Null) {
expr = new CastExpression(ConvertType(rr.Type), expr);
} else {
switch (rr.Type.GetDefinition()?.KnownTypeCode) {
case KnownTypeCode.SByte:
case KnownTypeCode.Byte:
case KnownTypeCode.Int16:
case KnownTypeCode.UInt16:
expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr);
break;
}
} else if (rr.Type.IsCSharpSmallIntegerType()) {
expr = new CastExpression(new PrimitiveType(KnownTypeReference.GetCSharpNameByTypeCode(rr.Type.GetDefinition().KnownTypeCode)), expr);
} else if (rr.Type.IsCSharpNativeIntegerType()) {
expr = new CastExpression(new PrimitiveType(rr.Type.Name), expr);
}
}
var exprRR = expr.Annotation<ResolveResult>();
@ -900,24 +895,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -900,24 +895,14 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
// Ensure the inputs have the correct sign:
KnownTypeCode inputType = KnownTypeCode.None;
switch (inst.InputType) {
case StackType.I: // In order to generate valid C# we need to treat (U)IntPtr as (U)Int64 in comparisons.
case StackType.I8:
inputType = inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt64 : KnownTypeCode.Int64;
break;
case StackType.I4:
inputType = inst.Sign == Sign.Unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32;
break;
}
if (inputType != KnownTypeCode.None) {
IType targetType = compilation.FindType(inputType);
if (inst.InputType.IsIntegerType()) {
// Ensure the inputs have the correct sign:
IType inputType = FindArithmeticType(inst.InputType, inst.Sign);
if (inst.IsLifted) {
targetType = NullableType.Create(compilation, targetType);
inputType = NullableType.Create(compilation, inputType);
}
left = left.ConvertTo(targetType, this);
right = right.ConvertTo(targetType, this);
left = left.ConvertTo(inputType, this);
right = right.ConvertTo(inputType, this);
}
return new BinaryOperatorExpression(left.Expression, op, right.Expression)
.WithILInstruction(inst)
@ -993,22 +978,22 @@ namespace ICSharpCode.Decompiler.CSharp @@ -993,22 +978,22 @@ namespace ICSharpCode.Decompiler.CSharp
{
switch (inst.Operator) {
case BinaryNumericOperator.Add:
return HandleBinaryNumeric(inst, BinaryOperatorType.Add);
return HandleBinaryNumeric(inst, BinaryOperatorType.Add, context);
case BinaryNumericOperator.Sub:
return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract);
return HandleBinaryNumeric(inst, BinaryOperatorType.Subtract, context);
case BinaryNumericOperator.Mul:
return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply);
return HandleBinaryNumeric(inst, BinaryOperatorType.Multiply, context);
case BinaryNumericOperator.Div:
return HandlePointerSubtraction(inst)
?? HandleBinaryNumeric(inst, BinaryOperatorType.Divide);
?? HandleBinaryNumeric(inst, BinaryOperatorType.Divide, context);
case BinaryNumericOperator.Rem:
return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus);
return HandleBinaryNumeric(inst, BinaryOperatorType.Modulus, context);
case BinaryNumericOperator.BitAnd:
return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd);
return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseAnd, context);
case BinaryNumericOperator.BitOr:
return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr);
return HandleBinaryNumeric(inst, BinaryOperatorType.BitwiseOr, context);
case BinaryNumericOperator.BitXor:
return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr);
return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context);
case BinaryNumericOperator.ShiftLeft:
return HandleShift(inst, BinaryOperatorType.ShiftLeft);
case BinaryNumericOperator.ShiftRight:
@ -1158,7 +1143,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1158,7 +1143,7 @@ namespace ICSharpCode.Decompiler.CSharp
TranslatedExpression EnsureIntegerType(TranslatedExpression expr)
{
if (!expr.Type.IsCSharpPrimitiveIntegerType()) {
if (!expr.Type.IsCSharpPrimitiveIntegerType() && !expr.Type.IsCSharpNativeIntegerType()) {
// pointer arithmetic accepts all primitive integer types, but no enums etc.
StackType targetType = expr.Type.GetStackType() == StackType.I4 ? StackType.I4 : StackType.I8;
expr = expr.ConvertTo(
@ -1254,7 +1239,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1254,7 +1239,7 @@ namespace ICSharpCode.Decompiler.CSharp
}
}
TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op)
TranslatedExpression HandleBinaryNumeric(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context)
{
var resolverWithOverflowCheck = resolver.WithCheckForOverflow(inst.CheckForOverflow);
var left = Translate(inst.Left);
@ -1312,8 +1297,12 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1312,8 +1297,12 @@ namespace ICSharpCode.Decompiler.CSharp
|| !IsCompatibleWithSign(left.Type, inst.Sign) || !IsCompatibleWithSign(right.Type, inst.Sign))
{
// Left and right operands are incompatible, so convert them to a common type
StackType targetStackType = inst.UnderlyingResultType == StackType.I ? StackType.I8 : inst.UnderlyingResultType;
IType targetType = compilation.FindType(targetStackType.ToKnownTypeCode(inst.Sign));
Sign sign = inst.Sign;
if (sign == Sign.None) {
// If the sign doesn't matter, try to use the same sign as expected by the context
sign = context.TypeHint.GetSign();
}
IType targetType = FindArithmeticType(inst.UnderlyingResultType, sign);
left = left.ConvertTo(NullableType.IsNullable(left.Type) ? NullableType.Create(compilation, targetType) : targetType, this);
right = right.ConvertTo(NullableType.IsNullable(right.Type) ? NullableType.Create(compilation, targetType) : targetType, this);
rr = resolverWithOverflowCheck.ResolveBinaryOperator(op, left.ResolveResult, right.ResolveResult);
@ -1327,6 +1316,28 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1327,6 +1316,28 @@ namespace ICSharpCode.Decompiler.CSharp
return resultExpr;
}
IType FindType(StackType stackType, Sign sign)
{
if (stackType == StackType.I && settings.NativeIntegers) {
return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt;
} else {
return compilation.FindType(stackType.ToKnownTypeCode(sign));
}
}
IType FindArithmeticType(StackType stackType, Sign sign)
{
if (stackType == StackType.I) {
if (settings.NativeIntegers) {
return sign == Sign.Unsigned ? SpecialType.NUInt : SpecialType.NInt;
} else {
// If native integers are not available, use 64-bit arithmetic instead
stackType = StackType.I8;
}
}
return compilation.FindType(stackType.ToKnownTypeCode(sign));
}
/// <summary>
/// Handle oversized arguments needing truncation; and avoid IntPtr/pointers in arguments.
/// </summary>
@ -1339,17 +1350,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1339,17 +1350,17 @@ namespace ICSharpCode.Decompiler.CSharp
if (argStackType.IsIntegerType() && argStackType.GetSize() < argUType.GetSize()) {
// If the argument is oversized (needs truncation to match stack size of its ILInstruction),
// perform the truncation now.
IType targetType = compilation.FindType(argStackType.ToKnownTypeCode(sign));
IType targetType = FindType(argStackType, sign);
argUType = targetType;
if (isLifted)
targetType = NullableType.Create(compilation, targetType);
arg = arg.ConvertTo(targetType, this);
}
if (argUType.GetStackType() == StackType.I) {
if (argUType.IsKnownType(KnownTypeCode.IntPtr) || argUType.IsKnownType(KnownTypeCode.UIntPtr)) {
// None of the operators we might want to apply are supported by IntPtr/UIntPtr.
// Also, pointer arithmetic has different semantics (works in number of elements, not bytes).
// So any inputs of size StackType.I must be converted to long/ulong.
IType targetType = compilation.FindType(StackType.I8.ToKnownTypeCode(sign));
IType targetType = FindArithmeticType(StackType.I, sign);
if (isLifted)
targetType = NullableType.Create(compilation, targetType);
arg = arg.ConvertTo(targetType, this);
@ -1631,7 +1642,11 @@ namespace ICSharpCode.Decompiler.CSharp @@ -1631,7 +1642,11 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitConv(Conv inst, TranslationContext context)
{
var arg = Translate(inst.Argument);
Sign hintSign = inst.InputSign;
if (hintSign == Sign.None) {
hintSign = context.TypeHint.GetSign();
}
var arg = Translate(inst.Argument, typeHint: FindArithmeticType(inst.InputType, hintSign));
IType inputType = NullableType.GetUnderlyingType(arg.Type);
StackType inputStackType = inst.InputType;
// Note: we're dealing with two conversions here:
@ -2344,8 +2359,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -2344,8 +2359,17 @@ namespace ICSharpCode.Decompiler.CSharp
protected internal override TranslatedExpression VisitBox(Box inst, TranslationContext context)
{
IType targetType = inst.Type;
var arg = Translate(inst.Argument, typeHint: targetType);
if (settings.NativeIntegers && !arg.Type.Equals(targetType)) {
if (targetType.IsKnownType(KnownTypeCode.IntPtr)) {
targetType = SpecialType.NInt;
} else if (targetType.IsKnownType(KnownTypeCode.UIntPtr)) {
targetType = SpecialType.NUInt;
}
}
arg = arg.ConvertTo(targetType, this);
var obj = compilation.FindType(KnownTypeCode.Object);
var arg = Translate(inst.Argument, typeHint: inst.Type).ConvertTo(inst.Type, this);
return new CastExpression(ConvertType(obj), arg.Expression)
.WithILInstruction(inst)
.WithRR(new ConversionResolveResult(obj, arg.ResolveResult, Conversion.BoxingConversion));

81
ICSharpCode.Decompiler/CSharp/Resolver/CSharpResolver.cs

@ -774,6 +774,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -774,6 +774,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return HandleEnumComparison(op, rhsType, isNullable, lhs, rhs);
} else if (lhsType is PointerType && rhsType is PointerType) {
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs);
} else if (lhsType.IsCSharpNativeIntegerType() || rhsType.IsCSharpNativeIntegerType()) {
if (lhsType.Equals(rhsType))
return BinaryOperatorResolveResult(compilation.FindType(KnownTypeCode.Boolean), lhs, op, rhs, isLifted: isNullable);
else
return new ErrorResolveResult(compilation.FindType(KnownTypeCode.Boolean));
}
if (op == BinaryOperatorType.Equality || op == BinaryOperatorType.InEquality) {
if (lhsType.IsReferenceType == true && rhsType.IsReferenceType == true) {
@ -855,6 +860,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -855,6 +860,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
default:
throw new InvalidOperationException();
}
if (lhsType.IsCSharpNativeIntegerType() || rhsType.IsCSharpNativeIntegerType()) {
if (lhsType.Equals(rhsType)) {
return BinaryOperatorResolveResult(
isNullable ? NullableType.Create(compilation, lhsType) : lhsType,
lhs, op, rhs, isLifted: isNullable);
}
// mixing nint/nuint is not allowed
return new ErrorResolveResult(lhsType);
}
OverloadResolution builtinOperatorOR = CreateOverloadResolution(new[] { lhs, rhs });
foreach (var candidate in methodGroup) {
builtinOperatorOR.AddCandidate(candidate);
@ -1000,8 +1014,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1000,8 +1014,22 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool BinaryNumericPromotion(bool isNullable, ref ResolveResult lhs, ref ResolveResult rhs, bool allowNullableConstants)
{
// C# 4.0 spec: §7.3.6.2
TypeCode lhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(lhs.Type));
TypeCode rhsCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(rhs.Type));
var lhsUType = NullableType.GetUnderlyingType(lhs.Type);
var rhsUType = NullableType.GetUnderlyingType(rhs.Type);
TypeCode lhsCode = ReflectionHelper.GetTypeCode(lhsUType);
TypeCode rhsCode = ReflectionHelper.GetTypeCode(rhsUType);
// Treat C# 9 native integers as falling between int and long.
// However they don't have a TypeCode, so we hack around that here:
if (lhsUType.Kind == TypeKind.NInt) {
lhsCode = TypeCode.Int32;
} else if (lhsUType.Kind == TypeKind.NUInt) {
lhsCode = TypeCode.UInt32;
}
if (rhsUType.Kind == TypeKind.NInt) {
rhsCode = TypeCode.Int32;
} else if (rhsUType.Kind == TypeKind.NUInt) {
rhsCode = TypeCode.UInt32;
}
// if one of the inputs is the null literal, promote that to the type of the other operand
if (isNullable && lhs.Type.Kind == TypeKind.Null && rhsCode >= TypeCode.Boolean && rhsCode <= TypeCode.Decimal) {
lhs = CastTo(rhsCode, isNullable, lhs, allowNullableConstants);
@ -1018,7 +1046,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1018,7 +1046,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
if (lhsCode == TypeCode.Decimal || rhsCode == TypeCode.Decimal) {
targetType = TypeCode.Decimal;
bindingError = (lhsCode == TypeCode.Single || lhsCode == TypeCode.Double
|| rhsCode == TypeCode.Single || rhsCode == TypeCode.Double);
|| rhsCode == TypeCode.Single || rhsCode == TypeCode.Double);
} else if (lhsCode == TypeCode.Double || rhsCode == TypeCode.Double) {
targetType = TypeCode.Double;
} else if (lhsCode == TypeCode.Single || rhsCode == TypeCode.Single) {
@ -1026,10 +1054,19 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1026,10 +1054,19 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
} else if (lhsCode == TypeCode.UInt64 || rhsCode == TypeCode.UInt64) {
targetType = TypeCode.UInt64;
bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs);
} else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) {
targetType = TypeCode.Int64;
} else if (lhsUType.Kind == TypeKind.NUInt || rhsUType.Kind == TypeKind.NUInt) {
bindingError = IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs);
lhs = CastTo(SpecialType.NUInt, isNullable, lhs, allowNullableConstants);
rhs = CastTo(SpecialType.NUInt, isNullable, rhs, allowNullableConstants);
return !bindingError;
} else if (lhsCode == TypeCode.UInt32 || rhsCode == TypeCode.UInt32) {
targetType = (IsSigned(lhsCode, lhs) || IsSigned(rhsCode, rhs)) ? TypeCode.Int64 : TypeCode.UInt32;
} else if (lhsCode == TypeCode.Int64 || rhsCode == TypeCode.Int64) {
targetType = TypeCode.Int64;
} else if (lhsUType.Kind == TypeKind.NInt || rhsUType.Kind == TypeKind.NInt) {
lhs = CastTo(SpecialType.NInt, isNullable, lhs, allowNullableConstants);
rhs = CastTo(SpecialType.NInt, isNullable, rhs, allowNullableConstants);
return !bindingError;
} else {
targetType = TypeCode.Int32;
}
@ -1063,17 +1100,21 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1063,17 +1100,21 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return false;
}
}
ResolveResult CastTo(TypeCode targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants)
{
IType elementType = compilation.FindType(targetType);
IType nullableType = MakeNullable(elementType, isNullable);
return CastTo(compilation.FindType(targetType), isNullable, expression, allowNullableConstants);
}
ResolveResult CastTo(IType targetType, bool isNullable, ResolveResult expression, bool allowNullableConstants)
{
IType nullableType = MakeNullable(targetType, isNullable);
if (nullableType.Equals(expression.Type))
return expression;
if (allowNullableConstants && expression.IsCompileTimeConstant) {
if (expression.ConstantValue == null)
return new ConstantResolveResult(nullableType, null);
ResolveResult rr = ResolveCast(elementType, expression);
ResolveResult rr = ResolveCast(targetType, expression);
if (rr.IsError)
return rr;
Debug.Assert(rr.IsCompileTimeConstant);
@ -1262,7 +1303,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1262,7 +1303,8 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
// C# 4.0 spec: §7.7.6 Cast expressions
Conversion c = conversions.ExplicitConversion(expression, targetType);
if (expression.IsCompileTimeConstant && !c.IsUserDefined) {
TypeCode code = ReflectionHelper.GetTypeCode(targetType);
IType underlyingType = targetType.GetEnumUnderlyingType();
TypeCode code = ReflectionHelper.GetTypeCode(underlyingType);
if (code >= TypeCode.Boolean && code <= TypeCode.Decimal && expression.ConstantValue != null) {
try {
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
@ -1276,16 +1318,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -1276,16 +1318,15 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return new ConstantResolveResult(targetType, expression.ConstantValue);
else
return new ErrorResolveResult(targetType);
} else if (targetType.Kind == TypeKind.Enum) {
code = ReflectionHelper.GetTypeCode(GetEnumUnderlyingType(targetType));
if (code >= TypeCode.SByte && code <= TypeCode.UInt64 && expression.ConstantValue != null) {
try {
return new ConstantResolveResult(targetType, CSharpPrimitiveCast(code, expression.ConstantValue));
} catch (OverflowException) {
return new ErrorResolveResult(targetType);
} catch (InvalidCastException) {
return new ErrorResolveResult(targetType);
}
} else if ((underlyingType.Kind == TypeKind.NInt || underlyingType.Kind == TypeKind.NUInt) && expression.ConstantValue != null) {
code = (underlyingType.Kind == TypeKind.NInt ? TypeCode.Int32 : TypeCode.UInt32);
try {
return new ConstantResolveResult(targetType, Util.CSharpPrimitiveCast.Cast(code, expression.ConstantValue, checkForOverflow: true));
} catch (OverflowException) {
// If constant value doesn't fit into 32-bits, the conversion is not a compile-time constant
return new ConversionResolveResult(targetType, expression, c, checkForOverflow);
} catch (InvalidCastException) {
return new ErrorResolveResult(targetType);
}
}
}

21
ICSharpCode.Decompiler/CSharp/Syntax/TypeSystemAstBuilder.cs

@ -722,7 +722,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -722,7 +722,7 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
return ace;
} else if (rr.IsCompileTimeConstant) {
var expr = ConvertConstantValue(rr.Type, rr.ConstantValue);
if (isBoxing && rr.Type.IsCSharpSmallIntegerType()) {
if (isBoxing && (rr.Type.IsCSharpSmallIntegerType() || rr.Type.IsCSharpNativeIntegerType())) {
// C# does not have small integer literal types.
// We need to add a cast so that the integer literal gets boxed as the correct type.
expr = new CastExpression(ConvertType(rr.Type), expr);
@ -793,21 +793,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -793,21 +793,24 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
if (type.IsKnownType(KnownTypeCode.Double) || type.IsKnownType(KnownTypeCode.Single))
return ConvertFloatingPointLiteral(type, constantValue);
IType literalType = type;
bool smallInteger = type.IsCSharpSmallIntegerType();
if (smallInteger) {
bool integerTypeMismatch = type.IsCSharpSmallIntegerType() || type.IsCSharpNativeIntegerType();
if (integerTypeMismatch) {
// C# does not have integer literals of small integer types,
// use `int` literal instead.
constantValue = CSharpPrimitiveCast.Cast(TypeCode.Int32, constantValue, false);
literalType = type.GetDefinition().Compilation.FindType(KnownTypeCode.Int32);
// It also doesn't have native integer literals, those also use `int` (or `uint` for `nuint`).
bool unsigned = type.Kind == TypeKind.NUInt;
constantValue = CSharpPrimitiveCast.Cast(unsigned ? TypeCode.UInt32 : TypeCode.Int32, constantValue, false);
var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation;
literalType = compilation?.FindType(unsigned ? KnownTypeCode.UInt32 : KnownTypeCode.Int32);
}
LiteralFormat format = LiteralFormat.None;
if (PrintIntegralValuesAsHex) {
format = LiteralFormat.HexadecimalNumber;
}
expr = new PrimitiveExpression(constantValue, format);
if (AddResolveResultAnnotations)
if (AddResolveResultAnnotations && literalType != null)
expr.AddAnnotation(new ConstantResolveResult(literalType, constantValue));
if (smallInteger && !type.Equals(expectedType)) {
if (integerTypeMismatch && !type.Equals(expectedType)) {
expr = new CastExpression(ConvertType(type), expr);
}
return expr;
@ -822,7 +825,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax @@ -822,7 +825,9 @@ namespace ICSharpCode.Decompiler.CSharp.Syntax
// find IType of constant in compilation.
var constantType = expectedType;
if (!expectedType.IsKnownType(info.Type)) {
var compilation = expectedType.GetDefinition().Compilation;
var compilation = resolver?.Compilation ?? expectedType.GetDefinition()?.Compilation;
if (compilation == null)
return false;
constantType = compilation.FindType(info.Type);
}
// if the field definition cannot be found, do not generate a reference to the field.

2
ICSharpCode.Decompiler/IL/ILTypeExtensions.cs

@ -202,7 +202,7 @@ namespace ICSharpCode.Decompiler.IL @@ -202,7 +202,7 @@ namespace ICSharpCode.Decompiler.IL
case BinaryNumericOperator.BitXor:
var left = bni.Left.InferType(compilation);
var right = bni.Right.InferType(compilation);
if (left.Equals(right) && (left.IsCSharpPrimitiveIntegerType() || left.IsKnownType(KnownTypeCode.Boolean)))
if (left.Equals(right) && (left.IsCSharpPrimitiveIntegerType() || left.IsCSharpNativeIntegerType() || left.IsKnownType(KnownTypeCode.Boolean)))
return left;
else
return SpecialType.UnknownType;

16
ICSharpCode.Decompiler/TypeSystem/TypeUtils.cs

@ -133,6 +133,22 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -133,6 +133,22 @@ namespace ICSharpCode.Decompiler.TypeSystem
}
}
/// <summary>
/// Gets whether the type is a C# 9 native integer type: nint or nuint.
///
/// Returns false for (U)IntPtr.
/// </summary>
public static bool IsCSharpNativeIntegerType(this IType type)
{
switch (type.Kind) {
case TypeKind.NInt:
case TypeKind.NUInt:
return true;
default:
return false;
}
}
/// <summary>
/// Gets whether the type is a C# primitive integer type: byte, sbyte, short, ushort, int, uint, long and ulong.
///

Loading…
Cancel
Save