Skip to content

Commit

Permalink
Fix #1680: Invalid cast (uint?)-1
Browse files Browse the repository at this point in the history
  • Loading branch information
dgrunwald committed Sep 28, 2019
1 parent 83ea344 commit 1d95eb6
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 11 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand All @@ -33,8 +39,8 @@ static void Main()
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)
Expand All @@ -54,5 +60,27 @@ static void Main()
// 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 changes: 19 additions & 9 deletions ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -283,10 +283,9 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
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,
Expand Down Expand Up @@ -318,7 +317,7 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
.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) {
Expand All @@ -336,7 +335,7 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
.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) {
Expand All @@ -359,14 +358,14 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
// -> 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)
Expand Down Expand Up @@ -418,6 +417,17 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
.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)
Expand Down

0 comments on commit 1d95eb6

Please sign in to comment.