Merge pull request #10321 from ethereum/reimplementConstantEvaluator

Reimplement constant evaluator.
This commit is contained in:
Daniel Kirchner 2020-12-04 16:31:31 +01:00 committed by GitHub
commit 06bca65222
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 534 additions and 324 deletions

View File

@ -24,8 +24,10 @@ Breaking Changes:
Language Features:
* Super constructors can now be called using the member notation e.g. ``M.C(123)``.
Bugfixes:
* Type Checker: Perform proper integer arithmetic when using constants in array length expressions.
AST Changes:
AST Changes:
* New AST Node ``IdentifierPath`` replacing in many places the ``UserDefinedTypeName``

View File

@ -35,6 +35,10 @@ the compiler notifying you about it.
* If a byte array in storage is accessed whose length is encoded incorrectly, a panic is caused.
A contract cannot get into this situation unless inline assembly is used to modify the raw representation of storage byte arrays.
* If constants are used in array length expressions, previous versions of Solidity would use arbitrary precision
in all branches of the evaluation tree. Now, if constant variables are used as intermediate expressions,
their values will be properly rounded in the same way as when they are used in run-time expressions.
New Restrictions
================

View File

@ -32,85 +32,357 @@ using namespace solidity;
using namespace solidity::frontend;
using namespace solidity::langutil;
using TypedRational = ConstantEvaluator::TypedRational;
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<rational> 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<int>();
}
else
{
if (abs(exp) > numeric_limits<uint32_t>::max())
return nullopt; // This will need too much memory to represent.
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
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<int>(_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<uint32_t>::max())
return nullopt;
if (_left.numerator() == 0)
return 0;
else
{
uint32_t exponent = _right.numerator().convert_to<uint32_t>();
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<uint32_t>::max())
return nullopt;
if (_left.numerator() == 0)
return 0;
else
{
uint32_t exponent = _right.numerator().convert_to<uint32_t>();
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<rational> ConstantEvaluator::evaluateUnaryOperator(Token _operator, rational const& _input)
{
switch (_operator)
{
case Token::BitNot:
if (_input.denominator() != 1)
return nullopt;
else
return ~_input.numerator();
case Token::Sub:
return -_input;
default:
return nullopt;
}
}
optional<TypedRational> convertType(rational const& _value, Type const& _type)
{
if (_type.category() == Type::Category::RationalNumber)
return TypedRational{TypeProvider::rationalNumber(_value), _value};
else if (auto const* integerType = dynamic_cast<IntegerType const*>(&_type))
{
if (_value > integerType->maxValue() || _value < integerType->minValue())
return nullopt;
else
return TypedRational{&_type, _value.numerator() / _value.denominator()};
}
else
return nullopt;
}
optional<TypedRational> convertType(optional<TypedRational> const& _value, Type const& _type)
{
return _value ? convertType(_value->value, _type) : nullopt;
}
optional<TypedRational> constantToTypedValue(Type const& _type)
{
if (_type.category() == Type::Category::RationalNumber)
return TypedRational{&_type, dynamic_cast<RationalNumberType const&>(_type).value()};
else
return nullopt;
}
optional<TypedRational> ConstantEvaluator::evaluate(
langutil::ErrorReporter& _errorReporter,
Expression const& _expr
)
{
return ConstantEvaluator{_errorReporter}.evaluate(_expr);
}
optional<TypedRational> ConstantEvaluator::evaluate(ASTNode const& _node)
{
if (!m_values.count(&_node))
{
if (auto const* varDecl = dynamic_cast<VariableDeclaration const*>(&_node))
{
solAssert(varDecl->isConstant(), "");
if (!varDecl->value())
m_values[&_node] = nullopt;
else
{
m_depth++;
if (m_depth > 32)
m_errorReporter.fatalTypeError(
5210_error,
varDecl->location(),
"Cyclic constant definition (or maximum recursion depth exhausted)."
);
m_values[&_node] = convertType(evaluate(*varDecl->value()), *varDecl->type());
m_depth--;
}
}
else if (auto const* expression = dynamic_cast<Expression const*>(&_node))
{
expression->accept(*this);
if (!m_values.count(&_node))
m_values[&_node] = nullopt;
}
}
return m_values.at(&_node);
}
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
{
auto sub = type(_operation.subExpression());
if (sub)
setType(_operation, sub->unaryOperatorResult(_operation.getOperator()));
optional<TypedRational> value = evaluate(_operation.subExpression());
if (!value)
return;
TypePointer resultType = value->type->unaryOperatorResult(_operation.getOperator());
if (!resultType)
return;
value = convertType(value, *resultType);
if (!value)
return;
if (optional<rational> result = evaluateUnaryOperator(_operation.getOperator(), value->value))
{
optional<TypedRational> convertedValue = convertType(*result, *resultType);
if (!convertedValue)
m_errorReporter.fatalTypeError(
3667_error,
_operation.location(),
"Arithmetic error when computing constant value."
);
m_values[&_operation] = convertedValue;
}
}
void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
{
auto left = type(_operation.leftExpression());
auto right = type(_operation.rightExpression());
if (left && right)
optional<TypedRational> left = evaluate(_operation.leftExpression());
optional<TypedRational> right = evaluate(_operation.rightExpression());
if (!left || !right)
return;
// If this is implemented in the future: Comparison operators have a "binaryOperatorResult"
// that is non-bool, but the result has to be bool.
if (TokenTraits::isCompareOp(_operation.getOperator()))
return;
TypePointer resultType = left->type->binaryOperatorResult(_operation.getOperator(), right->type);
if (!resultType)
{
TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right);
if (!commonType)
m_errorReporter.fatalTypeError(
6020_error,
_operation.location(),
"Operator " +
string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
left->toString() +
" and " +
right->toString()
m_errorReporter.fatalTypeError(
6020_error,
_operation.location(),
"Operator " +
string(TokenTraits::toString(_operation.getOperator())) +
" not compatible with types " +
left->type->toString() +
" and " +
right->type->toString()
);
setType(
_operation,
TokenTraits::isCompareOp(_operation.getOperator()) ?
TypeProvider::boolean() :
commonType
);
return;
}
left = convertType(left, *resultType);
right = convertType(right, *resultType);
if (!left || !right)
return;
if (optional<rational> value = evaluateBinaryOperator(_operation.getOperator(), left->value, right->value))
{
optional<TypedRational> convertedValue = convertType(*value, *resultType);
if (!convertedValue)
m_errorReporter.fatalTypeError(
2643_error,
_operation.location(),
"Arithmetic error when computing constant value."
);
m_values[&_operation] = convertedValue;
}
}
void ConstantEvaluator::endVisit(Literal const& _literal)
{
setType(_literal, TypeProvider::forLiteral(_literal));
if (Type const* literalType = TypeProvider::forLiteral(_literal))
m_values[&_literal] = constantToTypedValue(*literalType);
}
void ConstantEvaluator::endVisit(Identifier const& _identifier)
{
VariableDeclaration const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
if (!variableDeclaration)
return;
if (!variableDeclaration->isConstant())
return;
ASTPointer<Expression> const& value = variableDeclaration->value();
if (!value)
return;
else if (!m_types->count(value.get()))
{
if (m_depth > 32)
m_errorReporter.fatalTypeError(5210_error, _identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
}
setType(_identifier, type(*value));
if (variableDeclaration && variableDeclaration->isConstant())
m_values[&_identifier] = evaluate(*variableDeclaration);
}
void ConstantEvaluator::endVisit(TupleExpression const& _tuple)
{
if (!_tuple.isInlineArray() && _tuple.components().size() == 1)
setType(_tuple, type(*_tuple.components().front()));
}
void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type)
{
if (_type && _type->category() == Type::Category::RationalNumber)
(*m_types)[&_node] = _type;
}
TypePointer ConstantEvaluator::type(ASTNode const& _node)
{
return (*m_types)[&_node];
}
TypePointer ConstantEvaluator::evaluate(Expression const& _expr)
{
_expr.accept(*this);
return type(_expr);
m_values[&_tuple] = evaluate(*_tuple.components().front());
}

View File

@ -39,37 +39,48 @@ class TypeChecker;
/**
* Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants.
*
* Note: This always use "checked arithmetic" in the sense that any over- or underflow
* results in "unknown" value.
*/
class ConstantEvaluator: private ASTConstVisitor
{
public:
ConstantEvaluator(
langutil::ErrorReporter& _errorReporter,
size_t _newDepth = 0,
std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>()
):
m_errorReporter(_errorReporter),
m_depth(_newDepth),
m_types(std::move(_types))
struct TypedRational
{
}
TypePointer type;
rational value;
};
TypePointer evaluate(Expression const& _expr);
static std::optional<TypedRational> evaluate(
langutil::ErrorReporter& _errorReporter,
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<rational> 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<rational> evaluateUnaryOperator(Token _operator, rational const& _input);
private:
explicit ConstantEvaluator(langutil::ErrorReporter& _errorReporter): m_errorReporter(_errorReporter) {}
std::optional<TypedRational> evaluate(ASTNode const& _node);
void endVisit(BinaryOperation const& _operation) override;
void endVisit(UnaryOperation const& _operation) override;
void endVisit(Literal const& _literal) override;
void endVisit(Identifier const& _identifier) override;
void endVisit(TupleExpression const& _tuple) override;
void setType(ASTNode const& _node, TypePointer const& _type);
TypePointer type(ASTNode const& _node);
langutil::ErrorReporter& m_errorReporter;
/// Current recursion depth.
size_t m_depth = 0;
std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types;
/// Values of sub-expressions and variable declarations.
std::map<ASTNode const*, std::optional<TypedRational>> m_values;
};
}

View File

@ -265,26 +265,30 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
solAssert(baseType->storageBytes() != 0, "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length())
{
TypePointer& lengthTypeGeneric = length->annotation().type;
if (!lengthTypeGeneric)
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
u256 lengthValue = 0;
if (!lengthType || !lengthType->mobileType())
optional<rational> lengthValue;
if (length->annotation().type && length->annotation().type->category() == Type::Category::RationalNumber)
lengthValue = dynamic_cast<RationalNumberType const&>(*length->annotation().type).value();
else if (optional<ConstantEvaluator::TypedRational> value = ConstantEvaluator::evaluate(m_errorReporter, *length))
lengthValue = value->value;
if (!lengthValue || lengthValue > TypeProvider::uint256()->max())
m_errorReporter.typeError(
5462_error,
length->location(),
"Invalid array length, expected integer literal or constant expression."
);
else if (lengthType->isZero())
else if (*lengthValue == 0)
m_errorReporter.typeError(1406_error, length->location(), "Array with zero length specified.");
else if (lengthType->isFractional())
else if (lengthValue->denominator() != 1)
m_errorReporter.typeError(3208_error, length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
else if (*lengthValue < 0)
m_errorReporter.typeError(3658_error, length->location(), "Array with negative length specified.");
else
lengthValue = lengthType->literalValue(nullptr);
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
_typeName.annotation().type = TypeProvider::array(
DataLocation::Storage,
baseType,
lengthValue ? u256(lengthValue->numerator()) : u256(0)
);
}
else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);

View File

@ -290,10 +290,8 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation)
*_operation.rightExpression().annotation().isPure &&
(_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod)
)
if (auto rhs = dynamic_cast<RationalNumberType const*>(
ConstantEvaluator(m_errorReporter).evaluate(_operation.rightExpression())
))
if (rhs->isZero())
if (auto rhs = ConstantEvaluator::evaluate(m_errorReporter, _operation.rightExpression()))
if (rhs->value == 0)
m_errorReporter.typeError(
1211_error,
_operation.location(),
@ -313,10 +311,8 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall)
{
solAssert(_functionCall.arguments().size() == 3, "");
if (*_functionCall.arguments()[2]->annotation().isPure)
if (auto lastArg = dynamic_cast<RationalNumberType const*>(
ConstantEvaluator(m_errorReporter).evaluate(*(_functionCall.arguments())[2])
))
if (lastArg->isZero())
if (auto lastArg = ConstantEvaluator::evaluate(m_errorReporter, *(_functionCall.arguments())[2]))
if (lastArg->value == 0)
m_errorReporter.typeError(
4195_error,
_functionCall.location(),

View File

@ -26,6 +26,8 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolutil/Algorithms.h>
#include <libsolutil/CommonData.h>
#include <libsolutil/CommonIO.h>
@ -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<rational> 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<rational> 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<int>();
}
else
{
if (abs(exp) > numeric_limits<uint32_t>::max())
return nullptr; // This will need too much memory to represent.
uint32_t absExp = bigint(abs(exp)).convert_to<uint32_t>();
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<int>(_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<uint32_t>::max())
return nullptr;
if (m_value.numerator() == 0)
value = 0;
else
{
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
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<uint32_t>::max())
return nullptr;
if (m_value.numerator() == 0)
value = 0;
else
{
uint32_t exponent = other.m_value.numerator().convert_to<uint32_t>();
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

View File

@ -568,6 +568,11 @@ public:
u256 literalValue(Literal const* _literal) const override;
TypePointer mobileType() const override;
/// @returns the underlying raw literal value.
///
/// @see literalValue(Literal const*))
rational const& value() const noexcept { return m_value; }
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
IntegerType const* integerType() const;
/// @returns the smallest fixed type that can hold the value or incurs the least precision loss,

View File

@ -2,6 +2,7 @@ set(sources
Algorithms.h
AnsiColorized.h
Assertions.h
Common.cpp
Common.h
CommonData.cpp
CommonData.h

40
libsolutil/Common.cpp Normal file
View File

@ -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 <http://www.gnu.org/licenses/>.
*/
// SPDX-License-Identifier: GPL-3.0
#include <libsolutil/Common.h>
#include <liblangutil/Exceptions.h>
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;
}

View File

@ -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;

View File

@ -0,0 +1,11 @@
contract C {
function f() public pure returns (int, int) {
int x = int((-(-5.2 % 3)) * 5);
int t = 5;
return (x, (-(-t % 3)) * 5);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 11, 10

View File

@ -0,0 +1,15 @@
contract C {
int constant a = 7;
int constant b = 3;
int constant c = a / b;
int constant d = (-a) / b;
function f() public pure returns (uint, int, uint, int) {
uint[c] memory x;
uint[-d] memory y;
return (x.length, c, y.length, -d);
}
}
// ====
// compileViaYul: also
// ----
// f() -> 2, 2, 2, 2

View File

@ -0,0 +1,14 @@
contract C {
uint constant a = 12;
uint constant b = 10;
function f() public pure returns (uint, uint) {
uint[(a / b) * b] memory x;
return (x.length, (a / b) * b);
}
}
// ====
// compileViaYul: true
// ----
// constructor() ->
// f() -> 0x0a, 0x0a

View File

@ -7,4 +7,4 @@ contract C {
}
}
// ----
// TypeError 5210: (36-39): Cyclic constant definition (or maximum recursion depth exhausted).
// TypeError 5210: (17-44): Cyclic constant definition (or maximum recursion depth exhausted).

View File

@ -3,4 +3,4 @@ contract C {
uint[L] ids;
}
// ----
// TypeError 3208: (51-52): Array with fractional length specified.
// TypeError 5462: (51-52): Invalid array length, expected integer literal or constant expression.

View File

@ -5,4 +5,4 @@ contract C {
}
}
// ----
// TypeError 5210: (37-40): Cyclic constant definition (or maximum recursion depth exhausted).
// TypeError 5210: (17-40): Cyclic constant definition (or maximum recursion depth exhausted).

View File

@ -0,0 +1,9 @@
contract C {
uint8 constant a = 255;
uint16 constant b = a + 2;
function f() public pure {
uint[b] memory x;
}
}
// ----
// TypeError 2643: (65-70): Arithmetic error when computing constant value.

View File

@ -0,0 +1,8 @@
contract C {
int8 constant a = -7;
function f() public pure {
uint[-a] memory x;
x[0] = 2;
}
}
// ----

View File

@ -0,0 +1,8 @@
contract C {
uint8 constant a = 0;
function f() public pure {
uint[a - 1] memory x;
}
}
// ----
// TypeError 2643: (83-88): Arithmetic error when computing constant value.

View File

@ -0,0 +1,8 @@
contract C {
int8 constant a = -128;
function f() public pure {
uint[-a] memory x;
}
}
// ----
// TypeError 3667: (85-87): Arithmetic error when computing constant value.

View File

@ -7,3 +7,4 @@ contract test {
}
// ----
// SyntaxError 9636: (70-75): Use of unary + is disallowed.
// TypeError 4907: (70-75): Unary operator + cannot be applied to type rational_const 13 / 4

View File

@ -0,0 +1,10 @@
contract test {
uint constant a = 7;
uint constant b = 3;
function f() public {
uint[a / b] memory x; x[0];
uint[7 / 3] memory y; y[0];
}
}
// ----
// TypeError 3208: (141-146): Array with fractional length specified.

View File

@ -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.