From 165857b1d40ecb8a1bcf40eec9e371370ebe2541 Mon Sep 17 00:00:00 2001 From: Balajiganapathi S Date: Sat, 28 Oct 2017 16:33:11 +0530 Subject: Allow constant integer variables as array lengths. --- Changelog.md | 1 + libsolidity/analysis/ConstantEvaluator.cpp | 18 ++++++++++++++++ libsolidity/analysis/ConstantEvaluator.h | 1 + test/libsolidity/SolidityEndToEndTest.cpp | 18 ++++++++++++++++ test/libsolidity/SolidityNameAndTypeResolution.cpp | 24 +++++++++++++++++++++- 5 files changed, 61 insertions(+), 1 deletion(-) diff --git a/Changelog.md b/Changelog.md index 2487b87c..a362138e 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,6 +1,7 @@ ### 0.4.19 (unreleased) Features: + * Allow constant variables to be used as array length * Syntax Checker: Turn the usage of ``callcode`` into an error as experimental 0.5.0 feature. * Type Checker: Improve address checksum warning. * Type Checker: More detailed errors for invalid array lengths (such as division by zero). diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index bc3b7cf1..50044eb6 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -74,3 +74,21 @@ void ConstantEvaluator::endVisit(Literal const& _literal) if (!_literal.annotation().type) m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); } + +void ConstantEvaluator::endVisit(Identifier const& _identifier) +{ + VariableDeclaration const *variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration); + if (!variableDeclaration) + return; + if (!variableDeclaration->isConstant()) + m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant."); + + ASTPointer value = variableDeclaration->value(); + if (value) + { + if (!value->annotation().type) + ConstantEvaluator e(*value, m_errorReporter); + + _identifier.annotation().type = value->annotation().type; + } +} diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 90bceb5d..42ccfe7e 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -48,6 +48,7 @@ private: virtual void endVisit(BinaryOperation const& _operation); virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(Literal const& _literal); + virtual void endVisit(Identifier const& _identifier); ErrorReporter& m_errorReporter; }; diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 9a837113..c2f96aaa 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -2345,6 +2345,24 @@ BOOST_AUTO_TEST_CASE(constructor_static_array_argument) ABI_CHECK(callContractFunction("b(uint256)", u256(2)), encodeArgs(u256(4))); } +BOOST_AUTO_TEST_CASE(constant_var_as_array_length) +{ + char const* sourceCode = R"( + contract C { + uint constant LEN = 3; + uint[LEN] public a; + + function C(uint[LEN] _a) { + a = _a; + } + } + )"; + compileAndRun(sourceCode, 0, "C", encodeArgs(u256(1), u256(2), u256(3))); + ABI_CHECK(callContractFunction("a(uint256)", u256(0)), encodeArgs(u256(1))); + ABI_CHECK(callContractFunction("a(uint256)", u256(1)), encodeArgs(u256(2))); + ABI_CHECK(callContractFunction("a(uint256)", u256(2)), encodeArgs(u256(3))); +} + BOOST_AUTO_TEST_CASE(functions_called_by_constructor) { char const* sourceCode = R"( diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 88ec58ee..e00beefe 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2107,7 +2107,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) function f(uint a) public { uint8[a] x; } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Identifier must be declared constant"); } BOOST_AUTO_TEST_CASE(array_with_negative_length) @@ -7263,6 +7263,28 @@ BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer) CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } +BOOST_AUTO_TEST_CASE(array_length_constant_var) +{ + char const* text = R"( + contract C { + uint constant LEN = 10; + uint[LEN] ids; + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var) +{ + char const* text = R"( + contract C { + bool constant LEN = true; + uint[LEN] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + BOOST_AUTO_TEST_CASE(array_length_invalid_expression) { char const* text = R"( -- cgit v1.2.3 From c0b49694518d333a8ad62623321f839fc3b4bc6a Mon Sep 17 00:00:00 2001 From: Balajiganapathi S Date: Sat, 28 Oct 2017 18:06:46 +0530 Subject: Add more tests for constant var as array lengths --- test/libsolidity/SolidityNameAndTypeResolution.cpp | 97 +++++++++++++++++++++- 1 file changed, 96 insertions(+), 1 deletion(-) diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index e00beefe..cedf7f3a 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -2107,7 +2107,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length) function f(uint a) public { uint8[a] x; } } )"; - CHECK_ERROR(text, TypeError, "Identifier must be declared constant"); + CHECK_ERROR(text, TypeError, "Identifier must be declared constant."); } BOOST_AUTO_TEST_CASE(array_with_negative_length) @@ -7285,6 +7285,101 @@ BOOST_AUTO_TEST_CASE(array_length_non_integer_constant_var) CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); } +BOOST_AUTO_TEST_CASE(array_length_cannot_be_function) +{ + char const* text = R"( + contract C { + function f() {} + uint[f] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + +BOOST_AUTO_TEST_CASE(array_length_can_be_recursive_constant) +{ + char const* text = R"( + contract C { + uint constant L = 5; + uint constant LEN = L + 4 * L; + uint[LEN] ids; + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_cannot_be_function_call) +{ + char const* text = R"( + contract C { + function f(uint x) {} + uint constant LEN = f(); + uint[LEN] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + +BOOST_AUTO_TEST_CASE(array_length_const_cannot_be_fractional) +{ + char const* text = R"( + contract C { + fixed constant L = 10.5; + uint[L] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Array with fractional length specified"); +} + +BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_struct) +{ + char const* text = R"( + contract C { + uint constant LEN = 10; + struct Test { + uint[LEN] ids; + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_can_be_constant_in_function) +{ + char const* text = R"( + contract C { + uint constant LEN = 10; + function f() { + uint[LEN] a; + } + } + )"; + CHECK_SUCCESS(text); +} + +BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter) +{ + char const* text = R"( + contract C { + function f(uint constant LEN) { + uint[LEN] a; + } + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + +BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) +{ + char const* text = R"( + contract C { + uint constant LEN = keccak256(ripemd160(33)); + uint[LEN] ids; + } + )"; + CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); +} + BOOST_AUTO_TEST_CASE(array_length_invalid_expression) { char const* text = R"( -- cgit v1.2.3 From d102deaec90f6f5bfe38140b459a05215d3254f9 Mon Sep 17 00:00:00 2001 From: Balajiganapathi S Date: Fri, 17 Nov 2017 22:25:07 +0530 Subject: Detect cyclic constant definitions --- libsolidity/analysis/ConstantEvaluator.cpp | 16 +++++++----- libsolidity/analysis/ConstantEvaluator.h | 7 +++-- test/libsolidity/SolidityNameAndTypeResolution.cpp | 30 +++++++++++++++++++++- 3 files changed, 44 insertions(+), 9 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index 50044eb6..cb8bbc05 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -77,18 +77,22 @@ void ConstantEvaluator::endVisit(Literal const& _literal) void ConstantEvaluator::endVisit(Identifier const& _identifier) { - VariableDeclaration const *variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration); + VariableDeclaration const* variableDeclaration = dynamic_cast(_identifier.annotation().referencedDeclaration); if (!variableDeclaration) return; if (!variableDeclaration->isConstant()) m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant."); ASTPointer value = variableDeclaration->value(); - if (value) - { - if (!value->annotation().type) - ConstantEvaluator e(*value, m_errorReporter); + if (!value) + m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value."); - _identifier.annotation().type = value->annotation().type; + if (!value->annotation().type) + { + if (m_depth > 32) + m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition."); + ConstantEvaluator e(*value, m_errorReporter, m_depth + 1); } + + _identifier.annotation().type = value->annotation().type; } diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h index 42ccfe7e..6725d610 100644 --- a/libsolidity/analysis/ConstantEvaluator.h +++ b/libsolidity/analysis/ConstantEvaluator.h @@ -38,8 +38,9 @@ class TypeChecker; class ConstantEvaluator: private ASTConstVisitor { public: - ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter): - m_errorReporter(_errorReporter) + ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0): + m_errorReporter(_errorReporter), + m_depth(_newDepth) { _expr.accept(*this); } @@ -51,6 +52,8 @@ private: virtual void endVisit(Identifier const& _identifier); ErrorReporter& m_errorReporter; + /// Current recursion depth. + size_t m_depth; }; } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index cedf7f3a..0d02ac34 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7366,7 +7366,35 @@ BOOST_AUTO_TEST_CASE(array_length_cannot_be_constant_function_parameter) } } )"; - CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); + CHECK_ERROR(text, TypeError, "Constant identifier declaration must have a constant value."); +} + +BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant) +{ + char const* text = R"( + contract C { + uint constant LEN = LEN; + function f() { + uint[LEN] a; + } + } + )"; + CHECK_ERROR(text, TypeError, "Cyclic constant definition."); +} + +BOOST_AUTO_TEST_CASE(array_length_with_complex_cyclic_constant) +{ + char const* text = R"( + contract C { + uint constant L2 = LEN - 10; + uint constant L1 = L2 / 10; + uint constant LEN = 10 + L1 * 5; + function f() { + uint[LEN] a; + } + } + )"; + CHECK_ERROR(text, TypeError, "Cyclic constant definition."); } BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) -- cgit v1.2.3 From 455e51a608910d60348adb4b464590d976ce7fd7 Mon Sep 17 00:00:00 2001 From: Alex Beregszaszi Date: Wed, 22 Nov 2017 01:05:26 +0000 Subject: Improve cyclic constant error message --- libsolidity/analysis/ConstantEvaluator.cpp | 2 +- test/libsolidity/SolidityNameAndTypeResolution.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp index cb8bbc05..4d546e68 100644 --- a/libsolidity/analysis/ConstantEvaluator.cpp +++ b/libsolidity/analysis/ConstantEvaluator.cpp @@ -90,7 +90,7 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier) if (!value->annotation().type) { if (m_depth > 32) - m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition."); + m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted)."); ConstantEvaluator e(*value, m_errorReporter, m_depth + 1); } diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp index 0d02ac34..73c1660e 100644 --- a/test/libsolidity/SolidityNameAndTypeResolution.cpp +++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp @@ -7379,7 +7379,7 @@ BOOST_AUTO_TEST_CASE(array_length_with_cyclic_constant) } } )"; - CHECK_ERROR(text, TypeError, "Cyclic constant definition."); + CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted)."); } BOOST_AUTO_TEST_CASE(array_length_with_complex_cyclic_constant) @@ -7394,7 +7394,7 @@ BOOST_AUTO_TEST_CASE(array_length_with_complex_cyclic_constant) } } )"; - CHECK_ERROR(text, TypeError, "Cyclic constant definition."); + CHECK_ERROR(text, TypeError, "Cyclic constant definition (or maximum recursion depth exhausted)."); } BOOST_AUTO_TEST_CASE(array_length_with_pure_functions) -- cgit v1.2.3