Merge pull request #2722 from ethereum/statemutability

Introduce state mutability (to replace const/payable)
This commit is contained in:
Alex Beregszaszi 2017-08-14 15:36:47 +01:00 committed by GitHub
commit 0a04a35a2e
12 changed files with 141 additions and 105 deletions

View File

@ -500,8 +500,6 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(_function.location(), "Library functions cannot be payable."); m_errorReporter.typeError(_function.location(), "Library functions cannot be payable.");
if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface()) if (!_function.isConstructor() && !_function.isFallback() && !_function.isPartOfExternalInterface())
m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable."); m_errorReporter.typeError(_function.location(), "Internal functions cannot be payable.");
if (_function.isDeclaredConst())
m_errorReporter.typeError(_function.location(), "Functions cannot be constant and payable at the same time.");
} }
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters()) for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{ {

View File

@ -28,6 +28,7 @@
#include <libsolidity/ast/Types.h> #include <libsolidity/ast/Types.h>
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTAnnotations.h> #include <libsolidity/ast/ASTAnnotations.h>
#include <libsolidity/ast/ASTEnums.h>
#include <libevmasm/SourceLocation.h> #include <libevmasm/SourceLocation.h>
#include <libevmasm/Instruction.h> #include <libevmasm/Instruction.h>
@ -584,21 +585,19 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
Declaration::Visibility _visibility, Declaration::Visibility _visibility,
StateMutability _stateMutability,
bool _isConstructor, bool _isConstructor,
ASTPointer<ASTString> const& _documentation, ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
bool _isPayable,
ASTPointer<Block> const& _body ASTPointer<Block> const& _body
): ):
CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters), CallableDeclaration(_location, _name, _visibility, _parameters, _returnParameters),
Documented(_documentation), Documented(_documentation),
ImplementationOptional(_body != nullptr), ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability),
m_isConstructor(_isConstructor), m_isConstructor(_isConstructor),
m_isDeclaredConst(_isDeclaredConst),
m_isPayable(_isPayable),
m_functionModifiers(_modifiers), m_functionModifiers(_modifiers),
m_body(_body) m_body(_body)
{} {}
@ -606,10 +605,11 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; } bool isConstructor() const { return m_isConstructor; }
bool isFallback() const { return name().empty(); } bool isFallback() const { return name().empty(); }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; }
bool isPayable() const { return m_isPayable; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; } std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); } std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
Block const& body() const { solAssert(m_body, ""); return *m_body; } Block const& body() const { solAssert(m_body, ""); return *m_body; }
@ -634,9 +634,8 @@ public:
virtual FunctionDefinitionAnnotation& annotation() const override; virtual FunctionDefinitionAnnotation& annotation() const override;
private: private:
StateMutability m_stateMutability;
bool m_isConstructor; bool m_isConstructor;
bool m_isDeclaredConst;
bool m_isPayable;
std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers; std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<Block> m_body; ASTPointer<Block> m_body;
}; };
@ -896,11 +895,10 @@ public:
ASTPointer<ParameterList> const& _parameterTypes, ASTPointer<ParameterList> const& _parameterTypes,
ASTPointer<ParameterList> const& _returnTypes, ASTPointer<ParameterList> const& _returnTypes,
Declaration::Visibility _visibility, Declaration::Visibility _visibility,
bool _isDeclaredConst, StateMutability _stateMutability
bool _isPayable
): ):
TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes), TypeName(_location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes),
m_visibility(_visibility), m_isDeclaredConst(_isDeclaredConst), m_isPayable(_isPayable) m_visibility(_visibility), m_stateMutability(_stateMutability)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override; virtual void accept(ASTConstVisitor& _visitor) const override;
@ -914,15 +912,15 @@ public:
{ {
return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility; return m_visibility == Declaration::Visibility::Default ? Declaration::Visibility::Internal : m_visibility;
} }
bool isDeclaredConst() const { return m_isDeclaredConst; } StateMutability stateMutability() const { return m_stateMutability; }
bool isPayable() const { return m_isPayable; } bool isDeclaredConst() const { return m_stateMutability == StateMutability::View; }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
private: private:
ASTPointer<ParameterList> m_parameterTypes; ASTPointer<ParameterList> m_parameterTypes;
ASTPointer<ParameterList> m_returnTypes; ASTPointer<ParameterList> m_returnTypes;
Declaration::Visibility m_visibility; Declaration::Visibility m_visibility;
bool m_isDeclaredConst; StateMutability m_stateMutability;
bool m_isPayable;
}; };
/** /**

View File

@ -0,0 +1,52 @@
/*
This file is part of solidity.
solidity is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
solidity is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with solidity. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @date 2017
* Enums for AST classes.
*/
#pragma once
#include <libsolidity/interface/Exceptions.h>
#include <string>
namespace dev
{
namespace solidity
{
// How a function can mutate the EVM state.
enum class StateMutability { View, NonPayable, Payable };
inline std::string stateMutabilityToString(StateMutability const& _stateMutability)
{
switch(_stateMutability)
{
case StateMutability::View:
return "view";
case StateMutability::NonPayable:
return "nonpayable";
case StateMutability::Payable:
return "payable";
default:
solAssert(false, "Unknown state mutability.");
}
}
}
}

View File

@ -95,6 +95,5 @@ using ASTPointer = std::shared_ptr<T>;
using ASTString = std::string; using ASTString = std::string;
} }
} }

View File

@ -477,8 +477,8 @@ MemberList::MemberMap IntegerType::nativeMembers(ContractDefinition const*) cons
if (isAddress()) if (isAddress())
return { return {
{"balance", make_shared<IntegerType >(256)}, {"balance", make_shared<IntegerType >(256)},
{"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, false, true)}, {"call", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCall, true, StateMutability::Payable)},
{"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, false, true)}, {"callcode", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareCallCode, true, StateMutability::Payable)},
{"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)}, {"delegatecall", make_shared<FunctionType>(strings(), strings{"bool"}, FunctionType::Kind::BareDelegateCall, true)},
{"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)}, {"send", make_shared<FunctionType>(strings{"uint"}, strings{"bool"}, FunctionType::Kind::Send)},
{"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)} {"transfer", make_shared<FunctionType>(strings{"uint"}, strings(), FunctionType::Kind::Transfer)}
@ -2000,8 +2000,7 @@ TypePointer TupleType::closestTemporaryType(TypePointer const& _targetType) cons
FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal): FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal):
m_kind(_isInternal ? Kind::Internal : Kind::External), m_kind(_isInternal ? Kind::Internal : Kind::External),
m_isConstant(_function.isDeclaredConst()), m_stateMutability(_function.stateMutability()),
m_isPayable(_isInternal ? false : _function.isPayable()),
m_declaration(&_function) m_declaration(&_function)
{ {
TypePointers params; TypePointers params;
@ -2009,6 +2008,9 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
TypePointers retParams; TypePointers retParams;
vector<string> retParamNames; vector<string> retParamNames;
if (_isInternal && m_stateMutability == StateMutability::Payable)
m_stateMutability = StateMutability::NonPayable;
params.reserve(_function.parameters().size()); params.reserve(_function.parameters().size());
paramNames.reserve(_function.parameters().size()); paramNames.reserve(_function.parameters().size());
for (ASTPointer<VariableDeclaration> const& var: _function.parameters()) for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
@ -2030,7 +2032,7 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
} }
FunctionType::FunctionType(VariableDeclaration const& _varDecl): FunctionType::FunctionType(VariableDeclaration const& _varDecl):
m_kind(Kind::External), m_isConstant(true), m_declaration(&_varDecl) m_kind(Kind::External), m_stateMutability(StateMutability::View), m_declaration(&_varDecl)
{ {
TypePointers paramTypes; TypePointers paramTypes;
vector<string> paramNames; vector<string> paramNames;
@ -2090,7 +2092,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
} }
FunctionType::FunctionType(EventDefinition const& _event): FunctionType::FunctionType(EventDefinition const& _event):
m_kind(Kind::Event), m_isConstant(true), m_declaration(&_event) m_kind(Kind::Event), m_stateMutability(StateMutability::View), m_declaration(&_event)
{ {
TypePointers params; TypePointers params;
vector<string> paramNames; vector<string> paramNames;
@ -2107,14 +2109,10 @@ FunctionType::FunctionType(EventDefinition const& _event):
FunctionType::FunctionType(FunctionTypeName const& _typeName): FunctionType::FunctionType(FunctionTypeName const& _typeName):
m_kind(_typeName.visibility() == VariableDeclaration::Visibility::External ? Kind::External : Kind::Internal), m_kind(_typeName.visibility() == VariableDeclaration::Visibility::External ? Kind::External : Kind::Internal),
m_isConstant(_typeName.isDeclaredConst()), m_stateMutability(_typeName.stateMutability())
m_isPayable(_typeName.isPayable())
{ {
if (_typeName.isPayable()) if (_typeName.isPayable())
{
solAssert(m_kind == Kind::External, "Internal payable function type used."); solAssert(m_kind == Kind::External, "Internal payable function type used.");
solAssert(!m_isConstant, "Payable constant function");
}
for (auto const& t: _typeName.parameterTypes()) for (auto const& t: _typeName.parameterTypes())
{ {
solAssert(t->annotation().type, "Type not set for parameter."); solAssert(t->annotation().type, "Type not set for parameter.");
@ -2142,7 +2140,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
FunctionDefinition const* constructor = _contract.constructor(); FunctionDefinition const* constructor = _contract.constructor();
TypePointers parameters; TypePointers parameters;
strings parameterNames; strings parameterNames;
bool payable = false; StateMutability stateMutability = StateMutability::NonPayable;
if (constructor) if (constructor)
{ {
@ -2151,7 +2149,8 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
parameterNames.push_back(var->name()); parameterNames.push_back(var->name());
parameters.push_back(var->annotation().type); parameters.push_back(var->annotation().type);
} }
payable = constructor->isPayable(); if (constructor->isPayable())
stateMutability = StateMutability::Payable;
} }
return make_shared<FunctionType>( return make_shared<FunctionType>(
parameters, parameters,
@ -2161,8 +2160,7 @@ FunctionTypePointer FunctionType::newExpressionType(ContractDefinition const& _c
Kind::Creation, Kind::Creation,
false, false,
nullptr, nullptr,
false, stateMutability
payable
); );
} }
@ -2241,8 +2239,8 @@ bool FunctionType::operator==(Type const& _other) const
FunctionType const& other = dynamic_cast<FunctionType const&>(_other); FunctionType const& other = dynamic_cast<FunctionType const&>(_other);
if ( if (
m_kind != other.m_kind || m_kind != other.m_kind ||
m_isConstant != other.isConstant() || isConstant() != other.isConstant() ||
m_isPayable != other.isPayable() || isPayable() != other.isPayable() ||
m_parameterTypes.size() != other.m_parameterTypes.size() || m_parameterTypes.size() != other.m_parameterTypes.size() ||
m_returnParameterTypes.size() != other.m_returnParameterTypes.size() m_returnParameterTypes.size() != other.m_returnParameterTypes.size()
) )
@ -2304,9 +2302,9 @@ string FunctionType::toString(bool _short) const
for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it) for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ","); name += (*it)->toString(_short) + (it + 1 == m_parameterTypes.end() ? "" : ",");
name += ")"; name += ")";
if (m_isConstant) if (isConstant())
name += " constant"; name += " constant";
if (m_isPayable) if (isPayable())
name += " payable"; name += " payable";
if (m_kind == Kind::External) if (m_kind == Kind::External)
name += " external"; name += " external";
@ -2420,8 +2418,7 @@ FunctionTypePointer FunctionType::interfaceFunctionType() const
m_kind, m_kind,
m_arbitraryParameters, m_arbitraryParameters,
m_declaration, m_declaration,
m_isConstant, m_stateMutability
m_isPayable
); );
} }
@ -2438,7 +2435,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
MemberList::MemberMap members; MemberList::MemberMap members;
if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall) if (m_kind != Kind::BareDelegateCall && m_kind != Kind::DelegateCall)
{ {
if (m_isPayable) if (isPayable())
members.push_back(MemberList::Member( members.push_back(MemberList::Member(
"value", "value",
make_shared<FunctionType>( make_shared<FunctionType>(
@ -2449,8 +2446,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetValue, Kind::SetValue,
false, false,
nullptr, nullptr,
false, StateMutability::NonPayable,
false,
m_gasSet, m_gasSet,
m_valueSet m_valueSet
) )
@ -2467,8 +2463,7 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const*) con
Kind::SetGas, Kind::SetGas,
false, false,
nullptr, nullptr,
false, StateMutability::NonPayable,
false,
m_gasSet, m_gasSet,
m_valueSet m_valueSet
) )
@ -2604,8 +2599,7 @@ TypePointer FunctionType::copyAndSetGasOrValue(bool _setGas, bool _setValue) con
m_kind, m_kind,
m_arbitraryParameters, m_arbitraryParameters,
m_declaration, m_declaration,
m_isConstant, m_stateMutability,
m_isPayable,
m_gasSet || _setGas, m_gasSet || _setGas,
m_valueSet || _setValue, m_valueSet || _setValue,
m_bound m_bound
@ -2654,8 +2648,7 @@ FunctionTypePointer FunctionType::asMemberFunction(bool _inLibrary, bool _bound)
kind, kind,
m_arbitraryParameters, m_arbitraryParameters,
m_declaration, m_declaration,
m_isConstant, m_stateMutability,
m_isPayable,
m_gasSet, m_gasSet,
m_valueSet, m_valueSet,
_bound _bound

View File

@ -24,6 +24,7 @@
#include <libsolidity/interface/Exceptions.h> #include <libsolidity/interface/Exceptions.h>
#include <libsolidity/ast/ASTForward.h> #include <libsolidity/ast/ASTForward.h>
#include <libsolidity/ast/ASTEnums.h>
#include <libsolidity/parsing/Token.h> #include <libsolidity/parsing/Token.h>
#include <libdevcore/Common.h> #include <libdevcore/Common.h>
@ -889,8 +890,7 @@ public:
strings const& _returnParameterTypes, strings const& _returnParameterTypes,
Kind _kind = Kind::Internal, Kind _kind = Kind::Internal,
bool _arbitraryParameters = false, bool _arbitraryParameters = false,
bool _constant = false, StateMutability _stateMutability = StateMutability::NonPayable
bool _payable = false
): FunctionType( ): FunctionType(
parseElementaryTypeVector(_parameterTypes), parseElementaryTypeVector(_parameterTypes),
parseElementaryTypeVector(_returnParameterTypes), parseElementaryTypeVector(_returnParameterTypes),
@ -899,8 +899,7 @@ public:
_kind, _kind,
_arbitraryParameters, _arbitraryParameters,
nullptr, nullptr,
_constant, _stateMutability
_payable
) )
{ {
} }
@ -917,8 +916,7 @@ public:
Kind _kind = Kind::Internal, Kind _kind = Kind::Internal,
bool _arbitraryParameters = false, bool _arbitraryParameters = false,
Declaration const* _declaration = nullptr, Declaration const* _declaration = nullptr,
bool _isConstant = false, StateMutability _stateMutability = StateMutability::NonPayable,
bool _isPayable = false,
bool _gasSet = false, bool _gasSet = false,
bool _valueSet = false, bool _valueSet = false,
bool _bound = false bool _bound = false
@ -928,12 +926,11 @@ public:
m_parameterNames(_parameterNames), m_parameterNames(_parameterNames),
m_returnParameterNames(_returnParameterNames), m_returnParameterNames(_returnParameterNames),
m_kind(_kind), m_kind(_kind),
m_stateMutability(_stateMutability),
m_arbitraryParameters(_arbitraryParameters), m_arbitraryParameters(_arbitraryParameters),
m_gasSet(_gasSet), m_gasSet(_gasSet),
m_valueSet(_valueSet), m_valueSet(_valueSet),
m_bound(_bound), m_bound(_bound),
m_isConstant(_isConstant),
m_isPayable(_isPayable),
m_declaration(_declaration) m_declaration(_declaration)
{ {
solAssert( solAssert(
@ -985,6 +982,7 @@ public:
/// @returns true if the ABI is used for this call (only meaningful for external calls) /// @returns true if the ABI is used for this call (only meaningful for external calls)
bool isBareCall() const; bool isBareCall() const;
Kind const& kind() const { return m_kind; } Kind const& kind() const { return m_kind; }
StateMutability stateMutability() const { return m_stateMutability; }
/// @returns the external signature of this function type given the function name /// @returns the external signature of this function type given the function name
std::string externalSignature() const; std::string externalSignature() const;
/// @returns the external identifier of this function (the hash of the signature). /// @returns the external identifier of this function (the hash of the signature).
@ -995,12 +993,12 @@ public:
return *m_declaration; return *m_declaration;
} }
bool hasDeclaration() const { return !!m_declaration; } bool hasDeclaration() const { return !!m_declaration; }
bool isConstant() const { return m_isConstant; } bool isConstant() const { return m_stateMutability == StateMutability::View; }
/// @returns true if the the result of this function only depends on its arguments /// @returns true if the the result of this function only depends on its arguments
/// and it does not modify the state. /// and it does not modify the state.
/// Currently, this will only return true for internal functions like keccak and ecrecover. /// Currently, this will only return true for internal functions like keccak and ecrecover.
bool isPure() const; bool isPure() const;
bool isPayable() const { return m_isPayable; } bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> documentation() const; ASTPointer<ASTString> documentation() const;
@ -1033,13 +1031,12 @@ private:
std::vector<std::string> m_parameterNames; std::vector<std::string> m_parameterNames;
std::vector<std::string> m_returnParameterNames; std::vector<std::string> m_returnParameterNames;
Kind const m_kind; Kind const m_kind;
StateMutability m_stateMutability = StateMutability::NonPayable;
/// true if the function takes an arbitrary number of arguments of arbitrary types /// true if the function takes an arbitrary number of arguments of arbitrary types
bool const m_arbitraryParameters = false; bool const m_arbitraryParameters = false;
bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack bool const m_gasSet = false; ///< true iff the gas value to be used is on the stack
bool const m_valueSet = false; ///< true iff the value to be sent is on the stack bool const m_valueSet = false; ///< true iff the value to be sent is on the stack
bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn) bool const m_bound = false; ///< true iff the function is called as arg1.fun(arg2, ..., argn)
bool m_isConstant = false;
bool m_isPayable = false;
Declaration const* m_declaration = nullptr; Declaration const* m_declaration = nullptr;
}; };

View File

@ -645,8 +645,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
FunctionType::Kind::BareCall, FunctionType::Kind::BareCall,
false, false,
nullptr, nullptr,
false, StateMutability::NonPayable,
false,
true, true,
true true
), ),

View File

@ -307,6 +307,19 @@ Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
return visibility; return visibility;
} }
StateMutability Parser::parseStateMutability(Token::Value _token)
{
StateMutability stateMutability(StateMutability::NonPayable);
if (_token == Token::Payable)
stateMutability = StateMutability::Payable;
else if (_token == Token::Constant)
stateMutability = StateMutability::View;
else
solAssert(false, "Invalid state mutability specifier.");
m_scanner->next();
return stateMutability;
}
Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers) Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
{ {
FunctionHeaderParserResult result; FunctionHeaderParserResult result;
@ -321,23 +334,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
while (true) while (true)
{ {
Token::Value token = m_scanner->currentToken(); Token::Value token = m_scanner->currentToken();
if (token == Token::Constant) if (_allowModifiers && token == Token::Identifier)
{
if (result.isDeclaredConst)
parserError(string("Multiple \"constant\" specifiers."));
result.isDeclaredConst = true;
m_scanner->next();
}
else if (m_scanner->currentToken() == Token::Payable)
{
if (result.isPayable)
parserError(string("Multiple \"payable\" specifiers."));
result.isPayable = true;
m_scanner->next();
}
else if (_allowModifiers && token == Token::Identifier)
{ {
// This can either be a modifier (function declaration) or the name of the // This can either be a modifier (function declaration) or the name of the
// variable (function type name plus variable). // variable (function type name plus variable).
@ -364,6 +361,20 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyN
else else
result.visibility = parseVisibilitySpecifier(token); result.visibility = parseVisibilitySpecifier(token);
} }
else if (Token::isStateMutabilitySpecifier(token))
{
if (result.stateMutability != StateMutability::NonPayable)
{
parserError(string(
"State mutability already specified as \"" +
stateMutabilityToString(result.stateMutability) +
"\"."
));
m_scanner->next();
}
else
result.stateMutability = parseStateMutability(token);
}
else else
break; break;
} }
@ -408,13 +419,12 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
return nodeFactory.createNode<FunctionDefinition>( return nodeFactory.createNode<FunctionDefinition>(
header.name, header.name,
header.visibility, header.visibility,
header.stateMutability,
c_isConstructor, c_isConstructor,
docstring, docstring,
header.parameters, header.parameters,
header.isDeclaredConst,
header.modifiers, header.modifiers,
header.returnParameters, header.returnParameters,
header.isPayable,
block block
); );
} }
@ -425,8 +435,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
header.parameters, header.parameters,
header.returnParameters, header.returnParameters,
header.visibility, header.visibility,
header.isDeclaredConst, header.stateMutability
header.isPayable
); );
type = parseTypeNameSuffix(type, nodeFactory); type = parseTypeNameSuffix(type, nodeFactory);
VarDeclParserOptions options; VarDeclParserOptions options;
@ -751,8 +760,7 @@ ASTPointer<FunctionTypeName> Parser::parseFunctionType()
header.parameters, header.parameters,
header.returnParameters, header.returnParameters,
header.visibility, header.visibility,
header.isDeclaredConst, header.stateMutability
header.isPayable
); );
} }

View File

@ -60,8 +60,7 @@ private:
ASTPointer<ParameterList> parameters; ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters; ASTPointer<ParameterList> returnParameters;
Declaration::Visibility visibility = Declaration::Visibility::Default; Declaration::Visibility visibility = Declaration::Visibility::Default;
bool isDeclaredConst = false; StateMutability stateMutability = StateMutability::NonPayable;
bool isPayable = false;
std::vector<ASTPointer<ModifierInvocation>> modifiers; std::vector<ASTPointer<ModifierInvocation>> modifiers;
}; };
@ -73,7 +72,7 @@ private:
ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind); ASTPointer<ContractDefinition> parseContractDefinition(Token::Value _expectedKind);
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier(); ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token); Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
std::string visibilitySpecifierName(Declaration::Visibility _visibility); StateMutability parseStateMutability(Token::Value _token);
FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers); FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName); ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName); ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);

View File

@ -290,6 +290,7 @@ public:
static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; } static bool isVisibilitySpecifier(Value op) { return isVariableVisibilitySpecifier(op) || op == External; }
static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; } static bool isVariableVisibilitySpecifier(Value op) { return op == Public || op == Private || op == Internal; }
static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; } static bool isLocationSpecifier(Value op) { return op == Memory || op == Storage; }
static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == Payable; }
static bool isEtherSubdenomination(Value op) { return op == SubWei || op == SubSzabo || op == SubFinney || op == SubEther; } 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 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 <= TypeOf); } static bool isReservedKeyword(Value op) { return (Abstract <= op && op <= TypeOf); }

View File

@ -4771,15 +4771,6 @@ BOOST_AUTO_TEST_CASE(function_variable_mixin)
CHECK_ERROR(text, DeclarationError, "Identifier already declared."); CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
} }
BOOST_AUTO_TEST_CASE(payable_constant_conflict)
{
char const* text = R"(
contract C { function f() payable constant {} }
)";
CHECK_ERROR(text, TypeError, "Functions cannot be constant and payable at the same time.");
}
BOOST_AUTO_TEST_CASE(calling_payable) BOOST_AUTO_TEST_CASE(calling_payable)
{ {
char const* text = R"( char const* text = R"(

View File

@ -906,22 +906,23 @@ BOOST_AUTO_TEST_CASE(multiple_visibility_specifiers)
CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\"."); CHECK_PARSE_ERROR(text, "Visibility already specified as \"private\".");
} }
BOOST_AUTO_TEST_CASE(multiple_payable_specifiers) BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers)
{ {
char const* text = R"( char const* text = R"(
contract c { contract c {
function f() payable payable {} function f() payable payable {}
})"; })";
CHECK_PARSE_ERROR(text, "Multiple \"payable\" specifiers."); CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
} text = R"(
BOOST_AUTO_TEST_CASE(multiple_constant_specifiers)
{
char const* text = R"(
contract c { contract c {
function f() constant constant {} function f() constant constant {}
})"; })";
CHECK_PARSE_ERROR(text, "Multiple \"constant\" specifiers."); CHECK_PARSE_ERROR(text, "State mutability already specified as \"view\".");
text = R"(
contract c {
function f() payable constant {}
})";
CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
} }
BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations) BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations)