Browse Source

Introduce nint/nuint types and their conversions in the type system

pull/2063/head
Daniel Grunwald 5 years ago
parent
commit
450fae49e3
  1. 129
      ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs
  2. 50
      ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs
  3. 21
      ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs
  4. 16
      ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs
  5. 12
      ICSharpCode.Decompiler/TypeSystem/SpecialType.cs
  6. 9
      ICSharpCode.Decompiler/TypeSystem/TypeKind.cs

129
ICSharpCode.Decompiler.Tests/Semantics/ConversionTests.cs

@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -32,6 +32,8 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
// assign short names to the fake reflection types
using Null = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.Null;
using dynamic = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.Dynamic;
using nuint = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.NUInt;
using nint = ICSharpCode.Decompiler.TypeSystem.ReflectionHelper.NInt;
using C = Conversion;
[TestFixture, Parallelizable(ParallelScope.All)]
@ -56,6 +58,13 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -56,6 +58,13 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
return conversions.ImplicitConversion(from2, to2);
}
Conversion ExplicitConversion(Type from, Type to)
{
IType from2 = compilation.FindType(from);
IType to2 = compilation.FindType(to);
return conversions.ExplicitConversion(from2, to2);
}
[Test]
public void IdentityConversions()
{
@ -285,6 +294,126 @@ namespace ICSharpCode.Decompiler.Tests.Semantics @@ -285,6 +294,126 @@ namespace ICSharpCode.Decompiler.Tests.Semantics
Assert.AreEqual(C.None, ImplicitConversion(typeof(int*), typeof(dynamic)));
}
[Test]
public void ConversionToNInt()
{
// Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md
Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(nint)));
Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(void*), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(sbyte), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(byte), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(short), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(ushort), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(int), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(uint), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(long), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(ulong), typeof(nint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(char), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(float), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(double), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(decimal), typeof(nint)));
Assert.AreEqual(C.IdentityConversion, ExplicitConversion(typeof(IntPtr), typeof(nint)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(UIntPtr), typeof(nint)));
}
[Test]
public void ConversionToNUInt()
{
// Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md
Assert.AreEqual(C.UnboxingConversion, ExplicitConversion(typeof(object), typeof(nuint)));
Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(void*), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(sbyte), typeof(nuint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(byte), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(short), typeof(nuint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(ushort), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(int), typeof(nuint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(uint), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(long), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(ulong), typeof(nuint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(char), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(float), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(double), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(decimal), typeof(nuint)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(IntPtr), typeof(nuint)));
Assert.AreEqual(C.IdentityConversion, ExplicitConversion(typeof(UIntPtr), typeof(nuint)));
}
[Test]
public void ConversionFromNInt()
{
// Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md
Assert.AreEqual(C.BoxingConversion, ExplicitConversion(typeof(nint), typeof(object)));
Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(nint), typeof(void*)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(nuint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(sbyte)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(byte)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(short)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(ushort)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(int)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(uint)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(long)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(ulong)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(char)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(float)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(double)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nint), typeof(decimal)));
Assert.AreEqual(C.IdentityConversion, ExplicitConversion(typeof(nint), typeof(IntPtr)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(nint), typeof(UIntPtr)));
}
[Test]
public void ConversionFromNUInt()
{
// Test based on the table in https://github.com/dotnet/csharplang/blob/master/proposals/native-integers.md
Assert.AreEqual(C.BoxingConversion, ExplicitConversion(typeof(nuint), typeof(object)));
Assert.AreEqual(C.ExplicitPointerConversion, ExplicitConversion(typeof(nuint), typeof(void*)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(nint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(sbyte)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(byte)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(short)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(ushort)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(int)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(uint)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(long)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(ulong)));
Assert.AreEqual(C.ExplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(char)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(float)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(double)));
Assert.AreEqual(C.ImplicitNumericConversion, ExplicitConversion(typeof(nuint), typeof(decimal)));
Assert.AreEqual(C.None, ExplicitConversion(typeof(nuint), typeof(IntPtr)));
Assert.AreEqual(C.IdentityConversion, ExplicitConversion(typeof(nuint), typeof(UIntPtr)));
}
[Test]
public void NIntEnumConversion()
{
var explicitEnumConversion = C.EnumerationConversion(isImplicit: false, isLifted: false);
Assert.AreEqual(explicitEnumConversion, ExplicitConversion(typeof(nint), typeof(StringComparison)));
Assert.AreEqual(explicitEnumConversion, ExplicitConversion(typeof(nuint), typeof(StringComparison)));
Assert.AreEqual(explicitEnumConversion, ExplicitConversion(typeof(StringComparison), typeof(nint)));
Assert.AreEqual(explicitEnumConversion, ExplicitConversion(typeof(StringComparison), typeof(nuint)));
}
[Test]
public void IntegerLiteralToNIntConversions()
{
Assert.IsTrue(IntegerLiteralConversion(0, typeof(nint)));
Assert.IsTrue(IntegerLiteralConversion(-1, typeof(nint)));
Assert.IsFalse(IntegerLiteralConversion(uint.MaxValue, typeof(nint)));
Assert.IsFalse(IntegerLiteralConversion(long.MaxValue, typeof(nint)));
}
[Test]
public void IntegerLiteralToNUIntConversions()
{
Assert.IsTrue(IntegerLiteralConversion(0, typeof(nuint)));
Assert.IsFalse(IntegerLiteralConversion(-1, typeof(nuint)));
Assert.IsTrue(IntegerLiteralConversion(uint.MaxValue, typeof(nuint)));
Assert.IsFalse(IntegerLiteralConversion(long.MaxValue, typeof(nuint)));
}
[Test]
public void UnconstrainedTypeParameter()
{

50
ICSharpCode.Decompiler/CSharp/Resolver/CSharpConversions.cs

@ -326,10 +326,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -326,10 +326,12 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
/* char */ { false, true , true , true , true , true },
/* sbyte */ { true , false, true , false, true , false },
/* byte */ { true , true , true , true , true , true },
/* short */ { false, false, true , false, true , false },
/* ushort */ { false, false, true , true , true , true },
/* int */ { false, false, false, false, true , false },
/* uint */ { false, false, false, false, true , true },
/* short */ { true , false, true , false, true , false },
/* ushort */ { false, true , true , true , true , true },
/* int */ { false, false, true , false, true , false },
/* uint */ { false, false, false, true , true , true },
/* long */ { false, false, false, false, true , false },
/* ulong */ { false, false, false, false, false, true },
};
bool ImplicitNumericConversion(IType fromType, IType toType)
@ -337,7 +339,29 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -337,7 +339,29 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
// C# 4.0 spec: §6.1.2
TypeCode from = ReflectionHelper.GetTypeCode(fromType);
if (from == TypeCode.Empty) {
// When converting from a native-sized integer, treat it as 64-bits
switch (fromType.Kind) {
case TypeKind.NInt:
from = TypeCode.Int64;
break;
case TypeKind.NUInt:
from = TypeCode.UInt64;
break;
}
}
TypeCode to = ReflectionHelper.GetTypeCode(toType);
if (to == TypeCode.Empty) {
// When converting to a native-sized integer, only 32-bits can be stored safely
switch (toType.Kind) {
case TypeKind.NInt:
to = TypeCode.Int32;
break;
case TypeKind.NUInt:
to = TypeCode.UInt32;
break;
}
}
if (to >= TypeCode.Single && to <= TypeCode.Decimal) {
// Conversions to float/double/decimal exist from all integral types,
// and there's a conversion from float to double.
@ -345,7 +369,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -345,7 +369,7 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
|| from == TypeCode.Single && to == TypeCode.Double;
} else {
// Conversions to integral types: look at the table
return from >= TypeCode.Char && from <= TypeCode.UInt32
return from >= TypeCode.Char && from <= TypeCode.UInt64
&& to >= TypeCode.Int16 && to <= TypeCode.UInt64
&& implicitNumericConversionLookup[from - TypeCode.Char, to - TypeCode.Int16];
}
@ -353,6 +377,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -353,6 +377,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool IsNumericType(IType type)
{
switch (type.Kind) {
case TypeKind.NInt:
case TypeKind.NUInt:
return true;
}
TypeCode c = ReflectionHelper.GetTypeCode(type);
return c >= TypeCode.Char && c <= TypeCode.Decimal;
}
@ -693,7 +722,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -693,7 +722,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
return false;
// C# 4.0 spec: §6.1.9
TypeCode fromTypeCode = ReflectionHelper.GetTypeCode(rr.Type);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(NullableType.GetUnderlyingType(toType));
toType = NullableType.GetUnderlyingType(toType);
TypeCode toTypeCode = ReflectionHelper.GetTypeCode(toType);
if (toType.Kind == TypeKind.NUInt) {
toTypeCode = TypeCode.UInt32;
}
if (fromTypeCode == TypeCode.Int64) {
long val = (long)rr.ConstantValue;
return val >= 0 && toTypeCode == TypeCode.UInt64;
@ -772,6 +805,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver @@ -772,6 +805,11 @@ namespace ICSharpCode.Decompiler.CSharp.Resolver
bool IsIntegerType(IType type)
{
switch (type.Kind) {
case TypeKind.NInt:
case TypeKind.NUInt:
return true;
}
TypeCode c = ReflectionHelper.GetTypeCode(type);
return c >= TypeCode.SByte && c <= TypeCode.UInt64;
}

21
ICSharpCode.Decompiler/TypeSystem/NormalizeTypeVisitor.cs

@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -15,6 +15,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
ReplaceClassTypeParametersWithDummy = false,
ReplaceMethodTypeParametersWithDummy = false,
DynamicAndObject = true,
IntPtrToNInt = true,
TupleToUnderlyingType = true,
RemoveModOpt = true,
RemoveModReq = true,
@ -33,6 +34,7 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -33,6 +34,7 @@ namespace ICSharpCode.Decompiler.TypeSystem
public bool ReplaceClassTypeParametersWithDummy = true;
public bool ReplaceMethodTypeParametersWithDummy = true;
public bool DynamicAndObject = true;
public bool IntPtrToNInt = true;
public bool TupleToUnderlyingType = true;
public bool RemoveNullability = true;
@ -51,13 +53,18 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -51,13 +53,18 @@ namespace ICSharpCode.Decompiler.TypeSystem
public override IType VisitTypeDefinition(ITypeDefinition type)
{
if (DynamicAndObject && type.KnownTypeCode == KnownTypeCode.Object) {
// Instead of normalizing dynamic->object,
// we do this the opposite direction, so that we don't need a compilation to find the object type.
if (RemoveNullability)
return SpecialType.Dynamic;
else
return SpecialType.Dynamic.ChangeNullability(type.Nullability);
switch (type.KnownTypeCode) {
case KnownTypeCode.Object when DynamicAndObject:
// Instead of normalizing dynamic->object,
// we do this the opposite direction, so that we don't need a compilation to find the object type.
if (RemoveNullability)
return SpecialType.Dynamic;
else
return SpecialType.Dynamic.ChangeNullability(type.Nullability);
case KnownTypeCode.IntPtr when IntPtrToNInt:
return SpecialType.NInt;
case KnownTypeCode.UIntPtr when IntPtrToNInt:
return SpecialType.NUInt;
}
return base.VisitTypeDefinition(type);
}

16
ICSharpCode.Decompiler/TypeSystem/ReflectionHelper.cs

@ -36,7 +36,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -36,7 +36,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// A reflection class used to represent <c>dynamic</c>.
/// </summary>
public sealed class Dynamic {}
/// <summary>
/// A reflection class used to represent <c>nint</c>.
/// </summary>
public sealed class NInt { }
/// <summary>
/// A reflection class used to represent <c>nuint</c>.
/// </summary>
public sealed class NUInt { }
/// <summary>
/// A reflection class used to represent an unbound type argument.
/// </summary>
@ -101,6 +111,10 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -101,6 +111,10 @@ namespace ICSharpCode.Decompiler.TypeSystem
} else if (type.DeclaringType != null) {
if (type == typeof(Dynamic))
return SpecialType.Dynamic;
else if (type == typeof(NInt))
return SpecialType.NInt;
else if (type == typeof(NUInt))
return SpecialType.NUInt;
else if (type == typeof(Null))
return SpecialType.NullType;
else if (type == typeof(UnboundTypeArgument))

12
ICSharpCode.Decompiler/TypeSystem/SpecialType.cs

@ -46,7 +46,17 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -46,7 +46,17 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Type representing the C# 'dynamic' type.
/// </summary>
public readonly static SpecialType Dynamic = new SpecialType(TypeKind.Dynamic, "dynamic", isReferenceType: true);
/// <summary>
/// Type representing the C# 9 'nint' type.
/// </summary>
public readonly static SpecialType NInt = new SpecialType(TypeKind.NInt, "nint", isReferenceType: false);
/// <summary>
/// Type representing the C# 9 'nuint' type.
/// </summary>
public readonly static SpecialType NUInt = new SpecialType(TypeKind.NUInt, "nuint", isReferenceType: false);
/// <summary>
/// Type representing the result of the C# '__arglist()' expression.
/// </summary>

9
ICSharpCode.Decompiler/TypeSystem/TypeKind.cs

@ -94,5 +94,14 @@ namespace ICSharpCode.Decompiler.TypeSystem @@ -94,5 +94,14 @@ namespace ICSharpCode.Decompiler.TypeSystem
/// Modified type, with required modifier.
/// </summary>
ModReq,
/// <summary>
/// C# 9 <c>nint</c>
/// </summary>
NInt,
/// <summary>
/// C# 9 <c>nuint</c>
/// </summary>
NUInt,
}
}

Loading…
Cancel
Save