diff --git a/Changelog.md b/Changelog.md index 002276b6d..818d98862 100644 --- a/Changelog.md +++ b/Changelog.md @@ -29,6 +29,7 @@ Bugfixes: * SMTChecker: Fix internal error when ``array.push()`` is used as the LHS of an assignment. * SMTChecker: Fix CHC false positives when branches are used inside modifiers. * Code generator: Fix missing creation dependency tracking for abstract contracts. + * Constant Evaluator: Fix evaluation integer type operations. ### 0.7.4 (2020-10-19) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index d00affb91..cd5672a37 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -34,15 +34,16 @@ using namespace solidity::langutil; void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { - auto sub = type(_operation.subExpression()); + auto sub = type(_operation.subExpression()).value; if (sub) setType(_operation, sub->unaryOperatorResult(_operation.getOperator())); } void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { - auto left = type(_operation.leftExpression()); - auto right = type(_operation.rightExpression()); + auto left = type(_operation.leftExpression()).value; + auto right = type(_operation.rightExpression()).value; + if (left && right) { TypePointer commonType = left->binaryOperatorResult(_operation.getOperator(), right); @@ -57,6 +58,24 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) " and " + right->toString() ); + + if (auto const rationalCommonType = dynamic_cast(commonType)) + { + auto const leftType = type(_operation.leftExpression()).type; + auto const rightType = type(_operation.rightExpression()).type; + auto const leftInteger = leftType && leftType->category() == Type::Category::Integer; + auto const rightInteger = rightType && rightType->category() == Type::Category::Integer; + if (leftInteger || rightInteger) + { + rational const frac = rationalCommonType->value(); + bigint const num = frac.numerator() / frac.denominator(); + auto const value = TypeProvider::rationalNumber(rational(num, 1)); + auto const type = value->integerType(); + setType(_operation, TypedValue{type, value}); + return; + } + } + setType( _operation, TokenTraits::isCompareOp(_operation.getOperator()) ? @@ -89,7 +108,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value); } - setType(_identifier, type(*value)); + setType(_identifier, TypedValue{variableDeclaration->annotation().type, type(*value).value}); } void ConstantEvaluator::endVisit(TupleExpression const& _tuple) @@ -98,13 +117,13 @@ void ConstantEvaluator::endVisit(TupleExpression const& _tuple) setType(_tuple, type(*_tuple.components().front())); } -void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type) +void ConstantEvaluator::setType(ASTNode const& _node, TypedValue const& _type) { - if (_type && _type->category() == Type::Category::RationalNumber) + if (_type.value && _type.value->category() == Type::Category::RationalNumber) (*m_types)[&_node] = _type; } -TypePointer ConstantEvaluator::type(ASTNode const& _node) +ConstantEvaluator::TypedValue ConstantEvaluator::type(ASTNode const& _node) { return (*m_types)[&_node]; } @@ -112,5 +131,5 @@ TypePointer ConstantEvaluator::type(ASTNode const& _node) TypePointer ConstantEvaluator::evaluate(Expression const& _expr) { _expr.accept(*this); - return type(_expr); + return type(_expr).value; } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 521f46338..e5faf6e02 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -25,6 +25,7 @@ #include +#include #include namespace solidity::langutil @@ -43,10 +44,12 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: + struct TypedValue { TypePointer type; TypePointer value; }; + ConstantEvaluator( langutil::ErrorReporter& _errorReporter, size_t _newDepth = 0, - std::shared_ptr> _types = std::make_shared>() + std::shared_ptr> _types = std::make_shared>() ): m_errorReporter(_errorReporter), m_depth(_newDepth), @@ -63,13 +66,14 @@ private: 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); + void setType(ASTNode const& _node, TypedValue const& _type); + void setType(ASTNode const& _node, TypePointer _type) { setType(_node, {_type, _type}); }; + TypedValue type(ASTNode const& _node); langutil::ErrorReporter& m_errorReporter; /// Current recursion depth. size_t m_depth = 0; - std::shared_ptr> m_types; + std::shared_ptr> m_types; }; } diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h index 66df9b249..799b422bb 100644 --- a/libsolidity/ast/Types.h +++ b/libsolidity/ast/Types.h @@ -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, diff --git a/test/libsolidity/semanticTests/consteval_array_length.sol b/test/libsolidity/semanticTests/consteval_array_length.sol new file mode 100644 index 000000000..9e2b5050c --- /dev/null +++ b/test/libsolidity/semanticTests/consteval_array_length.sol @@ -0,0 +1,14 @@ +contract C { + uint constant a = 12; + uint constant b = 10; + + function f() public pure returns (uint) { + uint[(a / b) * b] memory x; + return x.length; + } +} +// ==== +// compileViaYul: true +// ---- +// constructor() -> +// f() -> 10