diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index f094988d9bd6..4e7c986d5277 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -275,6 +275,30 @@ TypedValue convertType(TypedValue const& _value, Type const& _type) }, _value.value); } +TypedValue cleanupValue(rational const& _value, Type const& _type) +{ + if (_type.category() != Type::Category::Integer) + return convertType(_value, _type); + + auto const* integerType = dynamic_cast(&_type); + solAssert(integerType); + + solAssert(_value.denominator() == 1); + bigint integerValue = _value.numerator() / _value.denominator(); + + unsigned int numBits = integerType->numBits(); + bigint mask = (bigint(1) << numBits) - 1; + bigint sign = bigint(1) << (numBits - 1); + // clean bits out of range + integerValue = integerValue & mask; + + // extend sign if needed + if (integerType->isSigned() && (integerValue & sign)) + integerValue = integerValue | ~mask; + + return convertType(rational(integerValue), _type); +} + TypedValue constantToTypedValue(Type const& _type) { if (_type.category() == Type::Category::RationalNumber) @@ -411,7 +435,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) std::get(right.value) )) { - TypedValue convertedValue = convertType(*value, *resultType); + TypedValue convertedValue = TokenTraits::isShiftOp(_operation.getOperator()) ? + cleanupValue(*value, *resultType) : + convertType(*value, *resultType); if (!convertedValue.type) m_errorReporter.fatalTypeError( 2643_error, diff --git a/test/libsolidity/semanticTests/constantEvaluator/shift_left_literal_overflow_equivalence.sol b/test/libsolidity/semanticTests/constantEvaluator/shift_left_literal_overflow_equivalence.sol new file mode 100644 index 000000000000..788d27868a82 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/shift_left_literal_overflow_equivalence.sol @@ -0,0 +1,13 @@ +contract C { + uint constant ONE = 1; + uint constant OVERFLOW = 2**255 << ONE; + + uint[OVERFLOW + 1] a; + + function testEquivalence() public view returns (bool) { + uint runTimeResult = 2**255 << ONE; + return OVERFLOW == runTimeResult; + } +} +// ---- +// testEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/shift_left_runtime_equivalence.sol b/test/libsolidity/semanticTests/constantEvaluator/shift_left_runtime_equivalence.sol new file mode 100644 index 000000000000..7626949e3229 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/shift_left_runtime_equivalence.sol @@ -0,0 +1,22 @@ +uint8 constant UNSIGNED = 16; +int8 constant SIGNED = 16; +contract C { + // should be 0 + uint8 constant public URESULT = UNSIGNED << 5; + int8 constant public SRESULT = SIGNED << 2; + // use as array length so constant values are evaluated in comptime + uint[URESULT + 1] a; + uint[SRESULT] b; + + function testEquivalence() public view returns (bool) { + uint8 runTimeUResult = UNSIGNED << 5; + int8 runTimeSResult = SIGNED << 2; + + return + URESULT == runTimeUResult && + SRESULT == runTimeSResult + ; + } +} +// ---- +// testEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/shift_left_signed_cleanup_equivalence.sol b/test/libsolidity/semanticTests/constantEvaluator/shift_left_signed_cleanup_equivalence.sol new file mode 100644 index 000000000000..d7db60de6b9c --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/shift_left_signed_cleanup_equivalence.sol @@ -0,0 +1,23 @@ +contract C { + int8 constant NEG = -63; + int8 constant POS = 63; + // should be 4 -> 1100 0001 << 2 = 0000 0100 + int8 constant public NLEFT_OPERAND = NEG << 2; + // should be -4 -> 0011 1111 << 2 = 1111 1100 + int8 constant public PLEFT_OPERAND = POS << 2; + // use as array length so constant values are evaluated in compTime + uint[NLEFT_OPERAND] a; + uint[PLEFT_OPERAND * -1] b; + + function testEquivalence() public view returns (bool) { + int8 runTimeNResult = NEG << 2; + int8 runTimePResult = POS << 2; + + return + NLEFT_OPERAND == runTimeNResult && + PLEFT_OPERAND == runTimePResult + ; + } +} +// ---- +// testEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/shift_signed_left_operand_runtime_equivalence.sol b/test/libsolidity/semanticTests/constantEvaluator/shift_signed_left_operand_runtime_equivalence.sol new file mode 100644 index 000000000000..47b698396704 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/shift_signed_left_operand_runtime_equivalence.sol @@ -0,0 +1,53 @@ +uint256 constant ONE = 1; +int8 constant I8_NEGATIVE_63 = -63; +int8 constant I8_POSITIVE_127 = 127; +int16 constant I16_POSITIVE_127 = 127; + +contract C { + // right side cannot be signed + int256 constant LITERAL_WRAP = -2**255 << ONE; // = 0 + uint[LITERAL_WRAP + 1] a; + int8 constant CONST_NO_WRAP = I8_NEGATIVE_63 << 1; + uint[CONST_NO_WRAP * -1] b; + int8 constant CONST_WRAP = I8_POSITIVE_127 << 1; // = -2 (1111 1110) + uint[CONST_WRAP * -1] c; + int16 constant CONST_SIGN_CHANGED = I16_POSITIVE_127 << 9; // = -512 (1111 1110 0000 0000) + uint[CONST_SIGN_CHANGED * -1] d; + + function testLiteralWrapEquivalence() public view returns (bool) { + int256 runTimeResult = -2**255 << ONE; + + return + LITERAL_WRAP == runTimeResult && + a.length == 1; + } + + function testConstNoWrapEquivalence() public view returns (bool) { + int8 runTimeResult = I8_NEGATIVE_63 << 1; + + return + CONST_NO_WRAP == runTimeResult && + b.length == 126; + } + + function testConstWrapEquivalence() public view returns (bool) { + int8 runTimeResult = I8_POSITIVE_127 << 1; + + return + CONST_WRAP == runTimeResult && + c.length == 2; + } + + function testConstSignChanged() public view returns (bool) { + int16 runTimeResult = I16_POSITIVE_127 << 9; + + return + CONST_SIGN_CHANGED == runTimeResult && + d.length == 512; + } +} +// ---- +// testLiteralWrapEquivalence() -> true +// testConstNoWrapEquivalence() -> true +// testConstWrapEquivalence() -> true +// testConstSignChanged() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/shift_unsigned_left_operand_runtime_equivalence.sol b/test/libsolidity/semanticTests/constantEvaluator/shift_unsigned_left_operand_runtime_equivalence.sol new file mode 100644 index 000000000000..ca054425d331 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/shift_unsigned_left_operand_runtime_equivalence.sol @@ -0,0 +1,43 @@ +uint256 constant ONE = 1; +uint8 constant U8_64 = 64; +uint8 constant U8_255 = 255; + +contract C { + // Expression with only literals have rational type with unlimited precision, + // so we use a integer constant to force the literal have uint256 (mobileType). + // The whole expression then has type uint256. + uint256 constant LITERAL_WRAP = 2**255 << ONE; // = 0 + uint[LITERAL_WRAP + 1] a; + uint8 constant CONST_NO_WRAP = U8_64 << 1; + uint[CONST_NO_WRAP] b; + uint8 constant CONST_WRAP = U8_255 << 4; // = 240 (1111 0000) + uint[CONST_WRAP] c; + + function testLiteralWrapEquivalence() public view returns (bool) { + uint256 runTimeResult = 2**255 << ONE; + + return + LITERAL_WRAP == runTimeResult && + a.length == runTimeResult + 1; + } + + function testConstNoWrapEquivalence() public view returns (bool) { + uint8 runTimeResult = U8_64 << 1; + + return + CONST_NO_WRAP == runTimeResult && + b.length == runTimeResult; + } + + function testConstWrapEquivalence() public view returns (bool) { + uint8 runTimeResult = U8_255 << 4; + + return + CONST_WRAP == runTimeResult && + c.length == runTimeResult; + } +} +// ---- +// testLiteralWrapEquivalence() -> true +// testConstNoWrapEquivalence() -> true +// testConstWrapEquivalence() -> true