Browse Source

Fix #1680: Invalid cast `(uint?)-1`

pull/1728/head
Daniel Grunwald 6 years ago
parent
commit
1d95eb60a2
  1. 32
      ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullableTests.cs
  2. 28
      ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

32
ICSharpCode.Decompiler.Tests/TestCases/Correctness/NullableTests.cs

@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -23,6 +23,12 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
class NullableTests
{
static void Main()
{
AvoidLifting();
BitNot();
}
static void AvoidLifting()
{
Console.WriteLine("MayThrow:");
Console.WriteLine(MayThrow(10, 2, 3));
@ -33,8 +39,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -33,8 +39,8 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
Console.WriteLine(NotUsingAllInputs(5, null));
Console.WriteLine("UsingUntestedValue:");
Console.WriteLine(NotUsingAllInputs(5, 3));
Console.WriteLine(NotUsingAllInputs(5, null));
Console.WriteLine(UsingUntestedValue(5, 3));
Console.WriteLine(UsingUntestedValue(5, null));
}
static int? MayThrow(int? a, int? b, int? c)
@ -54,5 +60,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness @@ -54,5 +60,27 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Correctness
// cannot be lifted because the value differs if b == null
return a.HasValue ? a.GetValueOrDefault() + b.GetValueOrDefault() : default(int?);
}
static void BitNot()
{
UInt32? value = 0;
Assert(~value == UInt32.MaxValue);
UInt64? value2 = 0;
Assert(~value2 == UInt64.MaxValue);
UInt16? value3 = 0;
Assert((UInt16)~value3 == (UInt16)UInt16.MaxValue);
UInt32 value4 = 0;
Assert(~value4 == UInt32.MaxValue);
UInt64 value5 = 0;
Assert(~value5 == UInt64.MaxValue);
UInt16 value6 = 0;
Assert((UInt16)~value6 == UInt16.MaxValue);
}
static void Assert(bool b)
{
if (!b)
throw new InvalidOperationException();
}
}
}

28
ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs

@ -283,10 +283,9 @@ namespace ICSharpCode.Decompiler.CSharp @@ -283,10 +283,9 @@ namespace ICSharpCode.Decompiler.CSharp
UnwrapChild(uoe.Expression).ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion)
).WithRR(new ResolveResult(targetType)).WithoutILInstruction();
}
bool isLifted = type.IsKnownType(KnownTypeCode.NullableOfT) && targetType.IsKnownType(KnownTypeCode.NullableOfT);
IType utype = isLifted ? NullableType.GetUnderlyingType(type) : type;
IType targetUType = isLifted ? NullableType.GetUnderlyingType(targetType) : targetType;
if (type.IsKnownType(KnownTypeCode.Boolean) && targetType.GetStackType().IsIntegerType()) {
IType utype = NullableType.GetUnderlyingType(type);
IType targetUType = NullableType.GetUnderlyingType(targetType);
if (type.IsKnownType(KnownTypeCode.Boolean) && targetUType.GetStackType().IsIntegerType()) {
// convert from boolean to integer (or enum)
return new ConditionalExpression(
this.Expression,
@ -318,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -318,7 +317,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
}
if (targetType.IsKnownType(KnownTypeCode.IntPtr)) { // Conversion to IntPtr
if (targetUType.IsKnownType(KnownTypeCode.IntPtr)) { // Conversion to IntPtr
if (type.IsKnownType(KnownTypeCode.Int32)) {
// normal casts work for int (both in checked and unchecked context)
} else if (checkForOverflow) {
@ -336,7 +335,7 @@ namespace ICSharpCode.Decompiler.CSharp @@ -336,7 +335,7 @@ namespace ICSharpCode.Decompiler.CSharp
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
}
} else if (targetType.IsKnownType(KnownTypeCode.UIntPtr)) { // Conversion to UIntPtr
} else if (targetUType.IsKnownType(KnownTypeCode.UIntPtr)) { // Conversion to UIntPtr
if (type.IsKnownType(KnownTypeCode.UInt32) || type.Kind == TypeKind.Pointer) {
// normal casts work for uint and pointers (both in checked and unchecked context)
} else if (checkForOverflow) {
@ -359,14 +358,14 @@ namespace ICSharpCode.Decompiler.CSharp @@ -359,14 +358,14 @@ namespace ICSharpCode.Decompiler.CSharp
// -> convert via underlying type
return this.ConvertTo(type.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
} else if (targetType.Kind == TypeKind.Enum && type.Kind == TypeKind.Pointer) {
} else if (targetUType.Kind == TypeKind.Enum && type.Kind == TypeKind.Pointer) {
// pointer to enum: C# doesn't allow such casts
// -> convert via underlying type
return this.ConvertTo(targetType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
return this.ConvertTo(targetUType.GetEnumUnderlyingType(), expressionBuilder, checkForOverflow)
.ConvertTo(targetType, expressionBuilder, checkForOverflow);
}
if (targetType.Kind == TypeKind.Pointer && type.IsKnownType(KnownTypeCode.Char)
|| targetType.IsKnownType(KnownTypeCode.Char) && type.Kind == TypeKind.Pointer) {
|| targetUType.IsKnownType(KnownTypeCode.Char) && type.Kind == TypeKind.Pointer) {
// char <-> pointer: C# doesn't allow such casts
// -> convert via ushort
return this.ConvertTo(compilation.FindType(KnownTypeCode.UInt16), expressionBuilder, checkForOverflow)
@ -418,6 +417,17 @@ namespace ICSharpCode.Decompiler.CSharp @@ -418,6 +417,17 @@ namespace ICSharpCode.Decompiler.CSharp
.WithoutILInstruction()
.WithRR(new ByReferenceResolveResult(elementRR, ReferenceKind.Ref));
}
if (this.ResolveResult.IsCompileTimeConstant && this.ResolveResult.ConstantValue != null
&& NullableType.IsNullable(targetType) && !utype.Equals(targetUType))
{
// Casts like `(uint?)-1` are only valid in an explicitly unchecked context, but we
// don't have logic to ensure such a context (usually we emit into an implicitly unchecked context).
// This only applies with constants as input (int->uint? is fine in implicitly unchecked context).
// We use an intermediate cast to the nullable's underlying type, which results
// in a constant conversion, so the final output will be something like `(uint?)uint.MaxValue`
return ConvertTo(targetUType, expressionBuilder, checkForOverflow, allowImplicitConversion: false)
.ConvertTo(targetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
}
var rr = expressionBuilder.resolver.WithCheckForOverflow(checkForOverflow).ResolveCast(targetType, ResolveResult);
if (rr.IsCompileTimeConstant && !rr.IsError) {
return expressionBuilder.ConvertConstantValue(rr, allowImplicitConversion)

Loading…
Cancel
Save