mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10321 from ethereum/reimplementConstantEvaluator
Reimplement constant evaluator.
This commit is contained in:
commit
06bca65222
@ -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``
|
||||
|
||||
|
||||
|
@ -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
|
||||
================
|
||||
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(),
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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
40
libsolutil/Common.cpp
Normal 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;
|
||||
}
|
@ -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;
|
||||
|
@ -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
|
@ -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
|
@ -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
|
@ -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).
|
||||
|
@ -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.
|
||||
|
@ -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).
|
||||
|
@ -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.
|
@ -0,0 +1,8 @@
|
||||
contract C {
|
||||
int8 constant a = -7;
|
||||
function f() public pure {
|
||||
uint[-a] memory x;
|
||||
x[0] = 2;
|
||||
}
|
||||
}
|
||||
// ----
|
@ -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.
|
@ -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.
|
@ -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
|
||||
|
@ -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.
|
@ -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.
|
||||
|
Loading…
Reference in New Issue
Block a user