Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
@@ -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``.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any chance of making statemutability: state_mutability? Pure aesthetics I know. But might make it easier to read. Atleast a mixedCase or - of some kind.

Copy link
Contributor Author

@axic axic Aug 21, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no prior example in the ABI so not sure which way to go. (However this was merged in a different PR so please open an issue/PR to solve it.)

* 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``).
Expand Down
2 changes: 1 addition & 1 deletion docs/abi-spec.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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"`.

Expand Down
23 changes: 22 additions & 1 deletion docs/contracts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps we should explain in more detail what reading from or writing to the state means.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can also postpone this explanation so that we do not delay the release.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move it to #2792? Do you want to list all the disallowed things?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we can move it to #2792. We should list as much as people need to understand.


::

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:
Expand Down
2 changes: 1 addition & 1 deletion docs/grammar.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down
5 changes: 3 additions & 2 deletions docs/miscellaneous.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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``.
Expand Down
2 changes: 1 addition & 1 deletion docs/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>)``
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/analysis/StaticAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.");
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is the safe way to introduce it unless the call graph analyser is finished.

return true;
}

Expand Down
4 changes: 3 additions & 1 deletion libsolidity/ast/ASTEnums.h
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
4 changes: 2 additions & 2 deletions libsolidity/ast/ASTJsonConverter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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())),
Expand Down Expand Up @@ -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))
Expand Down
2 changes: 1 addition & 1 deletion libsolidity/interface/ABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
2 changes: 2 additions & 0 deletions libsolidity/parsing/Parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 2 additions & 2 deletions libsolidity/parsing/Token.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) \
Expand Down Expand Up @@ -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) \
Expand Down Expand Up @@ -290,7 +290,7 @@ class Token
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); }
Expand Down
55 changes: 55 additions & 0 deletions test/libsolidity/SolidityABIJSON.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"(
Expand Down
10 changes: 10 additions & 0 deletions test/libsolidity/SolidityParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down