diff options
-rw-r--r-- | Changelog.md | 3 | ||||
-rw-r--r-- | docs/abi-spec.rst | 2 | ||||
-rw-r--r-- | docs/contracts.rst | 23 | ||||
-rw-r--r-- | docs/grammar.txt | 2 | ||||
-rw-r--r-- | docs/miscellaneous.rst | 5 | ||||
-rw-r--r-- | docs/types.rst | 2 | ||||
-rw-r--r-- | libsolidity/analysis/StaticAnalyzer.cpp | 2 | ||||
-rw-r--r-- | libsolidity/ast/ASTEnums.h | 4 | ||||
-rw-r--r-- | libsolidity/ast/ASTJsonConverter.cpp | 4 | ||||
-rw-r--r-- | libsolidity/interface/ABI.cpp | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Parser.cpp | 2 | ||||
-rw-r--r-- | libsolidity/parsing/Token.h | 4 | ||||
-rw-r--r-- | test/libsolidity/SolidityABIJSON.cpp | 55 | ||||
-rw-r--r-- | test/libsolidity/SolidityParser.cpp | 10 |
14 files changed, 107 insertions, 13 deletions
diff --git a/Changelog.md b/Changelog.md index 1ea3f6c8..8c540a28 100644 --- a/Changelog.md +++ b/Changelog.md @@ -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``). diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst index 159bd6c7..036b1ac8 100644 --- a/docs/abi-spec.rst +++ b/docs/abi-spec.rst @@ -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"`. diff --git a/docs/contracts.rst b/docs/contracts.rst index 0f1a882c..50e7f3d1 100644 --- a/docs/contracts.rst +++ b/docs/contracts.rst @@ -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: diff --git a/docs/grammar.txt b/docs/grammar.txt index 36ba36f0..041728c5 100644 --- a/docs/grammar.txt +++ b/docs/grammar.txt @@ -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 | diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst index 7889fff2..e78c4807 100644 --- a/docs/miscellaneous.rst +++ b/docs/miscellaneous.rst @@ -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``. diff --git a/docs/types.rst b/docs/types.rst index 9c1c73c6..fb88b006 100644 --- a/docs/types.rst +++ b/docs/types.rst @@ -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>)`` diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp index ab1cbb52..2f130414 100644 --- a/libsolidity/analysis/StaticAnalyzer.cpp +++ b/libsolidity/analysis/StaticAnalyzer.cpp @@ -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; } diff --git a/libsolidity/ast/ASTEnums.h b/libsolidity/ast/ASTEnums.h index f7c75878..5ba21907 100644 --- a/libsolidity/ast/ASTEnums.h +++ b/libsolidity/ast/ASTEnums.h @@ -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: diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp index f1f6770e..c0d635f3 100644 --- a/libsolidity/ast/ASTJsonConverter.cpp +++ b/libsolidity/ast/ASTJsonConverter.cpp @@ -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)) diff --git a/libsolidity/interface/ABI.cpp b/libsolidity/interface/ABI.cpp index 6e8563f7..dd56ff6d 100644 --- a/libsolidity/interface/ABI.cpp +++ b/libsolidity/interface/ABI.cpp @@ -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( diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp index cd0d6157..ddfdb667 100644 --- a/libsolidity/parsing/Parser.cpp +++ b/libsolidity/parsing/Parser.cpp @@ -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(); diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h index 3bc52f1d..805fbf5d 100644 --- a/libsolidity/parsing/Token.h +++ b/libsolidity/parsing/Token.h @@ -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); } diff --git a/test/libsolidity/SolidityABIJSON.cpp b/test/libsolidity/SolidityABIJSON.cpp index 12fb1f9c..dd51d926 100644 --- a/test/libsolidity/SolidityABIJSON.cpp +++ b/test/libsolidity/SolidityABIJSON.cpp @@ -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"( diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp index 8e84ead1..a39e0958 100644 --- a/test/libsolidity/SolidityParser.cpp +++ b/test/libsolidity/SolidityParser.cpp @@ -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) |