Merge pull request #3142 from Balajiganapathi/allow_constant_array_length

Allow constant integer variables as array lengths.
This commit is contained in:
Alex Beregszaszi 2017-11-22 02:34:17 +00:00 committed by GitHub
commit 4e0723ce27
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 193 additions and 3 deletions

View File

@ -1,6 +1,7 @@
### 0.4.19 (unreleased) ### 0.4.19 (unreleased)
Features: 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. * Syntax Checker: Turn the usage of ``callcode`` into an error as experimental 0.5.0 feature.
* Type Checker: Improve address checksum warning. * Type Checker: Improve address checksum warning.
* Type Checker: More detailed errors for invalid array lengths (such as division by zero). * Type Checker: More detailed errors for invalid array lengths (such as division by zero).

View File

@ -74,3 +74,25 @@ void ConstantEvaluator::endVisit(Literal const& _literal)
if (!_literal.annotation().type) if (!_literal.annotation().type)
m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value."); m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
} }
void ConstantEvaluator::endVisit(Identifier const& _identifier)
{
VariableDeclaration const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration);
if (!variableDeclaration)
return;
if (!variableDeclaration->isConstant())
m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant.");
ASTPointer<Expression> value = variableDeclaration->value();
if (!value)
m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value.");
if (!value->annotation().type)
{
if (m_depth > 32)
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
ConstantEvaluator e(*value, m_errorReporter, m_depth + 1);
}
_identifier.annotation().type = value->annotation().type;
}

View File

@ -38,8 +38,9 @@ class TypeChecker;
class ConstantEvaluator: private ASTConstVisitor class ConstantEvaluator: private ASTConstVisitor
{ {
public: public:
ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter): ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0):
m_errorReporter(_errorReporter) m_errorReporter(_errorReporter),
m_depth(_newDepth)
{ {
_expr.accept(*this); _expr.accept(*this);
} }
@ -48,8 +49,11 @@ private:
virtual void endVisit(BinaryOperation const& _operation); virtual void endVisit(BinaryOperation const& _operation);
virtual void endVisit(UnaryOperation const& _operation); virtual void endVisit(UnaryOperation const& _operation);
virtual void endVisit(Literal const& _literal); virtual void endVisit(Literal const& _literal);
virtual void endVisit(Identifier const& _identifier);
ErrorReporter& m_errorReporter; ErrorReporter& m_errorReporter;
/// Current recursion depth.
size_t m_depth;
}; };
} }

View File

@ -2345,6 +2345,24 @@ BOOST_AUTO_TEST_CASE(constructor_static_array_argument)
ABI_CHECK(callContractFunction("b(uint256)", u256(2)), encodeArgs(u256(4))); 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) BOOST_AUTO_TEST_CASE(functions_called_by_constructor)
{ {
char const* sourceCode = R"( char const* sourceCode = R"(

View File

@ -2107,7 +2107,7 @@ BOOST_AUTO_TEST_CASE(array_with_nonconstant_length)
function f(uint a) public { uint8[a] x; } 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) BOOST_AUTO_TEST_CASE(array_with_negative_length)
@ -7263,6 +7263,151 @@ BOOST_AUTO_TEST_CASE(array_length_not_convertible_to_integer)
CHECK_ERROR(text, TypeError, "Invalid array length, expected integer literal."); 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_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, "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 (or maximum recursion depth exhausted).");
}
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 (or maximum recursion depth exhausted).");
}
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) BOOST_AUTO_TEST_CASE(array_length_invalid_expression)
{ {
char const* text = R"( char const* text = R"(