diff --git a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs index 95f8e6269..7cd3368e6 100644 --- a/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs +++ b/ICSharpCode.Decompiler.Tests/TestCases/Pretty/TypeAnalysisTests.cs @@ -106,9 +106,21 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public uint RShiftByteWithZeroExtension(byte num) { + // zero extend -> cast to unsigned -> unsigned shift return (uint)num >> 8; } + public int RShiftByteWithZeroExtensionReturnAsSigned(byte num) + { +#if CS110 + // zero extend -> unsigned shift + return num >>> 8; +#else + // zero extend -> cast to unsigned -> unsigned shift -> cast to signed + return (int)((uint)num >> 8); +#endif + } + public int RShiftByteAsSByte(byte num) { return (sbyte)num >> 8; @@ -121,9 +133,25 @@ namespace ICSharpCode.Decompiler.Tests.TestCases.Pretty public uint RShiftSByteWithZeroExtension(sbyte num) { - return (uint)num >> 8; + return (uint)((byte)num >> 4); } + public uint RShiftSByteWithSignExtension(sbyte num) + { + // sign extend -> cast to unsigned -> unsigned shift + return (uint)num >> 4; + } + + public int RShiftSByteWithSignExtensionReturnAsSigned(sbyte num) + { +#if CS110 + // sign extend -> unsigned shift + return num >>> 4; +#else + // sign extend -> cast to unsigned -> unsigned shift -> cast to signed + return (int)((uint)num >> 4); +#endif + } public int RShiftSByteAsByte(sbyte num) { return (byte)num >> 8; diff --git a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs index 20a1c7d8e..6c0e30200 100644 --- a/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs +++ b/ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs @@ -1208,9 +1208,9 @@ namespace ICSharpCode.Decompiler.CSharp case BinaryNumericOperator.BitXor: return HandleBinaryNumeric(inst, BinaryOperatorType.ExclusiveOr, context); case BinaryNumericOperator.ShiftLeft: - return HandleShift(inst, BinaryOperatorType.ShiftLeft); + return HandleShift(inst, BinaryOperatorType.ShiftLeft, context); case BinaryNumericOperator.ShiftRight: - return HandleShift(inst, BinaryOperatorType.ShiftRight); + return HandleShift(inst, BinaryOperatorType.ShiftRight, context); default: throw new ArgumentOutOfRangeException(); } @@ -1736,7 +1736,7 @@ namespace ICSharpCode.Decompiler.CSharp } } - TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op) + TranslatedExpression HandleShift(BinaryNumericInstruction inst, BinaryOperatorType op, TranslationContext context) { var left = Translate(inst.Left); var right = Translate(inst.Right); @@ -1748,6 +1748,8 @@ namespace ICSharpCode.Decompiler.CSharp bool couldUseUnsignedRightShift = ( sign == Sign.Unsigned && op == BinaryOperatorType.ShiftRight && settings.UnsignedRightShift && (leftUType.IsCSharpPrimitiveIntegerType() || leftUType.IsCSharpNativeIntegerType()) + // If we need to cast to unsigned anyway, don't use >>> operator. + && context.TypeHint.GetSign() != Sign.Unsigned ); if (leftUType.IsCSharpSmallIntegerType() && inst.UnderlyingResultType == StackType.I4 && (sign != Sign.Unsigned || couldUseUnsignedRightShift)) @@ -1760,7 +1762,7 @@ namespace ICSharpCode.Decompiler.CSharp op = BinaryOperatorType.UnsignedShiftRight; } } - else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize()) + else if (couldUseUnsignedRightShift && leftUType.GetSize() == inst.UnderlyingResultType.GetSize() && leftUType.GetSign() == Sign.Signed) { // Use C# 11 unsigned right shift operator. We don't need any casts in this case. op = BinaryOperatorType.UnsignedShiftRight;