Merge pull request #4926 from ethereum/addressPayableParser

Accept ``address payable`` during parsing.
This commit is contained in:
chriseth 2018-09-11 15:29:53 +02:00 committed by GitHub
commit 1994b51ef3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 285 additions and 12 deletions

View File

@ -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.

View File

@ -50,6 +50,7 @@ TypeName = ElementaryTypeName
| Mapping
| ArrayTypeName
| FunctionTypeName
| ( 'address' 'payable' )
UserDefinedTypeName = Identifier ( '.' Identifier )*

View File

@ -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;
}

View File

@ -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
};
/**

View File

@ -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)

View File

@ -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); }

View File

@ -0,0 +1,5 @@
contract C {
address constant payable b = address(0);
}
// ----
// ParserError: (34-41): Expected identifier but got 'payable'

View File

@ -0,0 +1,4 @@
contract C {
function f(address) public pure returns (address) {}
function g(address payable) public pure returns (address payable) {}
}

View File

@ -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.

View File

@ -0,0 +1,3 @@
contract C {
address payable constant a = address(0);
}

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure {
address payable a = address payable(this);
}
}
// ----
// ParserError: (80-87): Expected ';' but got 'payable'

View File

@ -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;
}
}

View File

@ -0,0 +1,5 @@
library L {
}
contract C {
using L for address payable;
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -0,0 +1,8 @@
contract C {
struct S {
address payable a;
address payable[] b;
mapping(uint => address payable) c;
mapping(uint => address payable[]) d;
}
}

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure {
address payable;
}
}
// ----
// ParserError: (67-68): Expected identifier but got ';'

View File

@ -0,0 +1,5 @@
contract C {
address public payable a;
}
// ----
// ParserError: (32-39): Expected identifier but got 'payable'

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -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.

View File

@ -0,0 +1,5 @@
contract C {
mapping(address payable => uint) m;
}
// ----
// ParserError: (33-40): Expected '=>' but got 'payable'

View File

@ -0,0 +1,5 @@
contract C {
function f() public pure returns(address payable[] memory m) {
m = new address payable[](10);
}
}

View File

@ -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.