Merge pull request #4738 from ethereum/dataloc_merged

Enforce data location.
This commit is contained in:
chriseth 2018-08-14 21:38:07 +02:00 committed by GitHub
commit cc54f6c425
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
90 changed files with 589 additions and 313 deletions

View File

@ -5,7 +5,7 @@ How to update your code:
* Change every ``keccak256(a, b, c)`` to ``keccak256(abi.encodePacked(a, b, c))``.
* Add ``public`` to every function and ``external`` to every fallback or interface function that does not specify its visibility already.
* Make your fallback functions ``external``.
* Explicitly state the storage location for local variables of struct and array types, e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``.
* Explicitly state the data location for all variables of struct, array or mapping types (including function parameters), e.g. change ``uint[] x = m_x`` to ``uint[] storage x = m_x``. Note that ``external`` functions require parameters with a data location of ``calldata``.
* Explicitly convert values of contract type to addresses before using an ``address`` member. Example: if ``c`` is a contract, change ``c.transfer(...)`` to ``address(c).transfer(...)``.
Breaking Changes:
@ -54,6 +54,7 @@ Breaking Changes:
* Type Checker: Disallow calling constructor with wrong argument count. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Disallow uninitialized storage variables. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Detecting cyclic dependencies in variables and structs is limited in recursion to 256.
* Type Checker: Require explicit data location for all variables, including function parameters. This was partly already the case in the experimental 0.5.0 mode.
* Type Checker: Only accept a single ``bytes`` type for ``.call()`` (and family), ``keccak256()``, ``sha256()`` and ``ripemd160()``.
* Type Checker: Fallback function must be external. This was already the case in the experimental 0.5.0 mode.
* Type Checker: Interface functions must be declared external. This was already the case in the experimental 0.5.0 mode.
@ -61,7 +62,7 @@ Breaking Changes:
* Type Checker: Disallow "loose assembly" syntax entirely. This means that jump labels, jumps and non-functional instructions cannot be used anymore.
* Type System: Disallow explicit and implicit conversions from decimal literals to ``bytesXX`` types.
* Type System: Disallow explicit and implicit conversions from hex literals to ``bytesXX`` types of different size.
* Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/soldity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible.
* Remove obsolete ``std`` directory from the Solidity repository. This means accessing ``https://github.com/ethereum/solidity/blob/develop/std/*.sol`` (or ``https://github.com/ethereum/solidity/std/*.sol`` in Remix) will not be possible.
* References Resolver: Turn missing storage locations into an error. This was already the case in the experimental 0.5.0 mode.
* Syntax Checker: Disallow functions without implementation to use modifiers. This was already the case in the experimental 0.5.0 mode.
* Syntax Checker: Named return values in function types are an error.

View File

@ -90,7 +90,7 @@ of votes.
// If the first argument of `require` evaluates
// to `false`, execution terminates and all
// changes to the state and to Ether balances
// are reverted.
// are reverted.
// This used to consume all gas in old EVM versions, but
// not anymore.
// It is often a good idea to use `require` to check if
@ -708,7 +708,7 @@ For a contract that fulfills payments, the signed message must include:
A replay attack is when a signed message is reused to claim authorization for
a second action.
To avoid replay attacks we will use the same as in Ethereum transactions
themselves, a so-called nonce, which is the number of transactions sent by an
themselves, a so-called nonce, which is the number of transactions sent by an
account.
The smart contract will check if a nonce is used multiple times.
@ -731,7 +731,7 @@ Packing arguments
Now that we have identified what information to include in the
signed message, we are ready to put the message together, hash it,
and sign it. For simplicity, we just concatenate the data.
The
The
`ethereumjs-abi <https://github.com/ethereumjs/ethereumjs-abi>`_ library provides
a function called ``soliditySHA3`` that mimics the behavior
of Solidity's ``keccak256`` function applied to arguments encoded
@ -750,7 +750,7 @@ creates the proper signature for the ``ReceiverPays`` example:
["address", "uint256", "uint256", "address"],
[recipient, amount, nonce, contractAddress]
).toString("hex");
web3.personal.sign(hash, web3.eth.defaultAccount, callback);
}
@ -779,7 +779,7 @@ at the end of this chapter).
Computing the Message Hash
--------------------------
The smart contract needs to know exactly what parameters were signed,
and so it must recreate the message from the parameters and use that
for signature verification. The functions ``prefixed`` and
@ -801,7 +801,7 @@ The full contract
constructor() public payable {}
function claimPayment(uint256 amount, uint256 nonce, bytes signature) public {
function claimPayment(uint256 amount, uint256 nonce, bytes memory signature) public {
require(!usedNonces[nonce]);
usedNonces[nonce] = true;
@ -820,7 +820,7 @@ The full contract
}
/// signature methods.
function splitSignature(bytes sig)
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
@ -839,7 +839,7 @@ The full contract
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes sig)
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
@ -874,7 +874,7 @@ two parties (Alice and Bob). Using it involves three steps:
1. Alice funds a smart contract with Ether. This "opens" the payment channel.
2. Alice signs messages that specify how much of that Ether is owed to the recipient. This step is repeated for each payment.
3. Bob "closes" the payment channel, withdrawing their portion of the Ether and sending the remainder back to the sender.
Not ethat only steps 1 and 3 require Ethereum transactions, step 2 means that
the sender transmits a cryptographically signed message to the recipient via off chain ways (e.g. email).
This means only two transactions are required to support any number of transfers.
@ -906,7 +906,7 @@ Each message includes the following information:
* The smart contract's address, used to prevent cross-contract replay attacks.
* The total amount of Ether that is owed the recipient so far.
A payment channel is closed just once, at the of a series of transfers.
Because of this, only one of the messages sent will be redeemed. This is why
each message specifies a cumulative total amount of Ether owed, rather than the
@ -926,7 +926,7 @@ Here is the modified javascript code to cryptographically sign a message from th
[contractAddress, amount]
);
}
function signMessage(message, callback) {
web3.personal.sign(
"0x" + message.toString("hex"),
@ -934,10 +934,10 @@ Here is the modified javascript code to cryptographically sign a message from th
callback
);
}
// contractAddress is used to prevent cross-contract replay attacks.
// amount, in wei, specifies how much Ether should be sent.
function signPayment(contractAddress, amount, callback) {
var message = constructPaymentMessage(contractAddress, amount);
signMessage(message, callback);
@ -1003,7 +1003,7 @@ The full contract
expiration = now + duration;
}
function isValidSignature(uint256 amount, bytes signature)
function isValidSignature(uint256 amount, bytes memory signature)
internal
view
returns (bool)
@ -1017,7 +1017,7 @@ The full contract
/// the recipient can close the channel at any time by presenting a
/// signed amount from the sender. the recipient will be sent that amount,
/// and the remainder will go back to the sender
function close(uint256 amount, bytes signature) public {
function close(uint256 amount, bytes memory signature) public {
require(msg.sender == recipient);
require(isValidSignature(amount, signature));
@ -1043,7 +1043,7 @@ The full contract
/// All functions below this are just taken from the chapter
/// 'creating and verifying signatures' chapter.
function splitSignature(bytes sig)
function splitSignature(bytes memory sig)
internal
pure
returns (uint8 v, bytes32 r, bytes32 s)
@ -1058,11 +1058,11 @@ The full contract
// final byte (first byte of the next 32 bytes)
v := byte(0, mload(add(sig, 96)))
}
return (v, r, s);
}
function recoverSigner(bytes32 message, bytes sig)
function recoverSigner(bytes32 message, bytes memory sig)
internal
pure
returns (address)
@ -1083,7 +1083,7 @@ Note: The function ``splitSignature`` is very simple and does not use all securi
A real implementation should use a more rigorously tested library, such as
openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
Verifying Payments
------------------
@ -1101,7 +1101,7 @@ The recipient should verify each message using the following process:
2. Verify that the new total is the expected amount.
3. Verify that the new total does not exceed the amount of Ether escrowed.
4. Verify that the signature is valid and comes from the payment channel sender.
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
library to write this verifications. The final step can be done a number of ways,
but if it's being done in **JavaScript**.
@ -1117,14 +1117,14 @@ above:
["\x19Ethereum Signed Message:\n32", hash]
);
}
function recoverSigner(message, signature) {
var split = ethereumjs.Util.fromRpcSig(signature);
var publicKey = ethereumjs.Util.ecrecover(message, split.v, split.r, split.s);
var signer = ethereumjs.Util.pubToAddress(publicKey).toString("hex");
return signer;
}
function isValidSignature(contractAddress, amount, signature, expectedSigner) {
var message = prefixed(constructPaymentMessage(contractAddress, amount));
var signer = recoverSigner(message, signature);

View File

@ -580,16 +580,18 @@ variables are held).
Data location
-------------
Every complex type, i.e. *arrays* and *structs*, has an additional
annotation, the "data location", about whether it is stored in memory or in storage. Depending on the
context, there is always a default, but it can be overridden by appending
either ``storage`` or ``memory`` to the type. The default for function parameters (including return parameters) is ``memory``, the default for local variables is ``storage`` and the location is forced
to ``storage`` for state variables (obviously).
There is also a third data location, ``calldata``, which is a non-modifiable,
non-persistent area where function arguments are stored. Function parameters
(not return parameters) of external functions are forced to ``calldata`` and
behave mostly like ``memory``.
Every complex type, i.e. *arrays* and *structs*, has an additional
annotation, the "data location", about where it is stored. There are three data locations:
``memory``, ``storage`` and ``calldata``. Calldata is only valid for parameters of external contract
functions and is required for this type of parameter. Calldata is a non-modifiable,
non-persistent area where function arguments are stored, and behaves mostly like memory.
.. note::
Prior to version 0.5.0 the data location could be omitted, and would default to different locations
depending on the kind of variable, function type, etc., but all complex types must now give an explicit
data location.
Data locations are important because they change how assignments behave:
assignments between storage and memory and also to a state variable (even from other state variables)
@ -635,10 +637,6 @@ Forced data location:
- parameters (not return) of external functions: calldata
- state variables: storage
Default data location:
- parameters (also return) of functions: memory
- all other local variables: storage
.. index:: ! array
.. _arrays:

View File

@ -49,27 +49,23 @@ std::string joinHumanReadable
std::string const& _lastSeparator = ""
)
{
auto it = begin(_list);
auto itEnd = end(_list);
auto const itEnd = end(_list);
std::string result;
// append first string
if (it != itEnd)
for (auto it = begin(_list); it != itEnd; )
{
result += *it;
std::string element = *it;
bool first = (it == begin(_list));
++it;
}
for (;it != itEnd; ++it)
{
if ((std::next(it) == itEnd) && !_lastSeparator.empty())
result += _lastSeparator; // last iteration
else
result += _separator;
// append string
result += *it;
if (!first)
{
if (it == itEnd && !_lastSeparator.empty())
result += _lastSeparator; // last iteration
else
result += _separator;
}
result += std::move(element);
}
return result;

View File

@ -626,6 +626,17 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(FunctionTypeName& _funTypeName)
{
enterNewSubScope(_funTypeName);
return true;
}
void DeclarationRegistrationHelper::endVisit(FunctionTypeName&)
{
closeCurrentScope();
}
bool DeclarationRegistrationHelper::visit(Block& _block)
{
_block.setScope(m_currentScope);

View File

@ -171,6 +171,8 @@ private:
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
bool visit(FunctionTypeName& _funTypeName) override;
void endVisit(FunctionTypeName& _funTypeName) override;
bool visit(Block& _block) override;
void endVisit(Block& _block) override;
bool visit(ForStatement& _forLoop) override;

View File

@ -30,7 +30,10 @@
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libdevcore/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace dev;
@ -155,7 +158,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = make_shared<ContractType>(*contract);
else
{
_typeName.annotation().type = make_shared<TupleType>();
typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
@ -166,13 +172,13 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
return;
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
{
typeError(_typeName.location(), "Only external function types can be payable.");
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
return;
}
@ -182,7 +188,7 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->canBeUsedExternally(false))
{
typeError(t->location(), "Internal type cannot be used for external function type.");
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
return;
}
}
@ -300,6 +306,9 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
if (_variable.annotation().type)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
// This can still happen in very unusual cases where a developer uses constructs, such as
@ -309,127 +318,92 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
// after this step.
return;
}
TypePointer type;
type = _variable.typeName()->annotation().type;
using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory;
// References are forced to calldata for external function parameters (not return)
// and memory for parameters (also return) of publicly visible functions.
// They default to memory for function parameters and storage for local variables.
// As an exception, "storage" is allowed for library functions.
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
set<Location> allowedDataLocations = _variable.allowedDataLocations();
if (!allowedDataLocations.count(varLoc))
{
bool isPointer = true;
if (_variable.isExternalCallableParameter())
auto locationToString = [](VariableDeclaration::Location _location) -> string
{
auto const& contract = dynamic_cast<ContractDefinition const&>(
*dynamic_cast<Declaration const&>(*_variable.scope()).scope()
);
if (contract.isLibrary())
switch (_location)
{
if (varLoc == Location::Memory)
fatalTypeError(_variable.location(),
"Location has to be calldata or storage for external "
"library functions (remove the \"memory\" keyword)."
);
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Default: return "none";
}
else
{
// force location of external function parameters (not return) to calldata
if (varLoc != Location::CallData && varLoc != Location::Default)
fatalTypeError(_variable.location(),
"Location has to be calldata for external functions "
"(remove the \"memory\" or \"storage\" keyword)."
);
}
if (varLoc == Location::Default || varLoc == Location::CallData)
typeLoc = DataLocation::CallData;
else
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
else if (_variable.isCallableParameter() && dynamic_cast<Declaration const&>(*_variable.scope()).isPublic())
{
auto const& contract = dynamic_cast<ContractDefinition const&>(
*dynamic_cast<Declaration const&>(*_variable.scope()).scope()
);
// force locations of public or external function (return) parameters to memory
if (varLoc != Location::Memory && varLoc != Location::Default && !contract.isLibrary())
fatalTypeError(_variable.location(),
"Location has to be memory for publicly visible functions "
"(remove the \"storage\" or \"calldata\" keyword)."
);
if (varLoc == Location::Default || !contract.isLibrary())
typeLoc = DataLocation::Memory;
else
{
if (varLoc == Location::CallData)
fatalTypeError(_variable.location(),
"Location cannot be calldata for non-external functions "
"(remove the \"calldata\" keyword)."
);
typeLoc = varLoc == Location::Memory ? DataLocation::Memory : DataLocation::Storage;
}
}
return {};
};
string errorString;
if (!_variable.hasReferenceOrMappingType())
errorString = "Data location can only be specified for array, struct or mapping types";
else
{
if (_variable.isConstant())
{
if (varLoc != Location::Default && varLoc != Location::Memory)
fatalTypeError(
_variable.location(),
"Data location has to be \"memory\" (or unspecified) for constants."
);
typeLoc = DataLocation::Memory;
}
else if (varLoc == Location::Default)
{
if (_variable.isCallableParameter())
typeLoc = DataLocation::Memory;
else
{
typeLoc = DataLocation::Storage;
if (_variable.isLocalVariable())
typeError(
_variable.location(),
"Data location must be specified as either \"memory\" or \"storage\"."
);
}
}
errorString = "Data location must be " +
joinHumanReadable(
allowedDataLocations | boost::adaptors::transformed(locationToString),
", ",
" or "
);
if (_variable.isCallableParameter())
errorString +=
" for " +
string(_variable.isReturnParameter() ? "return " : "") +
"parameter in" +
string(_variable.isExternalCallableParameter() ? " external" : "") +
" function";
else
{
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
fatalTypeError(_variable.location(),
"Variable cannot be declared as \"calldata\" (remove the \"calldata\" keyword)."
);
break;
default:
solAssert(false, "Unknown data location");
}
}
isPointer = !_variable.isStateVariable();
errorString += " for variable";
}
errorString += ", but " + locationToString(varLoc) + " was given.";
typeError(_variable.location(), errorString);
solAssert(!allowedDataLocations.empty(), "");
varLoc = *allowedDataLocations.begin();
}
// Find correct data location.
if (_variable.isEventParameter())
{
solAssert(varLoc == Location::Default, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Default, "");
typeLoc = _variable.isConstant() ? DataLocation::Memory : DataLocation::Storage;
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
dynamic_cast<EnumDefinition const*>(_variable.scope())
)
// The actual location will later be changed depending on how the type is used.
typeLoc = DataLocation::Storage;
else
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Default:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
TypePointer type = _variable.typeName()->annotation().type;
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
{
bool isPointer = !_variable.isStateVariable();
type = ref->copyForLocation(typeLoc, isPointer);
}
else if (dynamic_cast<MappingType const*>(type.get()))
{
if (_variable.isLocalVariable() && varLoc != Location::Storage)
typeError(
_variable.location(),
"Data location for mappings must be specified as \"storage\"."
);
}
else if (varLoc != Location::Default && !ref)
typeError(_variable.location(), "Data location can only be given for array or struct types.");
_variable.annotation().type = type;
}

View File

@ -630,8 +630,13 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
for (ASTPointer<VariableDeclaration> const& var: _function.parameters() + _function.returnParameters())
{
if (
type(*var)->category() == Type::Category::Mapping &&
!type(*var)->dataStoredIn(DataLocation::Storage)
)
m_errorReporter.typeError(var->location(), "Mapping types can only have a data location of \"storage\".");
else if (
!type(*var)->canLiveOutsideStorage() &&
!(_function.visibility() <= FunctionDefinition::Visibility::Internal)
_function.visibility() > FunctionDefinition::Visibility::Internal
)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
@ -716,8 +721,6 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
expectType(*_variable.value(), *varType);
if (_variable.isConstant())
{
if (!_variable.isStateVariable())
m_errorReporter.typeError(_variable.location(), "Illegal use of \"constant\" specifier.");
if (!_variable.type()->isValueType())
{
bool allowed = false;

View File

@ -418,6 +418,7 @@ bool VariableDeclaration::isLocalVariable() const
{
auto s = scope();
return
dynamic_cast<FunctionTypeName const*>(s) ||
dynamic_cast<CallableDeclaration const*>(s) ||
dynamic_cast<Block const*>(s) ||
dynamic_cast<ForStatement const*>(s);
@ -425,14 +426,18 @@ bool VariableDeclaration::isLocalVariable() const
bool VariableDeclaration::isCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
if (isReturnParameter())
return true;
vector<ASTPointer<VariableDeclaration>> const* parameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
parameters = &funTypeName->parameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
parameters = &callable->parameters();
if (parameters)
for (auto const& variable: *parameters)
if (variable.get() == this)
return true;
return false;
@ -445,11 +450,16 @@ bool VariableDeclaration::isLocalOrReturn() const
bool VariableDeclaration::isReturnParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable)
return false;
if (callable->returnParameterList())
for (auto const& variable: callable->returnParameterList()->parameters())
vector<ASTPointer<VariableDeclaration>> const* returnParameters = nullptr;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
returnParameters = &funTypeName->returnParameterTypes();
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->returnParameterList())
returnParameters = &callable->returnParameterList()->parameters();
if (returnParameters)
for (auto const& variable: *returnParameters)
if (variable.get() == this)
return true;
return false;
@ -457,15 +467,88 @@ bool VariableDeclaration::isReturnParameter() const
bool VariableDeclaration::isExternalCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
if (!callable || callable->visibility() != Declaration::Visibility::External)
if (!isCallableParameter())
return false;
for (auto const& variable: callable->parameters())
if (variable.get() == this)
return true;
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->visibility() == Declaration::Visibility::External)
return !isReturnParameter();
return false;
}
bool VariableDeclaration::isInternalCallableParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funTypeName = dynamic_cast<FunctionTypeName const*>(scope()))
return funTypeName->visibility() == Declaration::Visibility::Internal;
else if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
return callable->visibility() <= Declaration::Visibility::Internal;
return false;
}
bool VariableDeclaration::isLibraryFunctionParameter() const
{
if (!isCallableParameter())
return false;
if (auto const* funDef = dynamic_cast<FunctionDefinition const*>(scope()))
return dynamic_cast<ContractDefinition const&>(*funDef->scope()).isLibrary();
else
return false;
}
bool VariableDeclaration::isEventParameter() const
{
return dynamic_cast<EventDefinition const*>(scope()) != nullptr;
}
bool VariableDeclaration::hasReferenceOrMappingType() const
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
TypePointer const& type = typeName()->annotation().type;
return type->category() == Type::Category::Mapping || dynamic_cast<ReferenceType const*>(type.get());
}
set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() const
{
using Location = VariableDeclaration::Location;
if (!hasReferenceOrMappingType() || isStateVariable() || isEventParameter())
return set<Location>{ Location::Default };
else if (isStateVariable() && isConstant())
return set<Location>{ Location::Memory };
else if (isExternalCallableParameter())
{
set<Location> locations{ Location::CallData };
if (isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isCallableParameter())
{
set<Location> locations{ Location::Memory };
if (isInternalCallableParameter() || isLibraryFunctionParameter())
locations.insert(Location::Storage);
return locations;
}
else if (isLocalVariable())
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
if (typeName()->annotation().type->category() == Type::Category::Mapping)
return set<Location>{ Location::Storage };
else
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
}
else
// Struct members etc.
return set<Location>{ Location::Default };
}
TypePointer VariableDeclaration::type() const
{
return annotation().type;
@ -580,7 +663,7 @@ bool Literal::passesAddressChecksum() const
return dev::passesAddressChecksum(value(), true);
}
std::string Literal::getChecksummedAddress() const
string Literal::getChecksummedAddress() const
{
solAssert(isHexNumber(), "Expected hex number");
/// Pad literal to be a proper hex address.

View File

@ -685,6 +685,8 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
/// @returns true iff this variable is the parameter (or return parameter) of a function
/// (or function type name or event) or declared inside a function body.
bool isLocalVariable() const;
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
@ -693,13 +695,27 @@ public:
/// @returns true if this variable is a local variable or return parameter.
bool isLocalOrReturn() const;
/// @returns true if this variable is a parameter (not return parameter) of an external function.
/// This excludes parameters of external function type names.
bool isExternalCallableParameter() const;
/// @returns true if this variable is a parameter or return parameter of an internal function
/// or a function type of internal visibility.
bool isInternalCallableParameter() const;
/// @returns true iff this variable is a parameter(or return parameter of a library function
bool isLibraryFunctionParameter() const;
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
/// in the body of a function or modifier.
/// @returns true if this variable is a parameter of an event.
bool isEventParameter() const;
/// @returns true if the type of the variable is a reference or mapping type, i.e.
/// array, struct or mapping. These types can take a data location (and often require it).
/// Can only be called after reference resolution.
bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_isConstant; }
Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable.
std::set<Location> allowedDataLocations() const;
virtual TypePointer type() const override;

View File

@ -140,7 +140,7 @@ contract moduleHandler is multiOwner, announcementTypes {
}
return (true, false, 0);
}
function replaceModule(string name, address addr, bool callCallback) external returns (bool success) {
function replaceModule(string calldata name, address addr, bool callCallback) external returns (bool success) {
/*
Module replace, can be called only by the Publisher contract.
@ -167,7 +167,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function callReplaceCallback(string moduleName, address newModule) external returns (bool success) {
function callReplaceCallback(string calldata moduleName, address newModule) external returns (bool success) {
require( block.number < debugModeUntil );
if ( ! insertAndCheckDo(calcDoHash("callReplaceCallback", keccak256(abi.encodePacked(moduleName, newModule)))) ) {
return true;
@ -178,7 +178,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function newModule(string name, address addr, bool schellingEvent, bool transferEvent) external returns (bool success) {
function newModule(string calldata name, address addr, bool schellingEvent, bool transferEvent) external returns (bool success) {
/*
Adding new module to the database. Can be called only by the Publisher contract.
@ -199,7 +199,7 @@ contract moduleHandler is multiOwner, announcementTypes {
addModule( modules_s(addr, keccak256(bytes(name)), schellingEvent, transferEvent), true);
return true;
}
function dropModule(string name, bool callCallback) external returns (bool success) {
function dropModule(string calldata name, bool callCallback) external returns (bool success) {
/*
Deleting module from the database. Can be called only by the Publisher contract.
@ -224,7 +224,7 @@ contract moduleHandler is multiOwner, announcementTypes {
return true;
}
function callDisableCallback(string moduleName) external returns (bool success) {
function callDisableCallback(string calldata moduleName) external returns (bool success) {
require( block.number < debugModeUntil );
if ( ! insertAndCheckDo(calcDoHash("callDisableCallback", keccak256(bytes(moduleName)))) ) {
return true;
@ -406,7 +406,7 @@ contract moduleHandler is multiOwner, announcementTypes {
require( token(modules[_id].addr).burn(from, value) );
return true;
}
function configureModule(string moduleName, announcementType aType, uint256 value) external returns (bool success) {
function configureModule(string calldata moduleName, announcementType aType, uint256 value) external returns (bool success) {
/*
Changing configuration of a module. Can be called only by Publisher or while debug mode by owners.

View File

@ -5,8 +5,8 @@ import "./tokenDB.sol";
import "./module.sol";
contract thirdPartyPContractAbstract {
function receiveCorionPremiumToken(address, uint256, bytes) external returns (bool, uint256) {}
function approvedCorionPremiumToken(address, uint256, bytes) external returns (bool) {}
function receiveCorionPremiumToken(address, uint256, bytes calldata) external returns (bool, uint256) {}
function approvedCorionPremiumToken(address, uint256, bytes calldata) external returns (bool) {}
}
contract ptokenDB is tokenDB {}
@ -108,7 +108,7 @@ contract premium is module, safeMath {
* @param extraData Data to give forward to the receiver
* @return True if the approval was successful
*/
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) {
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) {
/*
Authorize another address to use an exact amount of the principals balance.
After the transaction the approvedCorionPremiumToken function of the address will be called with the given data.
@ -226,7 +226,7 @@ contract premium is module, safeMath {
* @param extraData Data to give forward to the receiver
* @return Whether the transfer was successful or not
*/
function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) {
function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) {
/*
Launch a transaction where we transfer from a given address to another one.
After thetransaction the approvedCorionPremiumToken function of the receivers address is going to be called with the given data.

View File

@ -213,7 +213,7 @@ contract provider is module, safeMath, announcementTypes {
return ( ! priv && ( rate >= publicMinRate && rate <= publicMaxRate ) ) ||
( priv && ( rate >= privateMinRate && rate <= privateMaxRate ) );
}
function createProvider(bool priv, string name, string website, string country, string info, uint8 rate, bool isForRent, address admin) isReady external {
function createProvider(bool priv, string calldata name, string calldata website, string calldata country, string calldata info, uint8 rate, bool isForRent, address admin) isReady external {
/*
Creating a provider.
During the ICO its not allowed to create provider.
@ -270,7 +270,7 @@ contract provider is module, safeMath, announcementTypes {
}
emit EProviderOpen(msg.sender, currHeight);
}
function setProviderDetails(address addr, string website, string country, string info, uint8 rate, address admin) isReady external {
function setProviderDetails(address addr, string calldata website, string calldata country, string calldata info, uint8 rate, address admin) isReady external {
/*
Modifying the datas of the provider.
This can only be invited by the providers admin.
@ -369,7 +369,7 @@ contract provider is module, safeMath, announcementTypes {
setRightForInterest(getProviderCurrentSupply(msg.sender), 0, providers[msg.sender].data[currHeight].priv);
emit EProviderClose(msg.sender, currHeight);
}
function allowUsers(address provider, address[] addr) isReady external {
function allowUsers(address provider, address[] calldata addr) isReady external {
/*
Permition of the user to be able to connect to the provider.
This can only be invited by the providers admin.
@ -387,7 +387,7 @@ contract provider is module, safeMath, announcementTypes {
providers[provider].data[currHeight].allowedUsers[addr[a]] = true;
}
}
function disallowUsers(address provider, address[] addr) isReady external {
function disallowUsers(address provider, address[] calldata addr) isReady external {
/*
Disable of the user not to be able to connect to the provider.
It is can called only for the admin of the provider.

View File

@ -116,7 +116,7 @@ contract publisher is announcementTypes, module, safeMath {
return _amount * oppositeRate / 100 > weight;
}
function newAnnouncement(announcementType Type, string Announcement, string Link, bool Oppositable, string _str, uint256 _uint, address _addr) onlyOwner external {
function newAnnouncement(announcementType Type, string calldata Announcement, string calldata Link, bool Oppositable, string calldata _str, uint256 _uint, address _addr) onlyOwner external {
/*
New announcement. Can be called only by those in the admin list

View File

@ -310,7 +310,7 @@ contract schelling is module, announcementTypes, schellingVars {
setRound(currentRound, round);
}
function sendVote(string vote) isReady noContract external {
function sendVote(string calldata vote) isReady noContract external {
/*
Check vote (Envelope opening)
Only the sent envelopes can be opened.

View File

@ -7,8 +7,8 @@ import "./moduleHandler.sol";
import "./tokenDB.sol";
contract thirdPartyContractAbstract {
function receiveCorionToken(address, uint256, bytes) external returns (bool, uint256) {}
function approvedCorionToken(address, uint256, bytes) external returns (bool) {}
function receiveCorionToken(address, uint256, bytes calldata) external returns (bool, uint256) {}
function approvedCorionToken(address, uint256, bytes calldata) external returns (bool) {}
}
contract token is safeMath, module, announcementTypes {
@ -123,7 +123,7 @@ contract token is safeMath, module, announcementTypes {
* @param extraData Data to give forward to the receiver
* @return True if the approval was successful
*/
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes extraData) isReady external returns (bool success) {
function approveAndCall(address spender, uint256 amount, uint256 nonce, bytes calldata extraData) isReady external returns (bool success) {
/*
Authorise another address to use a certain quantity of the authorising owners balance
Following the transaction the receiver address `approvedCorionToken` function is called by the given data
@ -267,7 +267,7 @@ contract token is safeMath, module, announcementTypes {
* @param extraData Data to give forward to the receiver
* @return Whether the transfer was successful or not
*/
function transfer(address to, uint256 amount, bytes extraData) isReady external returns (bool success) {
function transfer(address to, uint256 amount, bytes calldata extraData) isReady external returns (bool success) {
/*
Start transaction to send a quantity from a given address to another address
After transaction the function `receiveCorionToken`of the receiver is called by the given data

View File

@ -55,7 +55,7 @@ contract MultisigWallet is Multisig, Shareable, DayLimit {
* @param _value The value to send
* @param _data The data part of the transaction
*/
function execute(address _to, uint256 _value, bytes _data) external onlyOwner returns (bytes32 _r) {
function execute(address _to, uint256 _value, bytes calldata _data) external onlyOwner returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
emit SingleTransact(msg.sender, _value, _to, _data);

View File

@ -18,7 +18,7 @@ contract HasNoTokens is Ownable {
* @param value_ uint256 the amount of the specified token
* @param data_ Bytes The data passed from the caller.
*/
function tokenFallback(address from_, uint256 value_, bytes data_) external {
function tokenFallback(address from_, uint256 value_, bytes calldata data_) external {
revert();
}

View File

@ -23,6 +23,6 @@ contract Multisig {
// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint256 _value, bytes _data) external returns (bytes32);
function execute(address _to, uint256 _value, bytes calldata _data) external returns (bytes32);
function confirm(bytes32 _h) public returns (bool);
}

View File

@ -132,7 +132,7 @@ contract GlobalRegistrar is Registrar, AuctionSystem {
}
}
function reserve(string _name) external payable {
function reserve(string calldata _name) external payable {
if (bytes(_name).length == 0)
revert();
bool needAuction = requiresAuction(_name);

View File

@ -348,7 +348,7 @@ contract multisig {
// TODO: document
function changeOwner(address _from, address _to) external;
function execute(address _to, uint _value, bytes _data) external returns (bytes32);
function execute(address _to, uint _value, bytes calldata _data) external returns (bytes32);
function confirm(bytes32 _h) public returns (bool);
}
@ -390,7 +390,7 @@ contract Wallet is multisig, multiowned, daylimit {
// If not, goes into multisig process. We provide a hash on return to allow the sender to provide
// shortcuts for the other confirmations (allowing them to avoid replicating the _to, _value
// and _data arguments). They still get the option of using them if they want, anyways.
function execute(address _to, uint _value, bytes _data) external onlyowner returns (bytes32 _r) {
function execute(address _to, uint _value, bytes calldata _data) external onlyowner returns (bytes32 _r) {
// first, take the opportunity to check that we're under the daily limit.
if (underLimit(_value)) {
emit SingleTransact(msg.sender, _value, _to, _data);

View File

@ -234,7 +234,7 @@ BOOST_AUTO_TEST_CASE(byte_arrays)
return (a, b.length, b[3], c);
}
function f_external(uint a, bytes b, uint c)
function f_external(uint a, bytes calldata b, uint c)
external pure returns (uint, uint, byte, uint) {
return (a, b.length, b[3], c);
}
@ -261,7 +261,7 @@ BOOST_AUTO_TEST_CASE(calldata_arrays_too_large)
{
string sourceCode = R"(
contract C {
function f(uint a, uint[] b, uint c) external pure returns (uint) {
function f(uint a, uint[] calldata b, uint c) external pure returns (uint) {
return 7;
}
}

View File

@ -367,7 +367,7 @@ BOOST_AUTO_TEST_CASE(calldata)
string sourceCode = R"(
contract C {
event E(bytes);
function f(bytes a) external {
function f(bytes calldata a) external {
emit E(a);
}
}

View File

@ -83,7 +83,7 @@
"id" : 3,
"name" : "",
"nodeType" : "VariableDeclaration",
"scope" : 16,
"scope" : 5,
"src" : "61:4:1",
"stateVariable" : false,
"storageLocation" : "default",
@ -167,7 +167,7 @@
"id" : 10,
"name" : "",
"nodeType" : "VariableDeclaration",
"scope" : 16,
"scope" : 12,
"src" : "113:4:1",
"stateVariable" : false,
"storageLocation" : "default",

View File

@ -1 +1,3 @@
contract C { function f(function() external payable returns (uint) x) returns (function() external view returns (uint)) {} }
// ----

View File

@ -100,7 +100,7 @@
{
"constant" : false,
"name" : "",
"scope" : 16,
"scope" : 5,
"stateVariable" : false,
"storageLocation" : "default",
"type" : "uint256",
@ -191,7 +191,7 @@
{
"constant" : false,
"name" : "",
"scope" : 16,
"scope" : 12,
"stateVariable" : false,
"storageLocation" : "default",
"type" : "uint256",

View File

@ -727,7 +727,7 @@ BOOST_AUTO_TEST_CASE(strings_and_arrays)
// bug #1801
char const* sourceCode = R"(
contract test {
function f(string a, bytes b, uint[] c) external {}
function f(string calldata a, bytes calldata b, uint[] calldata c) external {}
}
)";

View File

@ -526,7 +526,7 @@ BOOST_AUTO_TEST_CASE(array_multiple_local_vars)
{
char const* sourceCode = R"(
contract test {
function f(uint256[] seq) external pure returns (uint256) {
function f(uint256[] calldata seq) external pure returns (uint256) {
uint i = 0;
uint sum = 0;
while (i < seq.length)
@ -4540,7 +4540,7 @@ BOOST_AUTO_TEST_CASE(struct_containing_bytes_copy_and_delete)
struct Struct { uint a; bytes data; uint b; }
Struct data1;
Struct data2;
function set(uint _a, bytes _data, uint _b) external returns (bool) {
function set(uint _a, bytes calldata _data, uint _b) external returns (bool) {
data1.a = _a;
data1.b = _b;
data1.data = _data;
@ -4764,12 +4764,12 @@ BOOST_AUTO_TEST_CASE(struct_referencing)
}
library L {
struct S { uint b; uint a; }
function f() public pure returns (S) {
function f() public pure returns (S memory) {
S memory s;
s.a = 3;
return s;
}
function g() public pure returns (I.S) {
function g() public pure returns (I.S memory) {
I.S memory s;
s.a = 4;
return s;
@ -4779,25 +4779,25 @@ BOOST_AUTO_TEST_CASE(struct_referencing)
function a(S memory) public pure returns (uint) { return 2; }
}
contract C is I {
function f() public pure returns (S) {
function f() public pure returns (S memory) {
S memory s;
s.a = 1;
return s;
}
function g() public pure returns (I.S) {
function g() public pure returns (I.S memory) {
I.S memory s;
s.a = 2;
return s;
}
function h() public pure returns (L.S) {
function h() public pure returns (L.S memory) {
L.S memory s;
s.a = 5;
return s;
}
function x() public pure returns (L.S) {
function x() public pure returns (L.S memory) {
return L.f();
}
function y() public pure returns (I.S) {
function y() public pure returns (I.S memory) {
return L.g();
}
function a1() public pure returns (uint) { S memory s; return L.a(s); }
@ -4941,7 +4941,7 @@ BOOST_AUTO_TEST_CASE(bytes_in_arguments)
uint result;
function f(uint a, uint b) public { result += a + b; }
function g(uint a) public { result *= a; }
function test(uint a, bytes data1, bytes data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) {
function test(uint a, bytes calldata data1, bytes calldata data2, uint b) external returns (uint r_a, uint r, uint r_b, uint l) {
r_a = a;
address(this).call(data1);
address(this).call(data2);
@ -5876,7 +5876,7 @@ BOOST_AUTO_TEST_CASE(external_array_args)
{
char const* sourceCode = R"(
contract c {
function test(uint[8] a, uint[] b, uint[5] c, uint a_index, uint b_index, uint c_index)
function test(uint[8] calldata a, uint[] calldata b, uint[5] calldata c, uint a_index, uint b_index, uint c_index)
external returns (uint av, uint bv, uint cv) {
av = a[a_index];
bv = b[b_index];
@ -5901,10 +5901,10 @@ BOOST_AUTO_TEST_CASE(bytes_index_access)
char const* sourceCode = R"(
contract c {
bytes data;
function direct(bytes arg, uint index) external returns (uint) {
function direct(bytes calldata arg, uint index) external returns (uint) {
return uint(uint8(arg[index]));
}
function storageCopyRead(bytes arg, uint index) external returns (uint) {
function storageCopyRead(bytes calldata arg, uint index) external returns (uint) {
data = arg;
return uint(uint8(data[index]));
}
@ -5959,7 +5959,7 @@ BOOST_AUTO_TEST_CASE(array_copy_calldata_storage)
uint[9] m_data;
uint[] m_data_dyn;
uint8[][] m_byte_data;
function store(uint[9] a, uint8[3][] b) external returns (uint8) {
function store(uint[9] calldata a, uint8[3][] calldata b) external returns (uint8) {
m_data = a;
m_data_dyn = a;
m_byte_data = b;
@ -5998,7 +5998,7 @@ BOOST_AUTO_TEST_CASE(array_copy_nested_array)
uint[4][] a;
uint[10][] b;
uint[][] c;
function test(uint[2][] d) external returns (uint) {
function test(uint[2][] calldata d) external returns (uint) {
a = d;
b = a;
c = b;
@ -6949,7 +6949,7 @@ BOOST_AUTO_TEST_CASE(return_string)
char const* sourceCode = R"(
contract Main {
string public s;
function set(string _s) external {
function set(string calldata _s) external {
s = _s;
}
function get1() public returns (string memory r) {
@ -6975,7 +6975,7 @@ BOOST_AUTO_TEST_CASE(return_multiple_strings_of_various_sizes)
contract Main {
string public s1;
string public s2;
function set(string _s1, uint x, string _s2) external returns (uint) {
function set(string calldata _s1, uint x, string calldata _s2) external returns (uint) {
s1 = _s1;
s2 = _s2;
return x;
@ -7024,7 +7024,7 @@ BOOST_AUTO_TEST_CASE(accessor_involving_strings)
contract Main {
struct stringData { string a; uint b; string c; }
mapping(uint => stringData[]) public data;
function set(uint x, uint y, string a, uint b, string c) external returns (bool) {
function set(uint x, uint y, string calldata a, uint b, string calldata c) external returns (bool) {
data[x].length = y + 1;
data[x][y].a = a;
data[x][y].b = b;
@ -7061,7 +7061,7 @@ BOOST_AUTO_TEST_CASE(bytes_in_function_calls)
function setIndirectFromMemory(string memory _s1, uint x, string memory _s2) public returns (uint) {
return this.set(_s1, x, _s2);
}
function setIndirectFromCalldata(string _s1, uint x, string _s2) external returns (uint) {
function setIndirectFromCalldata(string calldata _s1, uint x, string calldata _s2) external returns (uint) {
return this.set(_s1, x, _s2);
}
}
@ -7102,7 +7102,7 @@ BOOST_AUTO_TEST_CASE(return_bytes_internal)
s1 = _s1;
_r1 = s1;
}
function set(bytes _s1) external returns (uint _r, bytes memory _r1) {
function set(bytes calldata _s1) external returns (uint _r, bytes memory _r1) {
_r1 = doSet(_s1);
_r = _r1.length;
}
@ -8040,7 +8040,7 @@ BOOST_AUTO_TEST_CASE(library_call)
BOOST_AUTO_TEST_CASE(library_function_external)
{
char const* sourceCode = R"(
library Lib { function m(bytes b) external pure returns (byte) { return b[2]; } }
library Lib { function m(bytes calldata b) external pure returns (byte) { return b[2]; } }
contract Test {
function f(bytes memory b) public pure returns (byte) {
return Lib.m(b);

View File

@ -158,7 +158,7 @@ BOOST_AUTO_TEST_CASE(function_external_types)
uint a;
}
contract Test {
function boo(uint, bool, bytes8, bool[2], uint[], C, address[]) external returns (uint ret) {
function boo(uint, bool, bytes8, bool[2] calldata, uint[] calldata, C, address[] calldata) external returns (uint ret) {
ret = 5;
}
}
@ -206,10 +206,10 @@ BOOST_AUTO_TEST_CASE(external_structs)
struct Simple { uint i; }
struct Nested { X[2][] a; uint y; }
struct X { bytes32 x; Test t; Simple[] s; }
function f(ActionChoices, uint, Simple) external {}
function g(Test, Nested) external {}
function h(function(Nested memory) external returns (uint)[]) external {}
function i(Nested[]) external {}
function f(ActionChoices, uint, Simple calldata) external {}
function g(Test, Nested calldata) external {}
function h(function(Nested memory) external returns (uint)[] calldata) external {}
function i(Nested[] calldata) external {}
}
)";
SourceUnit const* sourceUnit = parseAndAnalyse(text);
@ -234,10 +234,10 @@ BOOST_AUTO_TEST_CASE(external_structs_in_libraries)
struct Simple { uint i; }
struct Nested { X[2][] a; uint y; }
struct X { bytes32 x; Test t; Simple[] s; }
function f(ActionChoices, uint, Simple) external {}
function g(Test, Nested) external {}
function h(function(Nested memory) external returns (uint)[]) external {}
function i(Nested[]) external {}
function f(ActionChoices, uint, Simple calldata) external {}
function g(Test, Nested calldata) external {}
function h(function(Nested memory) external returns (uint)[] calldata) external {}
function i(Nested[] calldata) external {}
}
)";
SourceUnit const* sourceUnit = parseAndAnalyse(text);
@ -340,7 +340,7 @@ BOOST_AUTO_TEST_CASE(string)
char const* sourceCode = R"(
contract C {
string s;
function f(string x) external { s = x; }
function f(string calldata x) external { s = x; }
}
)";
BOOST_CHECK_NO_THROW(parseAndAnalyse(sourceCode));

View File

@ -4,4 +4,5 @@ contract C {
}
}
// ----
// DeclarationError: (28-45): The "constant" keyword can only be used for state variables.
// TypeError: (69-72): Invalid array length, expected integer literal or constant expression.

View File

@ -1,5 +1,5 @@
contract test {
function f() public pure returns (bytes) {
function f() public pure returns (bytes memory) {
return bytes("abc");
}
}

View File

@ -0,0 +1,4 @@
library L {
struct Nested { uint y; }
function c(function(Nested memory) external returns (uint)[] storage) external pure {}
}

View File

@ -0,0 +1,9 @@
library L {
struct Nested { uint y; }
function b(function(Nested calldata) external returns (uint)[] storage) external pure {}
function d(function(Nested storage) external returns (uint)[] storage) external pure {}
}
// ----
// TypeError: (66-72): Data location must be "memory" for parameter in function, but "calldata" was given.
// TypeError: (159-165): Data location must be "memory" for parameter in function, but "storage" was given.

View File

@ -0,0 +1,5 @@
contract C {
function i() external pure returns(uint[]) {}
}
// ----
// TypeError: (52-58): Data location must be "memory" for return parameter in function, but none was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes memory) external;
}
// ----
// TypeError: (31-36): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (31-36): Data location must be "calldata" for parameter in external function, but "memory" was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes storage) external;
}
// ----
// TypeError: (31-36): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (31-36): Data location must be "calldata" for parameter in external function, but "storage" was given.

View File

@ -1,5 +0,0 @@
library test {
function f(bytes calldata) public;
}
// ----
// TypeError: (30-35): Location cannot be calldata for non-external functions (remove the "calldata" keyword).

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes4 memory) public;
}
// ----
// TypeError: (31-37): Data location can only be given for array or struct types.
// TypeError: (31-37): Data location can only be specified for array, struct or mapping types, but "memory" was given.

View File

@ -0,0 +1,8 @@
contract C {
function f(uint[] memory, uint[] storage) private pure {}
function g(uint[] memory, uint[] storage) internal pure {}
function h(uint[] memory) public pure {}
function i(uint[] calldata) external pure {}
// No data location for events.
event e(uint[]);
}

View File

@ -0,0 +1,6 @@
contract C {
function f() private pure returns(uint[] memory, uint[] storage b) { b = b; }
function g() internal pure returns(uint[] memory, uint[] storage b) { b = b; }
function h() public pure returns(uint[] memory) {}
function i() external pure returns(uint[] memory) {}
}

View File

@ -0,0 +1,8 @@
contract C {
struct Nested { uint y; }
// ensure that we consider array of function pointers as reference type
function b(function(Nested memory) external returns (uint)[] storage) internal pure {}
function c(function(Nested memory) external returns (uint)[] memory) public pure {}
function d(function(Nested memory) external returns (uint)[] calldata) external pure {}
}
// ----

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes calldata) internal;
}
// ----
// TypeError: (31-36): Variable cannot be declared as "calldata" (remove the "calldata" keyword).
// TypeError: (31-36): Data location must be "storage" or "memory" for parameter in function, but "calldata" was given.

View File

@ -0,0 +1,5 @@
contract C {
function g(uint[]) internal pure {}
}
// ----
// TypeError: (28-34): Data location must be "storage" or "memory" for parameter in function, but none was given.

View File

@ -0,0 +1,5 @@
contract C {
function g() internal pure returns(uint[]) {}
}
// ----
// TypeError: (52-58): Data location must be "storage" or "memory" for return parameter in function, but none was given.

View File

@ -0,0 +1,12 @@
library L {
struct S { uint x; }
function g(uint[2]) external pure {}
function h(uint[]) external pure {}
function i(S) external pure {}
function j(mapping(uint => uint)) external pure {}
}
// ----
// TypeError: (52-59): Data location must be "storage" or "calldata" for parameter in external function, but none was given.
// TypeError: (93-99): Data location must be "storage" or "calldata" for parameter in external function, but none was given.
// TypeError: (133-134): Data location must be "storage" or "calldata" for parameter in external function, but none was given.
// TypeError: (168-189): Data location must be "storage" or "calldata" for parameter in external function, but none was given.

View File

@ -0,0 +1,12 @@
library L {
struct S { uint x; }
function g() external pure returns (uint[2]) {}
function h() external pure returns (uint[]) {}
function i() external pure returns (S) {}
function j() external pure returns (mapping(uint => uint)) {}
}
// ----
// TypeError: (77-84): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (129-135): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (180-181): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (226-247): Data location must be "storage" or "memory" for return parameter in function, but none was given.

View File

@ -0,0 +1,10 @@
library L {
struct S { uint x; }
function f(uint[] memory, uint[] storage, S storage) private pure
returns (mapping(uint => uint) storage a, S memory b, uint[] storage c) { return (a, b, c); }
function g(uint[] memory, uint[] storage) internal pure
returns (mapping(uint => uint) storage a, S memory b, uint[] storage c) { return (a, b, c); }
function h(uint[] memory, uint[] storage) public pure returns (S storage x) { return x; }
function i(uint[] calldata, uint[] storage) external pure returns (S storage x) {return x; }
}
// ----

View File

@ -0,0 +1,20 @@
library L {
struct S { uint x; }
function g() internal pure returns (uint[2]) {}
function h() internal pure returns (uint[]) {}
function i() internal pure returns (S) {}
function j() internal pure returns (mapping(uint => uint)) {}
function gp(uint[2]) internal pure {}
function hp(uint[]) internal pure {}
function ip(S) internal pure {}
function jp(mapping(uint => uint)) internal pure {}
}
// ----
// TypeError: (77-84): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (129-135): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (180-181): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (226-247): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (268-275): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (310-316): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (351-352): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (387-408): Data location must be "storage" or "memory" for parameter in function, but none was given.

View File

@ -0,0 +1,20 @@
library L {
struct S { uint x; }
function g() private pure returns (uint[2]) {}
function h() private pure returns (uint[]) {}
function i() private pure returns (S) {}
function j() private pure returns (mapping(uint => uint)) {}
function gp(uint[2]) private pure {}
function hp(uint[]) private pure {}
function ip(S) private pure {}
function jp(mapping(uint => uint)) private pure {}
}
// ----
// TypeError: (76-83): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (127-133): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (177-178): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (222-243): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (264-271): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (305-311): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (345-346): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (380-401): Data location must be "storage" or "memory" for parameter in function, but none was given.

View File

@ -0,0 +1,19 @@
library L {
struct S { uint x; }
function g() private pure returns (uint[2]) {}
function h() private pure returns (uint[]) {}
function i() private pure returns (S) {}
function j() private pure returns (mapping(uint => uint)) {}
function gp(uint[2]) private pure {}
function hp(uint[]) private pure {}
function ip(S) private pure {}
function jp(mapping(uint => uint)) private pure {}}
// ----
// TypeError: (76-83): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (127-133): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (177-178): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (222-243): Data location must be "storage" or "memory" for return parameter in function, but none was given.
// TypeError: (264-271): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (305-311): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (345-346): Data location must be "storage" or "memory" for parameter in function, but none was given.
// TypeError: (380-401): Data location must be "storage" or "memory" for parameter in function, but none was given.

View File

@ -2,4 +2,4 @@ library test {
function f(bytes memory) external;
}
// ----
// TypeError: (30-35): Location has to be calldata or storage for external library functions (remove the "memory" keyword).
// TypeError: (30-35): Data location must be "storage" or "calldata" for parameter in external function, but "memory" was given.

View File

@ -2,4 +2,4 @@ library test {
function f(bytes calldata) internal pure {}
}
// ----
// TypeError: (30-35): Variable cannot be declared as "calldata" (remove the "calldata" keyword).
// TypeError: (30-35): Data location must be "storage" or "memory" for parameter in function, but "calldata" was given.

View File

@ -0,0 +1,5 @@
contract C {
function f(uint[]) private pure {}
}
// ----
// TypeError: (28-34): Data location must be "storage" or "memory" for parameter in function, but none was given.

View File

@ -0,0 +1,5 @@
contract C {
function f() private pure returns(uint[]) {}
}
// ----
// TypeError: (51-57): Data location must be "storage" or "memory" for return parameter in function, but none was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes calldata) public;
}
// ----
// TypeError: (31-36): Location has to be memory for publicly visible functions (remove the "storage" or "calldata" keyword).
// TypeError: (31-36): Data location must be "memory" for parameter in function, but "calldata" was given.

View File

@ -2,4 +2,4 @@ contract test {
function f(bytes storage) public;
}
// ----
// TypeError: (31-36): Location has to be memory for publicly visible functions (remove the "storage" or "calldata" keyword).
// TypeError: (31-36): Data location must be "memory" for parameter in function, but "storage" was given.

View File

@ -0,0 +1,5 @@
contract C {
function h(uint[]) public pure {}
}
// ----
// TypeError: (28-34): Data location must be "memory" for parameter in function, but none was given.

View File

@ -0,0 +1,5 @@
contract C {
function h() public pure returns(uint[]) {}
}
// ----
// TypeError: (50-56): Data location must be "memory" for return parameter in function, but none was given.

View File

@ -7,7 +7,7 @@ contract test {
}
}
// ----
// TypeError: (48-63): Data location can only be given for array or struct types.
// TypeError: (71-89): Data location can only be given for array or struct types.
// TypeError: (97-111): Data location can only be given for array or struct types.
// TypeError: (119-136): Data location can only be given for array or struct types.
// TypeError: (48-63): Data location can only be specified for array, struct or mapping types, but "storage" was given.
// TypeError: (71-89): Data location can only be specified for array, struct or mapping types, but "storage" was given.
// TypeError: (97-111): Data location can only be specified for array, struct or mapping types, but "memory" was given.
// TypeError: (119-136): Data location can only be specified for array, struct or mapping types, but "memory" was given.

View File

@ -3,7 +3,7 @@
// when converting to a function type.
contract C {
function f(function(bytes memory) pure external /*g*/) pure public { }
function callback(bytes) pure external {}
function callback(bytes calldata) pure external {}
function g() view public {
f(this.callback);
}

View File

@ -2,6 +2,6 @@ contract test {
function f(uint[] memory constant a) public { }
}
// ----
// TypeError: (31-55): Illegal use of "constant" specifier.
// DeclarationError: (31-55): The "constant" keyword can only be used for state variables.
// TypeError: (31-55): Constants of non-value type not yet implemented.
// TypeError: (31-55): Uninitialized "constant" variable.

View File

@ -1,8 +1,8 @@
contract Vehicle {
function f(bytes) external returns (uint256 r) {r = 1;}
function f(bytes calldata) external returns (uint256 r) {r = 1;}
}
contract Bike is Vehicle {
function f(bytes) external returns (uint256 r) {r = 42;}
function f(bytes calldata) external returns (uint256 r) {r = 42;}
}
// ----
// Warning: (23-78): Function state mutability can be restricted to pure
// Warning: (23-87): Function state mutability can be restricted to pure

View File

@ -2,4 +2,4 @@ contract C {
function f(uint[] memory a) external {}
}
// ----
// TypeError: (28-43): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (28-43): Data location must be "calldata" for parameter in external function, but "memory" was given.

View File

@ -2,4 +2,4 @@ contract C {
function f(uint[] storage a) external {}
}
// ----
// TypeError: (28-44): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (28-44): Data location must be "calldata" for parameter in external function, but "storage" was given.

View File

@ -1,17 +1,11 @@
contract Test {
string s;
bytes b;
function h(string _s) external { bytes(_s).length; }
function i(string memory _s) internal { bytes(_s).length; }
function j() internal { bytes(s).length; }
function k(bytes _b) external { string(_b); }
function l(bytes memory _b) internal { string(_b); }
function m() internal { string(b); }
function h(string calldata _s) pure external { bytes(_s).length; }
function i(string memory _s) pure internal { bytes(_s).length; }
function j() view internal { bytes(s).length; }
function k(bytes calldata _b) pure external { string(_b); }
function l(bytes memory _b) pure internal { string(_b); }
function m() view internal { string(b); }
}
// ----
// Warning: (47-99): Function state mutability can be restricted to pure
// Warning: (104-163): Function state mutability can be restricted to pure
// Warning: (168-210): Function state mutability can be restricted to view
// Warning: (215-260): Function state mutability can be restricted to pure
// Warning: (265-317): Function state mutability can be restricted to pure
// Warning: (322-358): Function state mutability can be restricted to view

View File

@ -1,9 +1,9 @@
contract C {
function f(bytes bytesAsCalldata) external {
function f(bytes calldata bytesAsCalldata) external {
assembly {
let x := bytesAsCalldata
}
}
}
// ----
// TypeError: (102-117): Call data elements cannot be accessed directly. Copy to a local variable first or use "calldataload" or "calldatacopy" with manually determined offsets and sizes.
// TypeError: (111-126): Call data elements cannot be accessed directly. Copy to a local variable first or use "calldataload" or "calldatacopy" with manually determined offsets and sizes.

View File

@ -9,5 +9,5 @@ contract C {
}
}
// ----
// TypeError: (104-107): Data location must be specified as either "memory" or "storage".
// TypeError: (123-131): Data location must be specified as either "memory" or "storage".
// TypeError: (104-107): Data location must be "storage" or "memory" for variable, but none was given.
// TypeError: (123-131): Data location must be "storage" or "memory" for variable, but none was given.

View File

@ -2,4 +2,4 @@ contract C {
function f(uint storage a) public { }
}
// ----
// TypeError: (28-42): Data location can only be given for array or struct types.
// TypeError: (28-42): Data location can only be specified for array, struct or mapping types, but "storage" was given.

View File

@ -4,4 +4,4 @@ contract C {
}
}
// ----
// TypeError: (28-42): Data location can only be given for array or struct types.
// TypeError: (28-42): Data location can only be specified for array, struct or mapping types, but "storage" was given.

View File

@ -1,6 +1,6 @@
contract C {
function f(uint[85678901234] a) pure external {
function f(uint[85678901234] calldata a) pure external {
}
}
// ----
// TypeError: (28-47): Array is too large to be encoded.
// TypeError: (28-56): Array is too large to be encoded.

View File

@ -0,0 +1,8 @@
contract C {
// This should probably have a better error message at some point.
// Constant mappings should not be possible in general.
mapping(uint => uint) constant x;
}
// ----
// TypeError: (148-180): Constants of non-value type not yet implemented.
// TypeError: (148-180): Uninitialized "constant" variable.

View File

@ -2,4 +2,6 @@ contract Foo {
function f(uint[] storage constant x, uint[] memory y) internal { }
}
// ----
// TypeError: (30-55): Data location has to be "memory" (or unspecified) for constants.
// DeclarationError: (30-55): The "constant" keyword can only be used for state variables.
// TypeError: (30-55): Constants of non-value type not yet implemented.
// TypeError: (30-55): Uninitialized "constant" variable.

View File

@ -3,5 +3,4 @@ contract C {
}
}
// ----
// TypeError: (28-49): Type is required to live outside storage.
// TypeError: (28-49): Internal or recursive type is not allowed for public or external functions.
// TypeError: (28-49): Data location must be "calldata" for parameter in external function, but "storage" was given.

View File

@ -3,5 +3,4 @@ contract C {
}
}
// ----
// TypeError: (28-49): Type is required to live outside storage.
// TypeError: (28-49): Internal or recursive type is not allowed for public or external functions.
// TypeError: (28-49): Data location must be "memory" for parameter in function, but "storage" was given.

View File

@ -3,4 +3,4 @@ contract C {
}
}
// ----
// TypeError: (28-51): Location has to be calldata for external functions (remove the "memory" or "storage" keyword).
// TypeError: (28-51): Data location must be "calldata" for parameter in external function, but "storage" was given.

View File

@ -3,4 +3,4 @@ contract C {
}
}
// ----
// TypeError: (28-51): Location has to be memory for publicly visible functions (remove the "storage" or "calldata" keyword).
// TypeError: (28-51): Data location must be "memory" for parameter in function, but "storage" was given.

View File

@ -3,4 +3,5 @@ contract C {
}
}
// ----
// TypeError: (37-56): Data location must be "memory" for parameter in function, but "storage" was given.
// TypeError: (37-56): Internal type cannot be used for external function type.

View File

@ -3,4 +3,5 @@ contract C {
}
}
// ----
// TypeError: (57-76): Data location must be "memory" for return parameter in function, but "storage" was given.
// TypeError: (57-76): Internal type cannot be used for external function type.

View File

@ -0,0 +1,6 @@
contract c {
function f1(mapping(uint => uint)[] calldata) pure external {}
}
// ----
// TypeError: (29-52): Type is required to live outside storage.
// TypeError: (29-52): Internal or recursive type is not allowed for public or external functions.

View File

@ -3,4 +3,4 @@ contract C {
}
}
// ----
// TypeError: (53-84): Location has to be memory for publicly visible functions (remove the "storage" or "calldata" keyword).
// TypeError: (53-84): Data location must be "memory" for return parameter in function, but "storage" was given.

View File

@ -3,4 +3,4 @@ contract C {
}
}
// ----
// TypeError: (51-82): Location has to be memory for publicly visible functions (remove the "storage" or "calldata" keyword).
// TypeError: (51-82): Data location must be "memory" for return parameter in function, but "storage" was given.

View File

@ -6,4 +6,4 @@ contract c {
}
}
// ----
// TypeError: (81-113): Data location for mappings must be specified as "storage".
// TypeError: (81-113): Data location must be "storage" for variable, but "calldata" was given.

View File

@ -6,4 +6,4 @@ contract c {
}
}
// ----
// TypeError: (81-104): Data location for mappings must be specified as "storage".
// TypeError: (81-104): Data location must be "storage" for variable, but none was given.

View File

@ -0,0 +1,6 @@
contract c {
function f1(mapping(uint => uint) calldata) pure external returns (mapping(uint => uint) memory) {}
}
// ----
// TypeError: (29-50): Type is required to live outside storage.
// TypeError: (29-50): Internal or recursive type is not allowed for public or external functions.

View File

@ -0,0 +1,4 @@
contract c {
function f4(mapping(uint => uint) memory) pure internal {}
}
// ----

View File

@ -0,0 +1,6 @@
contract c {
function f3(mapping(uint => uint) memory) view public {}
}
// ----
// TypeError: (29-50): Type is required to live outside storage.
// TypeError: (29-50): Internal or recursive type is not allowed for public or external functions.

View File

@ -6,4 +6,4 @@ contract c {
}
}
// ----
// TypeError: (81-111): Data location for mappings must be specified as "storage".
// TypeError: (81-111): Data location must be "storage" for variable, but "memory" was given.

View File

@ -3,5 +3,4 @@ contract C {
}
}
// ----
// TypeError: (53-82): Type is required to live outside storage.
// TypeError: (53-82): Internal or recursive type is not allowed for public or external functions.
// TypeError: (53-82): Data location must be "memory" for return parameter in function, but "storage" was given.

View File

@ -3,5 +3,4 @@ contract C {
}
}
// ----
// TypeError: (51-80): Type is required to live outside storage.
// TypeError: (51-80): Internal or recursive type is not allowed for public or external functions.
// TypeError: (51-80): Data location must be "memory" for return parameter in function, but "storage" was given.

View File

@ -0,0 +1,7 @@
contract C {
function f() public pure returns (mapping(uint=>uint) memory m) {
}
}
// ----
// TypeError: (51-79): Type is required to live outside storage.
// TypeError: (51-79): Internal or recursive type is not allowed for public or external functions.