mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #2745 from ethereum/statemutability-pure
Introduce pure specifier on functions
This commit is contained in:
commit
d3fd6a8800
@ -1,7 +1,8 @@
|
||||
### 0.4.16 (unreleased)
|
||||
|
||||
Features:
|
||||
* ABI JSON: Include new field ``statemutability`` with values ``view``, ``nonpayable`` and ``payable``.
|
||||
* Introduce ``pure`` functions. The pureness is not enforced yet, use with care.
|
||||
* ABI JSON: Include new field ``statemutability`` with values ``pure``, ``view``, ``nonpayable`` and ``payable``.
|
||||
* Analyzer: Experimental partial support for Z3 SMT checker.
|
||||
* Parser: Display previous visibility specifier in error if multiple are found.
|
||||
* Parser: Introduce ``view`` keyword on functions (``constant`` remains an alias for ``view``).
|
||||
|
@ -295,7 +295,7 @@ The JSON format for a contract's interface is given by an array of function and/
|
||||
- `outputs`: an array of objects similar to `inputs`, can be omitted if function doesn't return anything;
|
||||
- `constant`: `true` if function is :ref:`specified to not modify blockchain state <view-functions>`);
|
||||
- `payable`: `true` if function accepts ether, defaults to `false`;
|
||||
- `statemutability`: a string with one of the following values: `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
|
||||
- `statemutability`: a string with one of the following values: `pure` (:ref:`specified to not read blockchain state <pure-functions>`), `view` (same as `constant` above), `nonpayable` and `payable` (same as `payable` above).
|
||||
|
||||
`type` can be omitted, defaulting to `"function"`.
|
||||
|
||||
|
@ -475,7 +475,7 @@ Functions can be declared ``view`` in which case they promise not to modify the
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint b) view returns (uint) {
|
||||
return a * (b + 42);
|
||||
return a * (b + 42) + now;
|
||||
}
|
||||
}
|
||||
|
||||
@ -488,6 +488,27 @@ Functions can be declared ``view`` in which case they promise not to modify the
|
||||
.. warning::
|
||||
The compiler does not enforce yet that a ``view`` method is not modifying state.
|
||||
|
||||
.. _pure-functions:
|
||||
|
||||
**************
|
||||
Pure Functions
|
||||
**************
|
||||
|
||||
Functions can be declared ``pure`` in which case they promise not to read from or modify the state.
|
||||
|
||||
::
|
||||
|
||||
pragma solidity ^0.4.0;
|
||||
|
||||
contract C {
|
||||
function f(uint a, uint b) pure returns (uint) {
|
||||
return a * (b + 42);
|
||||
}
|
||||
}
|
||||
|
||||
.. warning::
|
||||
The compiler does not enforce yet that a ``pure`` method is not reading from the state.
|
||||
|
||||
.. index:: ! fallback function, function;fallback
|
||||
|
||||
.. _fallback-function:
|
||||
|
@ -53,7 +53,7 @@ ArrayTypeName = TypeName '[' Expression? ']'
|
||||
FunctionTypeName = 'function' TypeNameList ( 'internal' | 'external' | StateMutability )*
|
||||
( 'returns' TypeNameList )?
|
||||
StorageLocation = 'memory' | 'storage'
|
||||
StateMutability = 'constant' | 'view' | 'payable'
|
||||
StateMutability = 'pure' | 'constant' | 'view' | 'payable'
|
||||
|
||||
Block = '{' Statement* '}'
|
||||
Statement = IfStatement | WhileStatement | ForStatement | Block | InlineAssemblyStatement |
|
||||
|
@ -500,12 +500,13 @@ Function Visibility Specifiers
|
||||
- ``internal``: only visible internally
|
||||
|
||||
|
||||
.. index:: modifiers, constant, anonymous, indexed
|
||||
.. index:: modifiers, pure, view, payable, constant, anonymous, indexed
|
||||
|
||||
Modifiers
|
||||
=========
|
||||
|
||||
- ``view`` for functions: Disallow modification of state - this is not enforced yet.
|
||||
- ``pure`` for functions: Disallows modification or access of state - this is not enforced yet.
|
||||
- ``view`` for functions: Disallows modification of state - this is not enforced yet.
|
||||
- ``payable`` for functions: Allows them to receive Ether together with a call.
|
||||
- ``constant`` for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
|
||||
- ``constant`` for functions: Same as ``view``.
|
||||
|
@ -337,7 +337,7 @@ be passed via and returned from external function calls.
|
||||
|
||||
Function types are notated as follows::
|
||||
|
||||
function (<parameter types>) {internal|external} [constant|view|payable] [returns (<return types>)]
|
||||
function (<parameter types>) {internal|external} [pure|constant|view|payable] [returns (<return types>)]
|
||||
|
||||
In contrast to the parameter types, the return types cannot be empty - if the
|
||||
function type should not return anything, the whole ``returns (<return types>)``
|
||||
|
@ -57,6 +57,8 @@ bool StaticAnalyzer::visit(FunctionDefinition const& _function)
|
||||
solAssert(m_localVarUseCount.empty(), "");
|
||||
m_nonPayablePublic = _function.isPublic() && !_function.isPayable();
|
||||
m_constructor = _function.isConstructor();
|
||||
if (_function.stateMutability() == StateMutability::Pure)
|
||||
m_errorReporter.warning(_function.location(), "Function is marked pure. Be careful, pureness is not enforced yet.");
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -31,12 +31,14 @@ namespace solidity
|
||||
{
|
||||
|
||||
// How a function can mutate the EVM state.
|
||||
enum class StateMutability { View, NonPayable, Payable };
|
||||
enum class StateMutability { Pure, View, NonPayable, Payable };
|
||||
|
||||
inline std::string stateMutabilityToString(StateMutability const& _stateMutability)
|
||||
{
|
||||
switch(_stateMutability)
|
||||
{
|
||||
case StateMutability::Pure:
|
||||
return "pure";
|
||||
case StateMutability::View:
|
||||
return "view";
|
||||
case StateMutability::NonPayable:
|
||||
|
@ -325,7 +325,7 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
|
||||
std::vector<pair<string, Json::Value>> attributes = {
|
||||
make_pair("name", _node.name()),
|
||||
// FIXME: remove with next breaking release
|
||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
|
||||
make_pair("payable", _node.isPayable()),
|
||||
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
||||
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
||||
@ -418,7 +418,7 @@ bool ASTJsonConverter::visit(FunctionTypeName const& _node)
|
||||
make_pair("visibility", Declaration::visibilityToString(_node.visibility())),
|
||||
make_pair("statemutability", stateMutabilityToString(_node.stateMutability())),
|
||||
// FIXME: remove with next breaking release
|
||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() == StateMutability::View),
|
||||
make_pair(m_legacy ? "constant" : "isDeclaredConst", _node.stateMutability() <= StateMutability::View),
|
||||
make_pair("parameterTypes", toJson(*_node.parameterTypeList())),
|
||||
make_pair("returnParameterTypes", toJson(*_node.returnParameterTypeList())),
|
||||
make_pair("typeDescriptions", typePointerToJson(_node.annotation().type))
|
||||
|
@ -36,7 +36,7 @@ Json::Value ABI::generate(ContractDefinition const& _contractDef)
|
||||
method["type"] = "function";
|
||||
method["name"] = it.second->declaration().name();
|
||||
// TODO: deprecate constant in a future release
|
||||
method["constant"] = it.second->stateMutability() == StateMutability::View;
|
||||
method["constant"] = it.second->stateMutability() == StateMutability::Pure || it.second->stateMutability() == StateMutability::View;
|
||||
method["payable"] = it.second->isPayable();
|
||||
method["statemutability"] = stateMutabilityToString(it.second->stateMutability());
|
||||
method["inputs"] = formatTypeList(
|
||||
|
@ -321,6 +321,8 @@ StateMutability Parser::parseStateMutability(Token::Value _token)
|
||||
// FIXME: constant should be removed at the next breaking release
|
||||
else if (_token == Token::View || _token == Token::Constant)
|
||||
stateMutability = StateMutability::View;
|
||||
else if (_token == Token::Pure)
|
||||
stateMutability = StateMutability::Pure;
|
||||
else
|
||||
solAssert(false, "Invalid state mutability specifier.");
|
||||
m_scanner->next();
|
||||
|
@ -169,6 +169,7 @@ namespace solidity
|
||||
K(Public, "public", 0) \
|
||||
K(Pragma, "pragma", 0) \
|
||||
K(Private, "private", 0) \
|
||||
K(Pure, "pure", 0) \
|
||||
K(Return, "return", 0) \
|
||||
K(Returns, "returns", 0) \
|
||||
K(Storage, "storage", 0) \
|
||||
@ -230,7 +231,6 @@ namespace solidity
|
||||
K(Match, "match", 0) \
|
||||
K(NullLiteral, "null", 0) \
|
||||
K(Of, "of", 0) \
|
||||
K(Pure, "pure", 0) \
|
||||
K(Relocatable, "relocatable", 0) \
|
||||
K(Static, "static", 0) \
|
||||
K(Switch, "switch", 0) \
|
||||
@ -290,7 +290,7 @@ 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; }
|
||||
static bool isStateMutabilitySpecifier(Value op) { return op == Constant || op == View || op == Payable; }
|
||||
static bool isStateMutabilitySpecifier(Value op) { return op == Pure || op == Constant || 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 <= TypeOf); }
|
||||
|
@ -361,6 +361,61 @@ BOOST_AUTO_TEST_CASE(constant_function)
|
||||
checkInterface(sourceCode, interface);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(pure_function)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract test {
|
||||
function foo(uint a, uint b) returns(uint d) { return a + b; }
|
||||
function boo(uint32 a) pure returns(uint b) { return a * 4; }
|
||||
}
|
||||
)";
|
||||
|
||||
char const* interface = R"([
|
||||
{
|
||||
"name": "foo",
|
||||
"constant": false,
|
||||
"payable" : false,
|
||||
"statemutability": "nonpayable",
|
||||
"type": "function",
|
||||
"inputs": [
|
||||
{
|
||||
"name": "a",
|
||||
"type": "uint256"
|
||||
},
|
||||
{
|
||||
"name": "b",
|
||||
"type": "uint256"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "d",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "boo",
|
||||
"constant": true,
|
||||
"payable" : false,
|
||||
"statemutability": "pure",
|
||||
"type": "function",
|
||||
"inputs": [{
|
||||
"name": "a",
|
||||
"type": "uint32"
|
||||
}],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "b",
|
||||
"type": "uint256"
|
||||
}
|
||||
]
|
||||
}
|
||||
])";
|
||||
|
||||
checkInterface(sourceCode, interface);
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(events)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
|
@ -928,6 +928,16 @@ BOOST_AUTO_TEST_CASE(multiple_statemutability_specifiers)
|
||||
function f() payable constant {}
|
||||
})";
|
||||
CHECK_PARSE_ERROR(text, "State mutability already specified as \"payable\".");
|
||||
text = R"(
|
||||
contract c {
|
||||
function f() pure payable {}
|
||||
})";
|
||||
CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\".");
|
||||
text = R"(
|
||||
contract c {
|
||||
function f() pure constant {}
|
||||
})";
|
||||
CHECK_PARSE_ERROR(text, "State mutability already specified as \"pure\".");
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(literal_constants_with_ether_subdenominations)
|
||||
|
Loading…
Reference in New Issue
Block a user