diff --git a/Changelog.md b/Changelog.md index 441ff22dcc89..46ebaf31f658 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,8 @@ ### 0.8.36 (unreleased) Language Features: +* General: Add support for constant evaluation of `keccak256` builtin. +* General: Add support for constant evaluation for `uint256` type conversion. Compiler Features: * General: Speed up SHA-256 hashing (`picosha2`). diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index f094988d9bd6..f82e1df1306e 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -246,17 +246,65 @@ TypedValue convertType(rational const& _value, Type const& _type) else return TypedValue{&_type, _value.numerator() / _value.denominator()}; } - else - return TypedValue{}; + else if (auto const* fixedBytesType = dynamic_cast(&_type)) + { + // Only bytes32 is supported for now. + if (fixedBytesType->numBytes() != 32) + return TypedValue{}; + + if ( + _value.denominator() != 1 || + _value.numerator() < 0 || + _value.numerator() > TypeProvider::integer(fixedBytesType->numBytes() * 8, IntegerType::Modifier::Unsigned)->max() + ) + return TypedValue{}; + + u256 integerValue = u256(_value.numerator()); + // toBigEndian always returns 32 bytes, which is the only width supported. + // If support for narrower widths is added, then unused high bytes need to be erased + bytes bytesRepresentation = toBigEndian(integerValue); + return TypedValue{&_type, bytesRepresentation}; + } + + return TypedValue{}; } TypedValue convertType(std::string const& _value, Type const& _type) { if ( - _type.category() != Type::Category::StringLiteral && - _type.category() != Type::Category::Array + _type.category() == Type::Category::StringLiteral || + _type.category() == Type::Category::Array + ) + return TypedValue{&_type, _value}; + else if (_type.category() == Type::Category::FixedBytes) + { + auto const& fixedBytes = dynamic_cast(_type); + // Only bytes32 is supported for now. + if (fixedBytes.numBytes() != 32) + return TypedValue{}; + + if (_value.size() > fixedBytes.numBytes()) + return TypedValue{}; + + // Right pad with zeros to the full width + auto bytesValue = asBytes(_value); + bytesValue.resize(fixedBytes.numBytes(), 0); + return TypedValue{&_type, bytesValue}; + } + + return TypedValue{}; +} + +TypedValue convertType(bytes const& _value, Type const& _type) +{ + auto const* fixedBytes = dynamic_cast(&_type); + if ( + !fixedBytes || + _value.size() > fixedBytes->numBytes() || + fixedBytes->numBytes() != 32 // Supports only bytes32 for now ) return TypedValue{}; + return TypedValue{&_type, _value}; } @@ -269,10 +317,13 @@ TypedValue convertType(TypedValue const& _value, Type const& _type) [&](rational const& value) { return convertType(value, _type); }, + [&](bytes const& value) { + return convertType(value, _type); + }, [&](std::monostate const&) { return TypedValue{}; } - }, _value.value); + }, _value.value()); } TypedValue constantToTypedValue(Type const& _type) @@ -346,20 +397,20 @@ TypedValue ConstantEvaluator::evaluate(ASTNode const& _node) void ConstantEvaluator::endVisit(UnaryOperation const& _operation) { TypedValue value = evaluate(_operation.subExpression()); - if (!value.type) + if (value.empty()) return; - Type const* resultType = value.type->unaryOperatorResult(_operation.getOperator()); + Type const* resultType = value.type()->unaryOperatorResult(_operation.getOperator()); if (!resultType) return; value = convertType(value, *resultType); - if (!std::holds_alternative(value.value)) + if (!value.isRational()) return; - if (std::optional result = evaluateUnaryOperator(_operation.getOperator(), std::get(value.value))) + if (std::optional result = evaluateUnaryOperator(_operation.getOperator(), value.asRational())) { TypedValue convertedValue = convertType(*result, *resultType); - if (!convertedValue.type) + if (!convertedValue.type()) m_errorReporter.fatalTypeError( 3667_error, _operation.location(), @@ -373,7 +424,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) { TypedValue left = evaluate(_operation.leftExpression()); TypedValue right = evaluate(_operation.rightExpression()); - if (!left.type || !right.type) + if (!left.type() || !right.type()) return; // If this is implemented in the future: Comparison operators have a "binaryOperatorResult" @@ -381,7 +432,7 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) if (TokenTraits::isCompareOp(_operation.getOperator())) return; - Type const* resultType = left.type->binaryOperatorResult(_operation.getOperator(), right.type); + Type const* resultType = left.type()->binaryOperatorResult(_operation.getOperator(), right.type()); if (!resultType) { m_errorReporter.fatalTypeError( @@ -390,9 +441,9 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) "Operator " + std::string(TokenTraits::toString(_operation.getOperator())) + " not compatible with types " + - left.type->toString() + + left.type()->toString() + " and " + - right.type->toString() + right.type()->toString() ); return; } @@ -400,19 +451,19 @@ void ConstantEvaluator::endVisit(BinaryOperation const& _operation) left = convertType(left, *resultType); right = convertType(right, *resultType); if ( - !std::holds_alternative(left.value) || - !std::holds_alternative(right.value) + !left.isRational() || + !right.isRational() ) return; if (std::optional value = evaluateBinaryOperator( _operation.getOperator(), - std::get(left.value), - std::get(right.value) + left.asRational(), + right.asRational() )) { TypedValue convertedValue = convertType(*value, *resultType); - if (!convertedValue.type) + if (!convertedValue.type()) m_errorReporter.fatalTypeError( 2643_error, _operation.location(), @@ -443,6 +494,65 @@ void ConstantEvaluator::endVisit(TupleExpression const& _tuple) void ConstantEvaluator::endVisit(FunctionCall const& _functionCall) { + auto const* elementaryTypeNameExpression = dynamic_cast(&_functionCall.expression()); + if (elementaryTypeNameExpression) + { + // Type checking might not have been performed yet. This is the same that is done in that step. + auto const* expressionTypeType = TypeProvider::typeType(TypeProvider::fromElementaryTypeName( + elementaryTypeNameExpression->type().typeName(), + elementaryTypeNameExpression->type().stateMutability() + )); + solAssert(expressionTypeType); + solAssert(expressionTypeType->actualType()); + + // For now, only conversion target supported: uint256 + auto const* integerResultType = dynamic_cast(expressionTypeType->actualType()); + if (!integerResultType || integerResultType != TypeProvider::uint256()) + return; + + if (_functionCall.arguments().size() != 1 || !_functionCall.names().empty()) + return; + + TypedValue valueToConvert = evaluate(*_functionCall.arguments().front()); + if (!valueToConvert.type() || !valueToConvert.type()->isExplicitlyConvertibleTo(*integerResultType)) + return; + + u256 convertedValue = 0; + if ( + valueToConvert.type()->category() == Type::Category::Integer || + valueToConvert.type()->category() == Type::Category::RationalNumber + ) + { + solAssert(valueToConvert.isRational()); + auto const& rationalValue = valueToConvert.asRational(); + solAssert(rationalValue.denominator() == 1); + + convertedValue = rationalValue >= rational(0) ? + u256(rationalValue.numerator()) : + s2u(s256(rationalValue.numerator())); + } + else if (valueToConvert.type()->category() == Type::Category::FixedBytes) + { + solAssert(valueToConvert.isBytes()); + auto const& bytesValue = valueToConvert.asBytes(); + auto const& fixedBytesType = static_cast(*valueToConvert.type()); + solAssert(size_t(integerResultType->numBits()) == fixedBytesType.numBytes() * 8); + + // FixedBytes is left-aligned. + for (auto byte: bytesValue) + convertedValue = (convertedValue << 8) | u256(byte); + + // fill with remaining zero bytes + auto remainingBytes = size_t(fixedBytesType.numBytes()) - bytesValue.size(); + convertedValue <<= 8 * remainingBytes; + } + else // Type is explicitly convertible but not evaluated at comptime + return; + + m_values[&_functionCall] = TypedValue{integerResultType, rational(convertedValue)}; + return; + } + auto const* builtinFunction = dynamic_cast(ASTNode::referencedDeclaration(_functionCall.expression())); if (!builtinFunction) return; @@ -455,10 +565,10 @@ void ConstantEvaluator::endVisit(FunctionCall const& _functionCall) { solAssert(_functionCall.arguments().size() == 1); auto stringArg = evaluate(*(_functionCall.arguments()[0].get())); - if (!std::holds_alternative(stringArg.value)) + if (!stringArg.isString()) return; - h256 innerKeccak = keccak256(std::get(stringArg.value)); + h256 innerKeccak = keccak256(stringArg.asString()); h256 outerKeccak = keccak256(h256(u256(innerKeccak) - 1)); outerKeccak.data()[31] = 0; u256 slot = outerKeccak; @@ -466,7 +576,63 @@ void ConstantEvaluator::endVisit(FunctionCall const& _functionCall) m_values[&_functionCall] = TypedValue{functionType->returnParameterTypes()[0], rational{slot}}; break; } + case FunctionType::Kind::KECCAK256: + { + if (_functionCall.arguments().size() != 1) + return; + auto argValue = evaluate(*_functionCall.arguments()[0]); + if (!argValue.isString()) + return; + + h256 hash = keccak256(argValue.asString()); + solAssert(functionType->returnParameterTypes().size() == 1); + solAssert(functionType->returnParameterTypes()[0] == TypeProvider::fixedBytes(32)); + m_values[&_functionCall] = TypedValue{functionType->returnParameterTypes()[0], hash.asBytes()}; + break; + } default: break; } } + +TypedValue::TypedValue(Type const* _type, TypedValue::Value _value) +{ + std::visit(util::GenericVisitor{ + [&](std::string const&) { + solAssert(dynamic_cast(_type) || dynamic_cast(_type)); + }, + [&](rational const&) { + solAssert(dynamic_cast(_type) || dynamic_cast(_type)); + }, + [&](bytes const&) { + solAssert(dynamic_cast(_type)); + }, + [&](std::monostate const&) { + solAssert(!_type); + } + }, _value); + + m_type = _type; + m_value = std::move(_value); +} + +std::string const& TypedValue::asString() const +{ + auto const* stringValue = std::get_if(&m_value); + solAssert(stringValue); + return *stringValue; +} + +rational const& TypedValue::asRational() const +{ + auto const* rationalValue = std::get_if(&m_value); + solAssert(rationalValue); + return *rationalValue; +} + +bytes const& TypedValue::asBytes() const +{ + auto const* bytesValue = std::get_if(&m_value); + solAssert(bytesValue); + return *bytesValue; +} diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 67adc0be1a64..a4d1dbaa7441 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -38,7 +38,7 @@ namespace solidity::frontend class TypeChecker; /** - * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants. + * Small drop-in replacement for TypeChecker to evaluate simple expressions of integer and string constants. * * Note: This always use "checked arithmetic" in the sense that any over- or underflow * results in "unknown" value. @@ -46,11 +46,26 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - struct TypedValue + class TypedValue { + public: + using Value = std::variant; + + TypedValue(): m_type(nullptr), m_value(std::monostate()) {} + TypedValue(Type const* _type, Value _value); + bool empty() const { return std::holds_alternative(m_value); } + bool isString() const { return std::holds_alternative(m_value); } + bool isRational() const { return std::holds_alternative(m_value); } + bool isBytes() const { return std::holds_alternative(m_value); } + Type const* type() const { return m_type; } + Value const& value() const { return m_value; } + std::string const& asString() const; + rational const& asRational() const; + bytes const& asBytes() const; + private: // Type may be RationalType or IntegerType for value rational - Type const* type; - std::variant value; + Type const* m_type; + Value m_value; }; static TypedValue evaluate( diff --git a/libsolidity/analysis/DeclarationTypeChecker.cpp b/libsolidity/analysis/DeclarationTypeChecker.cpp index e2f0cf6e8b00..5d9de97680fd 100644 --- a/libsolidity/analysis/DeclarationTypeChecker.cpp +++ b/libsolidity/analysis/DeclarationTypeChecker.cpp @@ -339,9 +339,9 @@ void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName) if (length->annotation().type && length->annotation().type->category() == Type::Category::RationalNumber) lengthValue = dynamic_cast(*length->annotation().type).value(); else if (ConstantEvaluator::TypedValue value = ConstantEvaluator::evaluate(m_errorReporter, *length); - std::holds_alternative(value.value) + value.isRational() ) - lengthValue = std::get(value.value); + lengthValue = value.asRational(); if (!lengthValue) m_errorReporter.typeError( diff --git a/libsolidity/analysis/PostTypeContractLevelChecker.cpp b/libsolidity/analysis/PostTypeContractLevelChecker.cpp index be5d008a6161..7acf5d1774e8 100644 --- a/libsolidity/analysis/PostTypeContractLevelChecker.cpp +++ b/libsolidity/analysis/PostTypeContractLevelChecker.cpp @@ -137,8 +137,8 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio if (integerType) { ConstantEvaluator::TypedValue typedRational = ConstantEvaluator::evaluate(m_errorReporter, baseSlotExpression); - solAssert(!typedRational.type || dynamic_cast(typedRational.type)); - if (!typedRational.type) + solAssert(!typedRational.type() || dynamic_cast(typedRational.type())); + if (!typedRational.type()) { m_errorReporter.typeError( 1505_error, @@ -148,8 +148,8 @@ void PostTypeContractLevelChecker::checkStorageLayoutSpecifier(ContractDefinitio ); return; } - solAssert(std::holds_alternative(typedRational.value)); - baseSlotRationalValue = std::get(typedRational.value); + solAssert(typedRational.isRational()); + baseSlotRationalValue = typedRational.asRational(); } else { diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index 1ad3e77441db..a0c6e5d55655 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -314,12 +314,12 @@ bool StaticAnalyzer::visit(BinaryOperation const& _operation) if ( *_operation.rightExpression().annotation().isPure && (_operation.getOperator() == Token::Div || _operation.getOperator() == Token::Mod) && - ConstantEvaluator::evaluate(m_errorReporter, _operation.leftExpression()).type + ConstantEvaluator::evaluate(m_errorReporter, _operation.leftExpression()).type() ) if ( auto rhs = ConstantEvaluator::evaluate(m_errorReporter, _operation.rightExpression()); - std::holds_alternative(rhs.value) && - std::get(rhs.value) == 0 + rhs.isRational() && + rhs.asRational() == 0 ) m_errorReporter.typeError( 1211_error, @@ -342,8 +342,8 @@ bool StaticAnalyzer::visit(FunctionCall const& _functionCall) if (*_functionCall.arguments()[2]->annotation().isPure) if ( auto lastArg = ConstantEvaluator::evaluate(m_errorReporter, *(_functionCall.arguments())[2]); - std::holds_alternative(lastArg.value) && - std::get(lastArg.value) == 0 + lastArg.isRational() && + lastArg.asRational() == 0 ) m_errorReporter.typeError( 4195_error, diff --git a/libsolidity/ast/ASTUtils.cpp b/libsolidity/ast/ASTUtils.cpp index 532b0bc7af35..a5a380d25704 100644 --- a/libsolidity/ast/ASTUtils.cpp +++ b/libsolidity/ast/ASTUtils.cpp @@ -146,11 +146,11 @@ std::optional erc7201CompileTimeValue(FunctionCall const& _erc7201Call) auto evaluatedResult = ConstantEvaluator::tryEvaluate(_erc7201Call); - if (std::holds_alternative(evaluatedResult.value)) + if (evaluatedResult.empty()) return std::nullopt; - solAssert(std::holds_alternative(evaluatedResult.value)); - auto rationalValue = std::get(evaluatedResult.value); + solAssert(evaluatedResult.isRational()); + auto rationalValue = evaluatedResult.asRational(); solAssert(rationalValue.denominator() == 1); bigint baseSlot = rationalValue.numerator(); solAssert(baseSlot <= std::numeric_limits::max()); diff --git a/libsolidity/formal/SMTEncoder.cpp b/libsolidity/formal/SMTEncoder.cpp index c8e0cdc92b12..127e37f20a4c 100644 --- a/libsolidity/formal/SMTEncoder.cpp +++ b/libsolidity/formal/SMTEncoder.cpp @@ -3125,9 +3125,9 @@ RationalNumberType const* SMTEncoder::isConstant(Expression const& _expr) if ( auto typedValue = ConstantEvaluator::tryEvaluate(_expr); - std::holds_alternative(typedValue.value) + typedValue.isRational() ) - return TypeProvider::rationalNumber(std::get(typedValue.value)); + return TypeProvider::rationalNumber(typedValue.asRational()); return nullptr; } diff --git a/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_empty_string.sol b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_empty_string.sol new file mode 100644 index 000000000000..a657e0da2a1c --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_empty_string.sol @@ -0,0 +1,14 @@ +uint constant EMPTY = uint(keccak256("")); + +contract C { + uint[EMPTY] array; + + function testEmptyStringRuntimeEquivalence() public view returns (bool) { + uint runtime = uint(keccak256("")); + + return array.length == runtime; + } +} +// ---- +// testEmptyStringRuntimeEquivalence() -> true + diff --git a/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_string.sol b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_string.sol new file mode 100644 index 000000000000..08d071a72d24 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_string.sol @@ -0,0 +1,23 @@ +uint constant STRING_LITERAL = uint(keccak256("1234abcd")); +uint constant HEX_LITERAL = uint(keccak256(hex"4d41")); +uint constant EMPTY = uint(keccak256("")); + +contract C { + uint[STRING_LITERAL] array; + uint[HEX_LITERAL] array2; + + function testStringLiteralRuntimeEquivalence() public view returns (bool) { + uint runtime = uint(keccak256("1234abcd")); + + return array.length == runtime; + } + function testHexLiteralRuntimeEquivalence() public view returns (bool) { + uint runtime = uint(keccak256(hex"4d41")); + + return array2.length == runtime; + } +} +// ---- +// testStringLiteralRuntimeEquivalence() -> true +// testHexLiteralRuntimeEquivalence() -> true + diff --git a/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_unicode_string.sol b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_unicode_string.sol new file mode 100644 index 000000000000..4264bc641267 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/keccak256_runtime_equivalence_unicode_string.sol @@ -0,0 +1,14 @@ +uint constant UNICODE = uint(keccak256(unicode"Araçá")); + +contract C { + uint[UNICODE] array; + + function testUnicodeStringRuntimeEquivalence() public view returns (bool) { + uint runtime = uint(keccak256(unicode"Araçá")); + + return array.length == runtime; + } +} +// ---- +// testUnicodeStringRuntimeEquivalence() -> true + diff --git a/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_bytes32.sol b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_bytes32.sol new file mode 100644 index 000000000000..66d6b4aefa0a --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_bytes32.sol @@ -0,0 +1,44 @@ +bytes32 constant FULL_HEX = 0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef; +bytes32 constant FULL_STRING = "1234567890abcdef1234567890abcdef"; +bytes32 constant PARTIAL_STRING = "1234abcd"; +bytes32 constant PARTIAL_HEX_STR = hex"4d41"; +bytes32 constant FULL_HEX_STR = hex"1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef"; +bytes32 constant EMPTY = ""; + +contract C { + int[uint(FULL_HEX)] fullHex; + int[uint(FULL_STRING)] fullStr; + int[uint(PARTIAL_STRING)] partialStr; + int[uint(PARTIAL_HEX_STR)] partialHexStr; + int[uint(FULL_HEX_STR)] fullHexStr; + int[uint(EMPTY) + 1] empty; + + function testHexRuntimeEquivalence() public view returns (bool) { + uint runtimeFull = uint(FULL_HEX); + + return fullHex.length == runtimeFull; + } + function testStringRuntimeEquivalence() public view returns (bool) { + uint runtimeFull = uint(FULL_STRING); + uint runtimePartial = uint(PARTIAL_STRING); + + return fullStr.length == runtimeFull && partialStr.length == runtimePartial; + } + function testHexStrRuntimeEquivalence() public view returns (bool) { + uint runtimeFull = uint(FULL_HEX_STR); + uint runtimePartial = uint(PARTIAL_HEX_STR); + + return fullHexStr.length == runtimeFull && partialHexStr.length == runtimePartial; + } + function testEmptyRuntimeEquivalence() public view returns (bool) { + uint runtimeEmpty = uint(EMPTY); + + return empty.length == runtimeEmpty + 1; + } + +} +// ---- +// testHexRuntimeEquivalence() -> true +// testStringRuntimeEquivalence() -> true +// testHexStrRuntimeEquivalence() -> true +// testEmptyRuntimeEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed.sol b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed.sol new file mode 100644 index 000000000000..1721ce71ff1e --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed.sol @@ -0,0 +1,28 @@ +int constant NEGATIVE = -(2**128); +int constant POSITIVE = 2**64; + +contract C { + int[uint(NEGATIVE)] fromNegative; + int[uint(POSITIVE)] fromPositive; + int[2**256 - 1 - (uint(NEGATIVE) + uint(POSITIVE))] fromExpression; + + function testNegativeIntEquivalence() public view returns (bool) { + uint runtime = uint(NEGATIVE); + + return fromNegative.length == runtime; + } + function testPositiveIntEquivalence() public view returns (bool) { + uint runtime = uint(POSITIVE); + + return fromPositive.length == runtime; + } + function testExpressionEquivalence() public view returns (bool) { + uint runtime = 2**256 - 1 - (uint(NEGATIVE) + uint(POSITIVE)); + + return fromExpression.length == runtime; + } +} +// ---- +// testNegativeIntEquivalence() -> true +// testPositiveIntEquivalence() -> true +// testExpressionEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed_boundaries.sol b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed_boundaries.sol new file mode 100644 index 000000000000..57b1ef8abb64 --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_signed_boundaries.sol @@ -0,0 +1,17 @@ +int constant INT_MAX = 2**255 - 1; +int constant INT_MIN = -(2**255); + +contract C { + int[uint(INT_MAX)] arrayMax; + int[uint(INT_MIN)] arrayMin; + + function testRuntimeEquivalence() public view returns (bool) { + uint runtimeMin = uint(INT_MIN); + uint runtimeMax = uint(INT_MAX); + + return + arrayMin.length == runtimeMin && arrayMax.length == runtimeMax; + } +} +// ---- +// testRuntimeEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned.sol b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned.sol new file mode 100644 index 000000000000..0904ce04688b --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned.sol @@ -0,0 +1,35 @@ +uint constant UNSIGNED_8 = uint(8); + +contract C { + uint constant LITERAL = uint(42); + uint constant CONST = uint(UNSIGNED_8); + uint constant CHAINED = uint(uint(uint(64))); + uint constant EXPRESSION = uint((CONST + LITERAL) * 2); + + int[LITERAL] a; + int[CONST] b; + int[CHAINED] c; + int[EXPRESSION] d; + + function testLiteralEquivalence() public view returns (bool) { + uint runtime = uint(42); + return a.length == runtime; + } + function testConstEquivalence() public view returns (bool) { + uint runtime = uint(UNSIGNED_8); + return b.length == runtime; + } + function testChainedEquivalence() public view returns (bool) { + uint runtime = uint(uint(uint(64))); + return c.length == runtime; + } + function testExpressionEquivalence() public view returns (bool) { + uint runtime = uint((CONST + LITERAL) * 2); + return d.length == runtime; + } +} +// ---- +// testLiteralEquivalence() -> true +// testConstEquivalence() -> true +// testChainedEquivalence() -> true +// testExpressionEquivalence() -> true diff --git a/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned_boundaries.sol b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned_boundaries.sol new file mode 100644 index 000000000000..28ac5518fdbc --- /dev/null +++ b/test/libsolidity/semanticTests/constantEvaluator/uint256_conversion_runtime_equivalence_unsigned_boundaries.sol @@ -0,0 +1,13 @@ +contract C layout at uint(0) { + int[uint(2**256 - 1)] array; + function testRuntimeEquivalence() public view returns (bool) { + uint layoutBase; + assembly { + layoutBase := array.slot + } + + return array.length == 2**256 - 1 && layoutBase == 0; + } +} +// ---- +// testRuntimeEquivalence() -> true diff --git a/test/libsolidity/syntaxTests/array/length/literal_conversion.sol b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol index cf769f337974..c586d14e974a 100644 --- a/test/libsolidity/syntaxTests/array/length/literal_conversion.sol +++ b/test/libsolidity/syntaxTests/array/length/literal_conversion.sol @@ -1,6 +1,6 @@ contract C { - uint[uint(1)] valid_size_invalid_expr1; - uint[uint(2**256-1)] valid_size_invalid_expr2; + uint[uint(1)] valid_size_valid_expr1; + uint[uint(2**256-1)] valid_size_valid_expr2; uint[uint(2**256)] invalid_size_invalid_expr3; uint[int(1)] valid_size_invalid_expr4; @@ -8,9 +8,7 @@ contract C { uint[int(2**256)] invalid_size_invalid_expr6; } // ---- -// TypeError 5462: (22-29): Invalid array length, expected integer literal or constant expression. -// TypeError 5462: (66-80): Invalid array length, expected integer literal or constant expression. -// TypeError 5462: (117-129): Invalid array length, expected integer literal or constant expression. -// TypeError 5462: (169-175): Invalid array length, expected integer literal or constant expression. -// TypeError 5462: (212-225): Invalid array length, expected integer literal or constant expression. -// TypeError 5462: (262-273): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (113-125): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (165-171): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (208-221): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (258-269): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_smaller_uint.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_smaller_uint.sol new file mode 100644 index 000000000000..be9d449940c5 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_smaller_uint.sol @@ -0,0 +1,9 @@ +contract C { + int[uint128(42)] a; + int[uint8(8)] b; + int[uint64(uint(64))] c; +} +// ---- +// TypeError 5462: (21-32): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (45-53): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (66-82): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_overflow.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_overflow.sol new file mode 100644 index 000000000000..7230760fea85 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_overflow.sol @@ -0,0 +1,3 @@ +contract A layout at 2**255 * uint(2) {} +// ---- +// TypeError 2643: (21-37): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_underflow.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_underflow.sol new file mode 100644 index 000000000000..624737724da5 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_arithmetic_underflow.sol @@ -0,0 +1,3 @@ +contract C layout at uint(0) - 2**10 {} +// ---- +// TypeError 2643: (21-36): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_composed_operations.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_composed_operations.sol new file mode 100644 index 000000000000..f4125dafca57 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_composed_operations.sol @@ -0,0 +1,7 @@ +uint constant BASE = uint(0x42); + +contract A layout at 7 + uint(0xff) {} +contract B layout at uint(0x10) * 4 {} +contract C layout at uint(BASE) + uint(0x100) {} +contract D layout at (uint(0x1) << 8) + uint(0x42) {} +// ---- diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes.sol new file mode 100644 index 000000000000..4aa68d4e1870 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes.sol @@ -0,0 +1,14 @@ +bytes32 constant LITERAL = "1234abcd"; +bytes32 constant HEX_LITERAL = hex"12345678"; +bytes32 constant HEX_NUMBER = 0x0000000000000000000000000000000000000000000000000000000000000042; +uint constant CONST = uint(LITERAL); + +contract A layout at uint(LITERAL) {} +contract B layout at uint(HEX_LITERAL) { + int[uint(HEX_NUMBER)] array; +} +contract C { + uint[CONST] array; +} +// ---- +// Warning 7325: (352-363): Type uint256[22252025335055652125439449056026276909764973351109828411300806246416752050176] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes_string_too_big.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes_string_too_big.sol new file mode 100644 index 000000000000..f2446c22f946 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_bytes_string_too_big.sol @@ -0,0 +1,6 @@ +contract C { + bytes32 constant X = "00000000000000000000000000000000000000000000000000000000000000001"; + int[uint(X)] array; +} +// ---- +// TypeError 5462: (115-122): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_hex_literal.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_hex_literal.sol new file mode 100644 index 000000000000..18b006e73993 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_hex_literal.sol @@ -0,0 +1,6 @@ +contract A layout at uint(0xff) { + int[uint(0x0000000000000001)] a; + int[uint(0xAbCdEf123456)] b; + int[uint(0xff_ff)] c; +} +// ---- diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_integer_literal.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_integer_literal.sol new file mode 100644 index 000000000000..d9b6889d1083 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_integer_literal.sol @@ -0,0 +1,13 @@ +contract C layout at uint(0) + 123 { + int[uint(42)] a; + int[uint(3 * 3 - 1)] b; + int[uint(-(-8))] c; + int[uint(uint(10))] d; + int[uint((8 - 10) * -1)] e; + int[uint(1e2)] f; + int[uint(8 / 2)] g; + int[uint(2.0)] h; + int[uint(3 / 2 * 4)] i; + int[uint(2**256 - 1) / 2**255] j; +} +// ---- diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_keccak.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_keccak.sol new file mode 100644 index 000000000000..6d8d9d184131 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_keccak.sol @@ -0,0 +1,8 @@ +bytes32 constant CONST_HASH = keccak256("1234abcd"); + +contract A layout at uint(keccak256("example")) {} +contract C { + uint[uint(CONST_HASH)] array; +} +// ---- +// Warning 7325: (122-144): Type uint256[22668996584266725980521143636517830746133512938818882126204489763938146811214] covers a large part of storage and thus makes collisions likely. Either use mappings or dynamic arrays and allow their size to be increased only in small quantities per transaction. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_literal_invalid.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_literal_invalid.sol new file mode 100644 index 000000000000..2d5c0fcb1e9b --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_literal_invalid.sol @@ -0,0 +1,11 @@ +contract A layout at uint(2**256) {} +contract B layout at uint(-1) {} +contract C layout at uint(3/2) {} +contract D layout at uint(3.14) {} +contract E layout at uint(1.1618e2) {} +// ---- +// TypeError 9640: (21-33): Explicit type conversion not allowed from "int_const 1157...(70 digits omitted)...9936" to "uint256". +// TypeError 9640: (58-66): Explicit type conversion not allowed from "int_const -1" to "uint256". +// TypeError 9640: (91-100): Explicit type conversion not allowed from "rational_const 3 / 2" to "uint256". +// TypeError 9640: (125-135): Explicit type conversion not allowed from "rational_const 157 / 50" to "uint256". +// TypeError 9640: (160-174): Explicit type conversion not allowed from "rational_const 5809 / 50" to "uint256". diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_smaller_fixed_bytes.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_smaller_fixed_bytes.sol new file mode 100644 index 000000000000..1b25b1e3d2f7 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_from_smaller_fixed_bytes.sol @@ -0,0 +1,10 @@ +bytes16 constant B16 = hex"00112233445566778899aabbccddeeff"; +bytes8 constant B8 = "12345678"; + +contract C { + int[uint128(B16)] arr1; + int[uint64(B8)] arr2; +} +// ---- +// TypeError 5462: (117-129): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (145-155): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_named_param_invalid.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_named_param_invalid.sol new file mode 100644 index 000000000000..98bd93331b3d --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_named_param_invalid.sol @@ -0,0 +1,5 @@ +contract C { + int[uint({val: 77})] array; +} +// ---- +// TypeError 5462: (21-36): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_wrong_number_param_invalid.sol b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_wrong_number_param_invalid.sol new file mode 100644 index 000000000000..c11d387134c2 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/conversion_to_uint_wrong_number_param_invalid.sol @@ -0,0 +1,7 @@ +contract C { + int[uint()] array1; + int[uint(1, 2)] array2; +} +// ---- +// TypeError 5462: (21-27): Invalid array length, expected integer literal or constant expression. +// TypeError 5462: (45-55): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/converstion_to_uint_from_integer_variable.sol b/test/libsolidity/syntaxTests/constantEvaluator/converstion_to_uint_from_integer_variable.sol new file mode 100644 index 000000000000..18ae99dc54fb --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/converstion_to_uint_from_integer_variable.sol @@ -0,0 +1,13 @@ +uint constant CONST = uint(42); +uint constant CONST2 = uint(64); +int constant SIGNED_POSITIVE = 2; +int constant SIGNED_NEGATIVE = -1; + +contract C layout at CONST { + int[uint(CONST2)] a; + int[uint(SIGNED_POSITIVE)] b; + int[uint(CONST + CONST2)] c; + int[uint(CONST / 2)] d; + int[uint(SIGNED_NEGATIVE) / 2**255] e; +} +// ---- diff --git a/test/libsolidity/syntaxTests/constantEvaluator/keccak256_named_param_invalid.sol b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_named_param_invalid.sol new file mode 100644 index 000000000000..d8e17b4cf7e3 --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_named_param_invalid.sol @@ -0,0 +1,6 @@ +contract C { + bytes32 constant H = keccak256({_data: "abc"}); + uint[uint(H)] arr; +} +// ---- +// TypeError 4974: (38-63): Named argument "_data" does not match function declaration. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/keccak256_non_literal_param.sol b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_non_literal_param.sol new file mode 100644 index 000000000000..8ff8272f883b --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_non_literal_param.sol @@ -0,0 +1,5 @@ +contract C { + int[uint(keccak256(bytes.concat("ABCD")))] arr; +} +// ---- +// TypeError 5462: (21-58): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/constantEvaluator/keccak256_wrong_param_bytes32.sol b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_wrong_param_bytes32.sol new file mode 100644 index 000000000000..5f0ad966b96a --- /dev/null +++ b/test/libsolidity/syntaxTests/constantEvaluator/keccak256_wrong_param_bytes32.sol @@ -0,0 +1,7 @@ +contract C { + bytes32 constant b32 = "1234abcd"; + uint constant BYTES32 = uint(keccak256(b32)); + uint[BYTES32] array; +} +// ---- +// TypeError 5462: (111-118): Invalid array length, expected integer literal or constant expression. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol index 6c7cd0efbd39..ce4aaaf0c982 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/bitwise_negation_after_cast.sol @@ -1,3 +1,3 @@ contract C layout at ~uint(0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF) {} // ---- -// TypeError 1505: (21-94): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. +// TypeError 3667: (21-94): Arithmetic error when computing constant value. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol index fd75c94a4419..f2b2d71fb09f 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/constant_initialized_from_cast.sol @@ -1,4 +1,3 @@ uint constant x = uint(42); contract C layout at x {} // ---- -// TypeError 1505: (49-50): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time. diff --git a/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol b/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol index da64e83578fe..440ba1c1a6f6 100644 --- a/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol +++ b/test/libsolidity/syntaxTests/storageLayoutSpecifier/literal_cast.sol @@ -1,3 +1,2 @@ contract C layout at uint(42) { } // ---- -// TypeError 1505: (21-29): The base slot expression contains elements that are not yet supported by the internal constant evaluator and therefore cannot be evaluated at compilation time.