diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 421e358f6..e844861b1 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -217,7 +217,9 @@ ASTPointer Parser::parseContractDefinition(bool _isLibrary) if (currentTokenValue == Token::RBrace) break; else if (currentTokenValue == Token::Function) - subNodes.push_back(parseFunctionDefinition(name.get())); + // This can be a function or a state variable of function type (especially + // complicated to distinguish fallback function from function type state variable) + subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get())); else if (currentTokenValue == Token::Struct) subNodes.push_back(parseStructDefinition()); else if (currentTokenValue == Token::Enum) @@ -334,7 +336,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN return result; } -ASTPointer Parser::parseFunctionDefinition(ASTString const* _contractName) +ASTPointer Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName) { ASTNodeFactory nodeFactory(*this); ASTPointer docstring; @@ -343,28 +345,55 @@ ASTPointer Parser::parseFunctionDefinition(ASTString const* FunctionHeaderParserResult header = parseFunctionHeader(false, true); - ASTPointer block = ASTPointer(); - nodeFactory.markEndPosition(); - if (m_scanner->currentToken() != Token::Semicolon) + if ( + !header.modifiers.empty() || + !header.name->empty() || + m_scanner->currentToken() == Token::Semicolon || + m_scanner->currentToken() == Token::LBrace + ) { - block = parseBlock(); - nodeFactory.setEndPositionFromNode(block); + // this has to be a function + ASTPointer block = ASTPointer(); + nodeFactory.markEndPosition(); + if (m_scanner->currentToken() != Token::Semicolon) + { + block = parseBlock(); + nodeFactory.setEndPositionFromNode(block); + } + else + m_scanner->next(); // just consume the ';' + bool const c_isConstructor = (_contractName && *header.name == *_contractName); + return nodeFactory.createNode( + header.name, + header.visibility, + c_isConstructor, + docstring, + header.parameters, + header.isDeclaredConst, + header.modifiers, + header.returnParameters, + header.isPayable, + block + ); } else - m_scanner->next(); // just consume the ';' - bool const c_isConstructor = (_contractName && *header.name == *_contractName); - return nodeFactory.createNode( - header.name, - header.visibility, - c_isConstructor, - docstring, - header.parameters, - header.isDeclaredConst, - header.modifiers, - header.returnParameters, - header.isPayable, - block - ); + { + // this has to be a state variable + ASTPointer type = nodeFactory.createNode( + header.parameters, + header.returnParameters, + header.visibility, + header.isDeclaredConst, + header.isPayable + ); + type = parseTypeNameSuffix(type, nodeFactory); + VarDeclParserOptions options; + options.isStateVariable = true; + options.allowInitialValue = true; + auto node = parseVariableDeclaration(options, type); + expectToken(Token::Semicolon); + return node; + } } ASTPointer Parser::parseStructDefinition() @@ -613,6 +642,21 @@ ASTPointer Parser::parseUserDefinedTypeName() return nodeFactory.createNode(identifierPath); } +ASTPointer Parser::parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory) +{ + while (m_scanner->currentToken() == Token::LBrack) + { + m_scanner->next(); + ASTPointer length; + if (m_scanner->currentToken() != Token::RBrack) + length = parseExpression(); + nodeFactory.markEndPosition(); + expectToken(Token::RBrack); + type = nodeFactory.createNode(type, length); + } + return type; +} + ASTPointer Parser::parseTypeName(bool _allowVar) { ASTNodeFactory nodeFactory(*this); @@ -644,16 +688,7 @@ ASTPointer Parser::parseTypeName(bool _allowVar) if (type) // Parse "[...]" postfixes for arrays. - while (m_scanner->currentToken() == Token::LBrack) - { - m_scanner->next(); - ASTPointer length; - if (m_scanner->currentToken() != Token::RBrack) - length = parseExpression(); - nodeFactory.markEndPosition(); - expectToken(Token::RBrack); - type = nodeFactory.createNode(type, length); - } + type = parseTypeNameSuffix(type, nodeFactory); return type; } diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h index a59d26883..9295a7fa4 100644 --- a/libsolidity/parsing/Parser.h +++ b/libsolidity/parsing/Parser.h @@ -73,6 +73,7 @@ private: ASTPointer parseInheritanceSpecifier(); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); + ASTPointer parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer parseFunctionDefinition(ASTString const* _contractName); ASTPointer parseStructDefinition(); ASTPointer parseEnumDefinition(); @@ -87,6 +88,7 @@ private: ASTPointer parseModifierInvocation(); ASTPointer parseIdentifier(); ASTPointer parseUserDefinedTypeName(); + ASTPointer parseTypeNameSuffix(ASTPointer type, ASTNodeFactory& nodeFactory); ASTPointer parseTypeName(bool _allowVar); ASTPointer parseFunctionType(); ASTPointer parseMapping(); diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp index 76df19705..0f392cab7 100644 --- a/test/libsolidity/SolidityEndToEndTest.cpp +++ b/test/libsolidity/SolidityEndToEndTest.cpp @@ -7669,7 +7669,33 @@ BOOST_AUTO_TEST_CASE(pass_function_types_externally) BOOST_CHECK(callContractFunction("f2(uint256)", 7) == encodeArgs(u256(8))); } -// TODO: storage, arrays +BOOST_AUTO_TEST_CASE(store_function) +{ + char const* sourceCode = R"( + contract Other { + function addTwo(uint x) returns (uint) { return x + 2; } + } + contract C { + function (unction (uint) external returns (uint)) returns (uint) ev = eval; + function (uint) external returns (uint) x; + function store(function(uint) external returns (uint) y) { + x = y; + } + function eval(function(uint) external returns (uint) y) returns (uint) { + return y(7); + } + function t() returns (uint) { + this.store((new Other()).addTwo); + return ev(x); + } + } + )"; + + compileAndRun(sourceCode, 0, "C"); + BOOST_CHECK(callContractFunction("t()") == encodeArgs(u256(9))); +} + +// TODO: public function state variables, arrays BOOST_AUTO_TEST_CASE(shift_constant_left) { diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 69b8d0f0e..496f47031 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -1256,7 +1256,26 @@ BOOST_AUTO_TEST_CASE(function_type_in_expression) BOOST_AUTO_TEST_CASE(function_type_as_storage_variable) { - // TODO disambiguate from fallback function + char const* text = R"( + contract test { + function (uint, uint) internal returns (uint) f1; + } + )"; + BOOST_CHECK(successParse(text)); +} + +BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_modifiers) +{ + char const* text = R"( + contract test { + function (uint, uint) modifier1() returns (uint) f1; + } + )"; + BOOST_CHECK(!successParse(text)); +} + +BOOST_AUTO_TEST_CASE(function_type_as_storage_variable_with_assignment) +{ char const* text = R"( contract test { function f(uint x, uint y) returns (uint a) {}