diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index d00affb91..8ec9ccdf9 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -32,6 +32,201 @@ using namespace solidity; using namespace solidity::frontend; using namespace solidity::langutil; +namespace +{ + +/// Check whether (_base ** _exp) fits into 4096 bits. +bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) +{ + if (_base == 0) + return true; + + solAssert(_base > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base); + if (mostSignificantBaseBit == 0) // _base == 1 + return true; + if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 + return false; + + bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); + + return bitsNeeded <= bitsMax; +} + +/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. +bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) +{ + return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); +} + +} + +optional ConstantEvaluator::evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right) +{ + bool fractional = _left.denominator() != 1 || _right.denominator() != 1; + switch (_operator) + { + //bit operations will only be enabled for integers and fixed types that resemble integers + case Token::BitOr: + if (fractional) + return nullopt; + else + return _left.numerator() | _right.numerator(); + case Token::BitXor: + if (fractional) + return nullopt; + else + return _left.numerator() ^ _right.numerator(); + case Token::BitAnd: + if (fractional) + return nullopt; + else + return _left.numerator() & _right.numerator(); + case Token::Add: return _left + _right; + case Token::Sub: return _left - _right; + case Token::Mul: return _left * _right; + case Token::Div: + if (_right == rational(0)) + return nullopt; + else + return _left / _right; + case Token::Mod: + if (_right == rational(0)) + return nullopt; + else if (fractional) + { + rational tempValue = _left / _right; + return _left - (tempValue.numerator() / tempValue.denominator()) * _right; + } + else + return _left.numerator() % _right.numerator(); + break; + case Token::Exp: + { + if (_right.denominator() != 1) + return nullopt; + bigint const& exp = _right.numerator(); + + // x ** 0 = 1 + // for 0, 1 and -1 the size of the exponent doesn't have to be restricted + if (exp == 0) + return 1; + else if (_left == 0 || _left == 1) + return _left; + else if (_left == -1) + { + bigint isOdd = abs(exp) & bigint(1); + return 1 - 2 * isOdd.convert_to(); + } + else + { + if (abs(exp) > numeric_limits::max()) + return nullopt; // This will need too much memory to represent. + + uint32_t absExp = bigint(abs(exp)).convert_to(); + + if (!fitsPrecisionExp(abs(_left.numerator()), absExp) || !fitsPrecisionExp(abs(_left.denominator()), absExp)) + return nullopt; + + static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { + if (_base == 1) + return 1; + else if (_base == -1) + return 1 - 2 * static_cast(_exponent & 1); + else + return boost::multiprecision::pow(_base, _exponent); + }; + + bigint numerator = optimizedPow(_left.numerator(), absExp); + bigint denominator = optimizedPow(_left.denominator(), absExp); + + if (exp >= 0) + return makeRational(numerator, denominator); + else + // invert + return makeRational(denominator, numerator); + } + break; + } + case Token::SHL: + { + if (fractional) + return nullopt; + else if (_right < 0) + return nullopt; + else if (_right > numeric_limits::max()) + return nullopt; + if (_left.numerator() == 0) + return 0; + else + { + uint32_t exponent = _right.numerator().convert_to(); + if (!fitsPrecisionBase2(abs(_left.numerator()), exponent)) + return nullopt; + return _left.numerator() * boost::multiprecision::pow(bigint(2), exponent); + } + break; + } + // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue + // determines the resulting type and the type of shift (SAR or SHR). + case Token::SAR: + { + if (fractional) + return nullopt; + else if (_right < 0) + return nullopt; + else if (_right > numeric_limits::max()) + return nullopt; + if (_left.numerator() == 0) + return 0; + else + { + uint32_t exponent = _right.numerator().convert_to(); + if (exponent > boost::multiprecision::msb(boost::multiprecision::abs(_left.numerator()))) + return _left.numerator() < 0 ? -1 : 0; + else + { + if (_left.numerator() < 0) + // Add 1 to the negative value before dividing to get a result that is strictly too large, + // then subtract 1 afterwards to round towards negative infinity. + // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...). + // To see this note that for negative x, xor(x,all_ones) = (-x-1) and + // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is + // -(-x - 1) / 2^shift_amount - 1, which is the same as + // (x + 1) / 2^shift_amount - 1. + return rational((_left.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1); + else + return rational(_left.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1); + } + } + break; + } + default: + return nullopt; + } +} + +optional ConstantEvaluator::evaluateUnaryOperator(Token _operator, rational const& _input) +{ + switch (_operator) + { + case Token::BitNot: + if (_input.denominator() != 1) + return nullopt; + else + return ~_input.numerator(); + case Token::Add: + return +_input; + case Token::Sub: + return -_input; + default: + return nullopt; + } +} + void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { auto sub = type(_operation.subExpression()); diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 521f46338..a656ef46b 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -56,6 +56,14 @@ public: TypePointer evaluate(Expression const& _expr); + /// Performs arbitrary-precision evaluation of a binary operator. Returns nullopt on cases like + /// division by zero or e.g. bit operators applied to fractional values. + static std::optional evaluateBinaryOperator(Token _operator, rational const& _left, rational const& _right); + + /// Performs arbitrary-precision evaluation of a unary operator. Returns nullopt on cases like + /// bit operators applied to fractional values. + static std::optional evaluateUnaryOperator(Token _operator, rational const& _input); + private: void endVisit(BinaryOperation const& _operation) override; void endVisit(UnaryOperation const& _operation) override; diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index 82a80d974..35910ceac 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -26,6 +26,8 @@ #include #include +#include + #include #include #include @@ -56,50 +58,6 @@ using namespace solidity::frontend; namespace { -/// Check whether (_base ** _exp) fits into 4096 bits. -bool fitsPrecisionExp(bigint const& _base, bigint const& _exp) -{ - if (_base == 0) - return true; - - solAssert(_base > 0, ""); - - size_t const bitsMax = 4096; - - unsigned mostSignificantBaseBit = boost::multiprecision::msb(_base); - if (mostSignificantBaseBit == 0) // _base == 1 - return true; - if (mostSignificantBaseBit > bitsMax) // _base >= 2 ^ 4096 - return false; - - bigint bitsNeeded = _exp * (mostSignificantBaseBit + 1); - - return bitsNeeded <= bitsMax; -} - -/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, -/// where X is given indirectly via _log2OfBase = log2(X). -bool fitsPrecisionBaseX( - bigint const& _mantissa, - double _log2OfBase, - uint32_t _exp -) -{ - if (_mantissa == 0) - return true; - - solAssert(_mantissa > 0, ""); - - size_t const bitsMax = 4096; - - unsigned mostSignificantMantissaBit = boost::multiprecision::msb(_mantissa); - if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 - return false; - - bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; - return bitsNeeded <= bitsMax; -} - /// Checks whether _mantissa * (10 ** _expBase10) fits into 4096 bits. bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) { @@ -107,12 +65,6 @@ bool fitsPrecisionBase10(bigint const& _mantissa, uint32_t _expBase10) return fitsPrecisionBaseX(_mantissa, log2Of10AwayFromZero, _expBase10); } -/// Checks whether _mantissa * (2 ** _expBase10) fits into 4096 bits. -bool fitsPrecisionBase2(bigint const& _mantissa, uint32_t _expBase2) -{ - return fitsPrecisionBaseX(_mantissa, 1.0, _expBase2); -} - /// Checks whether _value fits into IntegerType _type. BoolResult fitsIntegerType(bigint const& _value, IntegerType const& _type) { @@ -1000,26 +952,10 @@ BoolResult RationalNumberType::isExplicitlyConvertibleTo(Type const& _convertTo) TypeResult RationalNumberType::unaryOperatorResult(Token _operator) const { - rational value; - switch (_operator) - { - case Token::BitNot: - if (isFractional()) - return nullptr; - value = ~m_value.numerator(); - break; - case Token::Add: - value = +(m_value); - break; - case Token::Sub: - value = -(m_value); - break; - case Token::After: - return this; - default: + if (optional value = ConstantEvaluator::evaluateUnaryOperator(_operator, m_value)) + return TypeResult{TypeProvider::rationalNumber(*value)}; + else return nullptr; - } - return TypeResult{TypeProvider::rationalNumber(value)}; } TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* _other) const @@ -1074,165 +1010,16 @@ TypeResult RationalNumberType::binaryOperatorResult(Token _operator, Type const* return nullptr; return thisMobile->binaryOperatorResult(_operator, otherMobile); } - else + else if (optional value = ConstantEvaluator::evaluateBinaryOperator(_operator, m_value, other.m_value)) { - rational value; - bool fractional = isFractional() || other.isFractional(); - switch (_operator) - { - //bit operations will only be enabled for integers and fixed types that resemble integers - case Token::BitOr: - if (fractional) - return nullptr; - value = m_value.numerator() | other.m_value.numerator(); - break; - case Token::BitXor: - if (fractional) - return nullptr; - value = m_value.numerator() ^ other.m_value.numerator(); - break; - case Token::BitAnd: - if (fractional) - return nullptr; - value = m_value.numerator() & other.m_value.numerator(); - break; - case Token::Add: - value = m_value + other.m_value; - break; - case Token::Sub: - value = m_value - other.m_value; - break; - case Token::Mul: - value = m_value * other.m_value; - break; - case Token::Div: - if (other.m_value == rational(0)) - return nullptr; - else - value = m_value / other.m_value; - break; - case Token::Mod: - if (other.m_value == rational(0)) - return nullptr; - else if (fractional) - { - rational tempValue = m_value / other.m_value; - value = m_value - (tempValue.numerator() / tempValue.denominator()) * other.m_value; - } - else - value = m_value.numerator() % other.m_value.numerator(); - break; - case Token::Exp: - { - if (other.isFractional()) - return nullptr; - solAssert(other.m_value.denominator() == 1, ""); - bigint const& exp = other.m_value.numerator(); - - // x ** 0 = 1 - // for 0, 1 and -1 the size of the exponent doesn't have to be restricted - if (exp == 0) - value = 1; - else if (m_value.numerator() == 0 || m_value == 1) - value = m_value; - else if (m_value == -1) - { - bigint isOdd = abs(exp) & bigint(1); - value = 1 - 2 * isOdd.convert_to(); - } - else - { - if (abs(exp) > numeric_limits::max()) - return nullptr; // This will need too much memory to represent. - - uint32_t absExp = bigint(abs(exp)).convert_to(); - - if (!fitsPrecisionExp(abs(m_value.numerator()), absExp) || !fitsPrecisionExp(abs(m_value.denominator()), absExp)) - return TypeResult::err("Precision of rational constants is limited to 4096 bits."); - - static auto const optimizedPow = [](bigint const& _base, uint32_t _exponent) -> bigint { - if (_base == 1) - return 1; - else if (_base == -1) - return 1 - 2 * static_cast(_exponent & 1); - else - return boost::multiprecision::pow(_base, _exponent); - }; - - bigint numerator = optimizedPow(m_value.numerator(), absExp); - bigint denominator = optimizedPow(m_value.denominator(), absExp); - - if (exp >= 0) - value = makeRational(numerator, denominator); - else - // invert - value = makeRational(denominator, numerator); - } - break; - } - case Token::SHL: - { - if (fractional) - return nullptr; - else if (other.m_value < 0) - return nullptr; - else if (other.m_value > numeric_limits::max()) - return nullptr; - if (m_value.numerator() == 0) - value = 0; - else - { - uint32_t exponent = other.m_value.numerator().convert_to(); - if (!fitsPrecisionBase2(abs(m_value.numerator()), exponent)) - return nullptr; - value = m_value.numerator() * boost::multiprecision::pow(bigint(2), exponent); - } - break; - } - // NOTE: we're using >> (SAR) to denote right shifting. The type of the LValue - // determines the resulting type and the type of shift (SAR or SHR). - case Token::SAR: - { - if (fractional) - return nullptr; - else if (other.m_value < 0) - return nullptr; - else if (other.m_value > numeric_limits::max()) - return nullptr; - if (m_value.numerator() == 0) - value = 0; - else - { - uint32_t exponent = other.m_value.numerator().convert_to(); - if (exponent > boost::multiprecision::msb(boost::multiprecision::abs(m_value.numerator()))) - value = m_value.numerator() < 0 ? -1 : 0; - else - { - if (m_value.numerator() < 0) - // Add 1 to the negative value before dividing to get a result that is strictly too large, - // then subtract 1 afterwards to round towards negative infinity. - // This is the same algorithm as used in ExpressionCompiler::appendShiftOperatorCode(...). - // To see this note that for negative x, xor(x,all_ones) = (-x-1) and - // therefore xor(div(xor(x,all_ones), exp(2, shift_amount)), all_ones) is - // -(-x - 1) / 2^shift_amount - 1, which is the same as - // (x + 1) / 2^shift_amount - 1. - value = rational((m_value.numerator() + 1) / boost::multiprecision::pow(bigint(2), exponent) - bigint(1), 1); - else - value = rational(m_value.numerator() / boost::multiprecision::pow(bigint(2), exponent), 1); - } - } - break; - } - default: - return nullptr; - } - // verify that numerator and denominator fit into 4096 bit after every operation - if (value.numerator() != 0 && max(boost::multiprecision::msb(abs(value.numerator())), boost::multiprecision::msb(abs(value.denominator()))) > 4096) + if (value->numerator() != 0 && max(boost::multiprecision::msb(abs(value->numerator())), boost::multiprecision::msb(abs(value->denominator()))) > 4096) return TypeResult::err("Precision of rational constants is limited to 4096 bits."); - return TypeResult{TypeProvider::rationalNumber(value)}; + return TypeResult{TypeProvider::rationalNumber(*value)}; } + else + return nullptr; } string RationalNumberType::richIdentifier() const diff --git a/libsolutil/CMakeLists.txt b/libsolutil/CMakeLists.txt index ea8bcdf86..76da41b08 100644 --- a/libsolutil/CMakeLists.txt +++ b/libsolutil/CMakeLists.txt @@ -3,6 +3,7 @@ set(sources AnsiColorized.h Assertions.h Common.h + Common.cpp CommonData.cpp CommonData.h CommonIO.cpp diff --git a/libsolutil/Common.cpp b/libsolutil/Common.cpp new file mode 100644 index 000000000..3f77e5d25 --- /dev/null +++ b/libsolutil/Common.cpp @@ -0,0 +1,40 @@ +/* + This file is part of solidity. + + solidity is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + solidity is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with solidity. If not, see . +*/ +// SPDX-License-Identifier: GPL-3.0 + +#include + +#include + +using namespace solidity; + +bool solidity::fitsPrecisionBaseX(bigint const& _mantissa, double _log2OfBase, uint32_t _exp) +{ + if (_mantissa == 0) + return true; + + solAssert(_mantissa > 0, ""); + + size_t const bitsMax = 4096; + + unsigned mostSignificantMantissaBit = boost::multiprecision::msb(_mantissa); + if (mostSignificantMantissaBit > bitsMax) // _mantissa >= 2 ^ 4096 + return false; + + bigint bitsNeeded = mostSignificantMantissaBit + bigint(floor(double(_exp) * _log2OfBase)) + 1; + return bitsNeeded <= bitsMax; +} diff --git a/libsolutil/Common.h b/libsolutil/Common.h index aae2fa859..659ea2e3e 100644 --- a/libsolutil/Common.h +++ b/libsolutil/Common.h @@ -107,6 +107,10 @@ inline u256 exp256(u256 _base, u256 _exponent) return result; } +/// Checks whether _mantissa * (X ** _exp) fits into 4096 bits, +/// where X is given indirectly via _log2OfBase = log2(X). +bool fitsPrecisionBaseX(bigint const& _mantissa, double _log2OfBase, uint32_t _exp); + inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes) { std::ostringstream ss; diff --git a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol index 4a9fc7349..2d7fbdfad 100644 --- a/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol +++ b/test/libsolidity/syntaxTests/types/rational_number_exp_limit_fail.sol @@ -19,7 +19,7 @@ contract c { } } // ---- -// TypeError 2271: (71-112): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4. Precision of rational constants is limited to 4096 bits. +// TypeError 2271: (71-112): Operator ** not compatible with types int_const 1797...(301 digits omitted)...7216 and int_const 4 // TypeError 7407: (71-112): Type int_const 1797...(301 digits omitted)...7216 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. // TypeError 2271: (135-151): Operator ** not compatible with types int_const 4 and int_const 1157...(70 digits omitted)...9936 // TypeError 7407: (126-169): Type int_const 1340...(147 digits omitted)...4096 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. @@ -29,13 +29,13 @@ contract c { // TypeError 2271: (258-270): Operator ** not compatible with types int_const -2 and int_const 1000...(1226 digits omitted)...0000 // TypeError 2271: (284-296): Operator ** not compatible with types int_const 2 and int_const -100...(1227 digits omitted)...0000 // TypeError 2271: (310-323): Operator ** not compatible with types int_const -2 and int_const -100...(1227 digits omitted)...0000 -// TypeError 2271: (337-348): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits. +// TypeError 2271: (337-348): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 2 // TypeError 7407: (337-348): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. -// TypeError 2271: (362-374): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2. Precision of rational constants is limited to 4096 bits. +// TypeError 2271: (362-374): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const 2 // TypeError 7407: (362-374): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. -// TypeError 2271: (388-400): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits. +// TypeError 2271: (388-400): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const -2 // TypeError 7407: (388-400): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. -// TypeError 2271: (414-427): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2. Precision of rational constants is limited to 4096 bits. +// TypeError 2271: (414-427): Operator ** not compatible with types int_const -100...(1227 digits omitted)...0000 and int_const -2 // TypeError 7407: (414-427): Type int_const -100...(1227 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256. // TypeError 2271: (441-457): Operator ** not compatible with types int_const 1000...(1226 digits omitted)...0000 and int_const 1000...(1226 digits omitted)...0000 // TypeError 7407: (441-457): Type int_const 1000...(1226 digits omitted)...0000 is not implicitly convertible to expected type int256. Literal is too large to fit in int256.