diff options
25 files changed, 285 insertions, 12 deletions
diff --git a/Changelog.md b/Changelog.md index 997c4069..846ae1f5 100644 --- a/Changelog.md +++ b/Changelog.md @@ -81,6 +81,7 @@ Language Features: * General: Allow ``mapping`` storage pointers as arguments and return values in all internal functions. * General: Allow ``struct``s in interfaces. * General: Provide access to the ABI decoder through ``abi.decode(bytes memory data, (...))``. + * Parser: Accept the ``address payable`` type during parsing. Compiler Features: * C API (``libsolc``): Export the ``solidity_license``, ``solidity_version`` and ``solidity_compile`` methods. diff --git a/docs/grammar.txt b/docs/grammar.txt index 43ef07a9..594998f0 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -50,6 +50,7 @@ TypeName = ElementaryTypeName | Mapping | ArrayTypeName | FunctionTypeName + | ( 'address' 'payable' ) UserDefinedTypeName = Identifier ( '.' Identifier )* diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp index 8750b47b..4b678c3b 100644 --- a/libsolidity/analysis/ReferencesResolver.cpp +++ b/libsolidity/analysis/ReferencesResolver.cpp @@ -112,7 +112,20 @@ bool ReferencesResolver::visit(Identifier const& _identifier) bool ReferencesResolver::visit(ElementaryTypeName const& _typeName) { - _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (!_typeName.annotation().type) + { + _typeName.annotation().type = Type::fromElementaryTypeName(_typeName.typeName()); + if (_typeName.stateMutability().is_initialized()) + { + // for non-address types this was already caught by the parser + solAssert(_typeName.annotation().type->category() == Type::Category::Address, ""); + if (!( + *_typeName.stateMutability() == StateMutability::Payable || + *_typeName.stateMutability() == StateMutability::NonPayable + )) + m_errorReporter.typeError(_typeName.location(), "Address types can only be payable or non-payable."); + } + } return true; } diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h index a5cd277d..f3464f92 100644 --- a/libsolidity/ast/AST.h +++ b/libsolidity/ast/AST.h @@ -876,23 +876,31 @@ public: }; /** - * Any pre-defined type name represented by a single keyword, i.e. it excludes mappings, - * contracts, functions, etc. + * Any pre-defined type name represented by a single keyword (and possibly a state mutability for address types), + * i.e. it excludes mappings, contracts, functions, etc. */ class ElementaryTypeName: public TypeName { public: - ElementaryTypeName(SourceLocation const& _location, ElementaryTypeNameToken const& _elem): - TypeName(_location), m_type(_elem) - {} + ElementaryTypeName( + SourceLocation const& _location, + ElementaryTypeNameToken const& _elem, + boost::optional<StateMutability> _stateMutability = {} + ): TypeName(_location), m_type(_elem), m_stateMutability(_stateMutability) + { + solAssert(!_stateMutability.is_initialized() || _elem.token() == Token::Address, ""); + } virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; ElementaryTypeNameToken const& typeName() const { return m_type; } + boost::optional<StateMutability> const& stateMutability() const { return m_stateMutability; } + private: ElementaryTypeNameToken m_type; + boost::optional<StateMutability> m_stateMutability; ///< state mutability for address type }; /** diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index 0c41e332..1228b833 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -57,7 +57,7 @@ public: solAssert(m_location.sourceName, ""); if (m_location.end < 0) markEndPosition(); - return make_shared<NodeType>(m_location, forward<Args>(_args)...); + return make_shared<NodeType>(m_location, std::forward<Args>(_args)...); } private: @@ -813,8 +813,24 @@ ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar) unsigned secondSize; tie(firstSize, secondSize) = m_scanner->currentTokenInfo(); ElementaryTypeNameToken elemTypeName(token, firstSize, secondSize); - type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(elemTypeName); + ASTNodeFactory nodeFactory(*this); + nodeFactory.markEndPosition(); m_scanner->next(); + auto stateMutability = boost::make_optional(elemTypeName.token() == Token::Address, StateMutability::NonPayable); + if (Token::isStateMutabilitySpecifier(m_scanner->currentToken(), false)) + { + if (elemTypeName.token() == Token::Address) + { + nodeFactory.markEndPosition(); + stateMutability = parseStateMutability(); + } + else + { + parserError("State mutability can only be specified for address types."); + m_scanner->next(); + } + } + type = nodeFactory.createNode<ElementaryTypeName>(elemTypeName, stateMutability); } else if (token == Token::Var) { @@ -1615,8 +1631,8 @@ Parser::LookAheadInfo Parser::peekStatementType() const // Distinguish between variable declaration (and potentially assignment) and expression statement // (which include assignments to other expressions and pre-declared variables). // We have a variable declaration if we get a keyword that specifies a type name. - // If it is an identifier or an elementary type name followed by an identifier, we also have - // a variable declaration. + // If it is an identifier or an elementary type name followed by an identifier + // or a mutability specifier, we also have a variable declaration. // If we get an identifier followed by a "[" or ".", it can be both ("lib.type[9] a;" or "variable.el[9] = 7;"). // In all other cases, we have an expression statement. Token::Value token(m_scanner->currentToken()); @@ -1627,6 +1643,12 @@ Parser::LookAheadInfo Parser::peekStatementType() const if (mightBeTypeName) { Token::Value next = m_scanner->peekNextToken(); + // So far we only allow ``address payable`` in variable declaration statements and in no other + // kind of statement. This means, for example, that we do not allow type expressions of the form + // ``address payable;``. + // If we want to change this in the future, we need to consider another scanner token here. + if (Token::isElementaryTypeName(token) && Token::isStateMutabilitySpecifier(next, false)) + return LookAheadInfo::VariableDeclaration; if (next == Token::Identifier || Token::isLocationSpecifier(next)) return LookAheadInfo::VariableDeclaration; if (next == Token::LBrack || next == Token::Period) diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 7ce24e69..73c85482 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -312,7 +312,12 @@ public: static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage || op == CallData; } - static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || op == View || op == Payable; } + static bool isStateMutabilitySpecifier(Value op, bool _allowConstant = true) + { + if (op == Constant && _allowConstant) + return true; + return op == Pure || op == View || op == Payable; + } static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } static bool isTimeSubdenomination(Value op) { return op == SubSecond || op == SubMinute || op == SubHour || op == SubDay || op == SubWeek || op == SubYear; } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= Unchecked); } diff --git a/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol b/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol new file mode 100644 index 00000000..d98f4ae3 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_constant_payable.sol @@ -0,0 +1,5 @@ +contract C { + address constant payable b = address(0); +} +// ---- +// ParserError: (34-41): Expected identifier but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol b/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol new file mode 100644 index 00000000..c377600a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_function_arguments_and_returns.sol @@ -0,0 +1,4 @@ +contract C { + function f(address) public pure returns (address) {} + function g(address payable) public pure returns (address payable) {} +} diff --git a/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol b/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol new file mode 100644 index 00000000..606f5cd0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_invalid_state_mutability.sol @@ -0,0 +1,26 @@ +contract C { + address view m_a; + address pure m_b; + address view[] m_c; + mapping(uint => address view) m_d; + function f() public pure { + address view a; + address pure b; + a; b; + } + function g(address view) public pure {} + function h(address pure) public pure {} + function i() public pure returns (address view) {} + function j() public pure returns (address pure) {} +} +// ---- +// TypeError: (14-26): Address types can only be payable or non-payable. +// TypeError: (33-45): Address types can only be payable or non-payable. +// TypeError: (52-64): Address types can only be payable or non-payable. +// TypeError: (89-101): Address types can only be payable or non-payable. +// TypeError: (195-207): Address types can only be payable or non-payable. +// TypeError: (236-248): Address types can only be payable or non-payable. +// TypeError: (300-312): Address types can only be payable or non-payable. +// TypeError: (352-364): Address types can only be payable or non-payable. +// TypeError: (138-150): Address types can only be payable or non-payable. +// TypeError: (156-168): Address types can only be payable or non-payable. diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol new file mode 100644 index 00000000..154bfb54 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_constant.sol @@ -0,0 +1,3 @@ +contract C { + address payable constant a = address(0); +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol b/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol new file mode 100644 index 00000000..bf073a52 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_conversion.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address payable a = address payable(this); + } +} +// ---- +// ParserError: (80-87): Expected ';' but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol new file mode 100644 index 00000000..234b528a --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_function_type.sol @@ -0,0 +1,6 @@ +contract C { + function (address payable) view internal returns (address payable) f; + function g(function (address payable) payable external returns (address payable)) public payable returns (function (address payable) payable external returns (address payable)) { + function (address payable) payable external returns (address payable) h; h; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_library.sol b/test/libsolidity/syntaxTests/parsing/address_payable_library.sol new file mode 100644 index 00000000..33787d33 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_library.sol @@ -0,0 +1,5 @@ +library L { +} +contract C { + using L for address payable; +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_local.sol b/test/libsolidity/syntaxTests/parsing/address_payable_local.sol new file mode 100644 index 00000000..544f7c21 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_local.sol @@ -0,0 +1,11 @@ +contract C { + mapping(uint => address payable) m; + mapping(uint => address payable[]) n; + function f() public view { + address payable a; + address payable[] memory b; + mapping(uint => address payable) storage c = m; + mapping(uint => address payable[]) storage d = n; + a; b; c; d; + } +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol b/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol new file mode 100644 index 00000000..12c2468d --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_state_variable.sol @@ -0,0 +1,8 @@ +contract C { + address payable a; + address payable public b; + address payable[] c; + address payable[] public d; + mapping(uint => address payable) e; + mapping(uint => address payable[]) f; +} diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol b/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol new file mode 100644 index 00000000..6c281bc7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_struct.sol @@ -0,0 +1,8 @@ +contract C { + struct S { + address payable a; + address payable[] b; + mapping(uint => address payable) c; + mapping(uint => address payable[]) d; + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol b/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol new file mode 100644 index 00000000..394b39c0 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_payable_type_expression.sol @@ -0,0 +1,7 @@ +contract C { + function f() public pure { + address payable; + } +} +// ---- +// ParserError: (67-68): Expected identifier but got ';' diff --git a/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol b/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol new file mode 100644 index 00000000..0acf5e4b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/address_public_payable_error.sol @@ -0,0 +1,5 @@ +contract C { + address public payable a; +} +// ---- +// ParserError: (32-39): Expected identifier but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol new file mode 100644 index 00000000..41b2762b --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elemantary_non_address_payable_state_variable.sol @@ -0,0 +1,29 @@ +contract C { + bool payable a; + string payable b; + int payable c; + int256 payable d; + uint payable e; + uint256 payable f; + byte payable g; + bytes payable h; + bytes32 payable i; + fixed payable j; + fixed80x80 payable k; + ufixed payable l; + ufixed80x80 payable m; +} +// ---- +// ParserError: (22-29): State mutability can only be specified for address types. +// ParserError: (44-51): State mutability can only be specified for address types. +// ParserError: (63-70): State mutability can only be specified for address types. +// ParserError: (85-92): State mutability can only be specified for address types. +// ParserError: (105-112): State mutability can only be specified for address types. +// ParserError: (128-135): State mutability can only be specified for address types. +// ParserError: (148-155): State mutability can only be specified for address types. +// ParserError: (169-176): State mutability can only be specified for address types. +// ParserError: (192-199): State mutability can only be specified for address types. +// ParserError: (213-220): State mutability can only be specified for address types. +// ParserError: (239-246): State mutability can only be specified for address types. +// ParserError: (261-268): State mutability can only be specified for address types. +// ParserError: (288-295): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol new file mode 100644 index 00000000..9cb0fb4f --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_argument.sol @@ -0,0 +1,29 @@ +contract C { + function a(bool payable) public pure {} + function b(string payable) public pure {} + function c(int payable) public pure {} + function d(int256 payable) public pure {} + function e(uint payable) public pure {} + function f(uint256 payable) public pure {} + function g(byte payable) public pure {} + function h(bytes payable) public pure {} + function i(bytes32 payable) public pure {} + function j(fixed payable) public pure {} + function k(fixed80x80 payable) public pure {} + function l(ufixed payable) public pure {} + function m(ufixed80x80 payable) public pure {} +} +// ---- +// ParserError: (33-40): State mutability can only be specified for address types. +// ParserError: (79-86): State mutability can only be specified for address types. +// ParserError: (122-129): State mutability can only be specified for address types. +// ParserError: (168-175): State mutability can only be specified for address types. +// ParserError: (212-219): State mutability can only be specified for address types. +// ParserError: (259-266): State mutability can only be specified for address types. +// ParserError: (303-310): State mutability can only be specified for address types. +// ParserError: (348-355): State mutability can only be specified for address types. +// ParserError: (395-402): State mutability can only be specified for address types. +// ParserError: (440-447): State mutability can only be specified for address types. +// ParserError: (490-497): State mutability can only be specified for address types. +// ParserError: (536-543): State mutability can only be specified for address types. +// ParserError: (587-594): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol new file mode 100644 index 00000000..3d47f1c7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_local.sol @@ -0,0 +1,31 @@ +contract C { + function f() public pure { + bool payable a; + string payable b; + int payable c; + int256 payable d; + uint payable e; + uint256 payable f; + byte payable g; + bytes payable h; + bytes32 payable i; + fixed payable j; + fixed80x80 payable k; + ufixed payable l; + ufixed80x80 payable m; + } +} +// ---- +// ParserError: (57-64): State mutability can only be specified for address types. +// ParserError: (83-90): State mutability can only be specified for address types. +// ParserError: (106-113): State mutability can only be specified for address types. +// ParserError: (132-139): State mutability can only be specified for address types. +// ParserError: (156-163): State mutability can only be specified for address types. +// ParserError: (183-190): State mutability can only be specified for address types. +// ParserError: (207-214): State mutability can only be specified for address types. +// ParserError: (232-239): State mutability can only be specified for address types. +// ParserError: (259-266): State mutability can only be specified for address types. +// ParserError: (284-291): State mutability can only be specified for address types. +// ParserError: (314-321): State mutability can only be specified for address types. +// ParserError: (340-347): State mutability can only be specified for address types. +// ParserError: (371-378): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol new file mode 100644 index 00000000..fc7a3c25 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/elementary_non_address_payable_return.sol @@ -0,0 +1,29 @@ +contract C { + function a() public pure returns (bool payable) {} + function b() public pure returns (string payable) {} + function c() public pure returns (int payable) {} + function d() public pure returns (int256 payable) {} + function e() public pure returns (uint payable) {} + function f() public pure returns (uint256 payable) {} + function g() public pure returns (byte payable) {} + function h() public pure returns (bytes payable) {} + function i() public pure returns (bytes32 payable) {} + function j() public pure returns (fixed payable) {} + function k() public pure returns (fixed80x80 payable) {} + function l() public pure returns (ufixed payable) {} + function m() public pure returns (ufixed80x80 payable) {} +} +// ---- +// ParserError: (56-63): State mutability can only be specified for address types. +// ParserError: (113-120): State mutability can only be specified for address types. +// ParserError: (167-174): State mutability can only be specified for address types. +// ParserError: (224-231): State mutability can only be specified for address types. +// ParserError: (279-286): State mutability can only be specified for address types. +// ParserError: (337-344): State mutability can only be specified for address types. +// ParserError: (392-399): State mutability can only be specified for address types. +// ParserError: (448-455): State mutability can only be specified for address types. +// ParserError: (506-513): State mutability can only be specified for address types. +// ParserError: (562-569): State mutability can only be specified for address types. +// ParserError: (623-630): State mutability can only be specified for address types. +// ParserError: (680-687): State mutability can only be specified for address types. +// ParserError: (742-749): State mutability can only be specified for address types. diff --git a/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol b/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol new file mode 100644 index 00000000..7e6f98d7 --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/mapping_from_address_payable.sol @@ -0,0 +1,5 @@ +contract C { + mapping(address payable => uint) m; +} +// ---- +// ParserError: (33-40): Expected '=>' but got 'payable' diff --git a/test/libsolidity/syntaxTests/parsing/new_address_payable.sol b/test/libsolidity/syntaxTests/parsing/new_address_payable.sol new file mode 100644 index 00000000..a52001db --- /dev/null +++ b/test/libsolidity/syntaxTests/parsing/new_address_payable.sol @@ -0,0 +1,5 @@ +contract C { + function f() public pure returns(address payable[] memory m) { + m = new address payable[](10); + } +}
\ No newline at end of file diff --git a/test/libsolidity/syntaxTests/parsing/payable_accessor.sol b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol index 6504004b..0bc85784 100644 --- a/test/libsolidity/syntaxTests/parsing/payable_accessor.sol +++ b/test/libsolidity/syntaxTests/parsing/payable_accessor.sol @@ -2,4 +2,4 @@ contract test { uint payable x; } // ---- -// ParserError: (22-29): Expected identifier but got 'payable' +// ParserError: (22-29): State mutability can only be specified for address types. |