aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md2
-rw-r--r--docs/frequently-asked-questions.rst47
-rw-r--r--docs/solidity-by-example.rst487
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp10
-rw-r--r--libsolidity/analysis/TypeChecker.cpp5
-rw-r--r--libsolidity/ast/AST.h16
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp7
-rw-r--r--test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol9
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol10
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/212_uninitialized_mapping_array_variable_050.sol9
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/234_non_initialized_references_050.sol11
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol2
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/310_rational_unary_plus_operation_v050.sol10
-rw-r--r--test/libsolidity/syntaxTests/nameAndTypeResolution/311_rational_unary_plus_assignment_v050.sol9
-rw-r--r--test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol2
-rw-r--r--test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol10
-rw-r--r--test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/309_rational_unary_plus_assignment.sol)2
19 files changed, 531 insertions, 121 deletions
diff --git a/Changelog.md b/Changelog.md
index 68fdfc80..65032511 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -35,9 +35,11 @@ Breaking Changes:
* Type Checker: Disallow conversions between ``bytesX`` and ``uintY`` of different size.
* Type Checker: Disallow empty tuple components. This was partly already the case in the experimental 0.5.0 mode.
* Type Checker: Disallow specifying base constructor arguments multiple times in the same inheritance hierarchy. 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: Only accept a single ``bytes`` type for ``.call()`` (and family), ``keccak256()``, ``sha256()`` and ``ripemd160()``.
* 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.
* Syntax Checker: Named return values in function types are an error.
+ * Syntax Checker: Disallow unary ``+``. This was already the case in the experimental 0.5.0 mode.
* View Pure Checker: Strictly enfore state mutability. This was already the case in the experimental 0.5.0 mode.
Language Features:
diff --git a/docs/frequently-asked-questions.rst b/docs/frequently-asked-questions.rst
index 0d6fa033..75693bdd 100644
--- a/docs/frequently-asked-questions.rst
+++ b/docs/frequently-asked-questions.rst
@@ -306,48 +306,11 @@ independent copy of the state variable is created in memory and
is another issue). The modifications to this independent copy do not
carry back to ``data1`` or ``data2``.
-A common mistake is to declare a local variable and assume that it will
-be created in memory, although it will be created in storage::
-
- /// THIS CONTRACT CONTAINS AN ERROR
-
- pragma solidity ^0.4.0;
-
- contract C {
- uint someVariable;
- uint[] data;
-
- function f() public {
- uint[] x;
- x.push(2);
- data = x;
- }
- }
-
-The type of the local variable ``x`` is ``uint[] storage``, but since
-storage is not dynamically allocated, it has to be assigned from
-a state variable before it can be used. So no space in storage will be
-allocated for ``x``, but instead it functions only as an alias for
-a pre-existing variable in storage.
-
-What will happen is that the compiler interprets ``x`` as a storage
-pointer and will make it point to the storage slot ``0`` by default.
-This has the effect that ``someVariable`` (which resides at storage
-slot ``0``) is modified by ``x.push(2)``.
-
-The correct way to do this is the following::
-
- pragma solidity ^0.4.0;
-
- contract C {
- uint someVariable;
- uint[] data;
-
- function f() public {
- uint[] x = data;
- x.push(2);
- }
- }
+.. warning::
+ Prior to version 0.5.0, a common mistake was to declare a local variable and assume that it will
+ be created in memory, although it will be created in storage. Using such a variable without initializing
+ it could lead to unexpected behavior. Starting from 0.5.0, however, storage variables have to be initialized,
+ which should prevent these kinds of mistakes.
******************
Advanced Questions
diff --git a/docs/solidity-by-example.rst b/docs/solidity-by-example.rst
index fcdd1862..07cb76c9 100644
--- a/docs/solidity-by-example.rst
+++ b/docs/solidity-by-example.rst
@@ -645,4 +645,489 @@ Safe Remote Purchase
Micropayment Channel
********************
-To be written.
+In this section we will learn how to build a simple implementation
+of a payment channel. It use cryptographics signatures to make
+repeated transfers of Ether between the same parties secure, instantaneous, and
+without transaction fees. To do it we need to understand how to
+sign and verify signatures, and setup the payment channel.
+
+Creating and verifying signatures
+=================================
+
+Imagine Alice wants to send a quantity of Ether to Bob, i.e.
+Alice is the sender and the Bob is the recipient.
+Alice only needs to send cryptographically signed messages off-chain
+(e.g. via email) to Bob and it will be very similar to writing checks.
+
+Signatures are used to authorize transactions,
+and they are a general tool that is available to
+smart contracts. Alice will build a simple
+smart contract that lets her transmit Ether, but
+in a unusual way, instead of calling a function herself
+to initiate a payment, she will let Bob
+do that, and therefore pay the transaction fee.
+The contract will work as follows:
+
+ 1. Alice deploys the ``ReceiverPays`` contract, attaching enough Ether to cover the payments that will be made.
+ 2. Alice authorizes a payment by signing a message with their private key.
+ 3. Alice sends the cryptographically signed message to Bob. The message does not need to be kept secret
+ (you will understand it later), and the mechanism for sending it does not matter.
+ 4. Bob claims their payment by presenting the signed message to the smart contract, it verifies the
+ authenticity of the message and then releases the funds.
+
+Creating the signature
+----------------------
+
+Alice does not need to interact with Ethereum network to
+sign the transaction, the proccess is completely offline.
+In this tutorial, we will sign messages in the browser
+using ``web3.js`` and ``MetaMask``.
+In particular, we will use the standard way described in `EIP-762 <https://github.com/ethereum/EIPs/pull/712>`_,
+as it provides a number of other security benefits.
+
+::
+
+ /// Hashing first makes a few things easier
+ var hash = web3.sha3("message to sign");
+ web3.personal.sign(hash, web3.eth.defaultAccount, function () {...});
+
+
+Note that the ``web3.personal.sign`` prepends the length of the message to the signed data.
+Since we hash first, the message will always be exactly 32 bytes long,
+and thus this length prefix is always the same, making everything easier.
+
+What to Sign
+------------
+
+For a contract that fulfills payments, the signed message must include:
+
+ 1. The recipient's address
+ 2. The amount to be transferred
+ 3. Protection against replay attacks
+
+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
+account.
+The smart contract will check if a nonce is used multiple times.
+
+There is another type of replay attacks, it occurs when the
+owner deploys a ``ReceiverPays`` smart contract, performs some payments,
+and then destroy the contract. Later, she decides to deploy the
+``RecipientPays`` smart contract again, but the new contract does not
+know the nonces used in the previous deployment, so the attacker
+can use the old messages again.
+
+Alice can protect against it including
+the contract's address in the message, and only
+messages containing contract's address itself will be accepted.
+This functionality can be found in the first two lines of the ``claimPayment()`` function in the full contract
+at the end of this chapter.
+
+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
+`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
+using ``abi.encodePacked``.
+Putting it all together, here is a JavaScript function that
+creates the proper signature for the ``ReceiverPays`` example:
+
+::
+
+ // recipient is the address that should be paid.
+ // amount, in wei, specifies how much ether should be sent.
+ // nonce can be any unique number to prevent replay attacks
+ // contractAddress is used to prevent cross-contract replay attacks
+ function signPayment(recipient, amount, nonce, contractAddress, callback) {
+ var hash = "0x" + ethereumjs.ABI.soliditySHA3(
+ ["address", "uint256", "uint256", "address"],
+ [recipient, amount, nonce, contractAddress]
+ ).toString("hex");
+
+ web3.personal.sign(hash, web3.eth.defaultAccount, callback);
+ }
+
+Recovering the Message Signer in Solidity
+-----------------------------------------
+
+In general, ECDSA signatures consist of two parameters, ``r`` and ``s``.
+Signatures in Ethereum include a third parameter called ``v``, that can be used
+to recover which account's private key was used to sign in the message,
+the transaction's sender. Solidity provides a built-in function
+`ecrecover <mathematical-and-cryptographic-functions>`_
+that accepts a message along with the ``r``, ``s`` and ``v`` parameters and
+returns the address that was used to sign the message.
+
+Extracting the Signature Parameters
+-----------------------------------
+
+Signatures produced by web3.js are the concatenation of ``r``, ``s`` and ``v``,
+so the first step is splitting those parameters back out. It can be done on the client,
+but doing it inside the smart contract means only one signature parameter
+needs to be sent rather than three.
+Splitting apart a byte array into component parts is a little messy.
+We will use `inline assembly <assembly>`_ to do the job
+in the ``splitSignature`` function (the third function in the full contract
+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
+``recoverSigner`` do this and their use can be found in the
+``claimPayment`` function.
+
+
+The full contract
+-----------------
+
+::
+
+ pragma solidity ^0.4.24;
+
+ contract ReceiverPays {
+ address owner = msg.sender;
+
+ mapping(uint256 => bool) usedNonces;
+
+ constructor() public payable {}
+
+ function claimPayment(uint256 amount, uint256 nonce, bytes signature) public {
+ require(!usedNonces[nonce]);
+ usedNonces[nonce] = true;
+
+ // this recreates the message that was signed on the client
+ bytes32 message = prefixed(keccak256(abi.encodePacked(msg.sender, amount, nonce, this)));
+
+ require(recoverSigner(message, signature) == owner);
+
+ msg.sender.transfer(amount);
+ }
+
+ /// destroy the contract and reclaim the leftover funds.
+ function kill() public {
+ require(msg.sender == owner);
+ selfdestruct(msg.sender);
+ }
+
+ /// signature methods.
+ function splitSignature(bytes sig)
+ internal
+ pure
+ returns (uint8 v, bytes32 r, bytes32 s)
+ {
+ require(sig.length == 65);
+
+ assembly {
+ // first 32 bytes, after the length prefix.
+ r := mload(add(sig, 32))
+ // second 32 bytes.
+ s := mload(add(sig, 64))
+ // 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)
+ internal
+ pure
+ returns (address)
+ {
+ (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
+
+ return ecrecover(message, v, r, s);
+ }
+
+ /// builds a prefixed hash to mimic the behavior of eth_sign.
+ function prefixed(bytes32 hash) internal pure returns (bytes32) {
+ return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
+ }
+ }
+
+
+Writing a Simple Payment Channel
+================================
+
+Alice will now build a simple but complete implementation of a payment channel.
+Payment channels use cryptographic signatures to make repeated transfers
+of Ether securely, instantaneously, and without transaction fees.
+
+What is a Payment Channel?
+--------------------------
+
+Payment channels allow participants to make repeated transfers of Ether without
+using transactions. This means that the delays and fees associated with transactions
+can be avoided. We are going to explore a simple unidirectional payment channel between
+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.
+
+Bob is guaranteed to receive their funds because the smart contract escrows
+the Ether and honors a valid signed message. The smart contract also enforces a timeout,
+so Alice is guaranteed to eventually recover their funds even if the recipient refuses
+to close the channel.
+It is up to the participants in a payment channel to decide how long to keep it open.
+For a short-lived transaction, such as paying an internet cafe for each minute of network access,
+or for a longer relationship, such as paying an employee an hourly wage, a payment could last for months or years.
+
+Opening the Payment Channel
+---------------------------
+
+To open the payment channel, Alice deploys the smart contract,
+attaching the Ether to be escrowed and specifying the intendend recipient
+and a maximum duration for the channel to exist. It is the function
+``SimplePaymentChannel`` in the contract, that is at the end of this chapter.
+
+Making Payments
+---------------
+
+Alice makes payments by sending signed messages to Bob.
+This step is performed entirely outside of the Ethereum network.
+Messages are cryptographically signed by the sender and then transmitted directly to the recipient.
+
+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
+amount of the individual micropayment. The recipient will naturally choose to
+redeem the most recent message because that is the one with the highest total.
+The nonce per-message is not needed anymore, because the smart contract will
+only honor a single message. The address of the smart contract is still used
+to prevent a message intended for one payment channel from being used for a different channel.
+
+Here is the modified javascript code to cryptographically sign a message from the previous chapter:
+
+::
+
+ function constructPaymentMessage(contractAddress, amount) {
+ return ethereumjs.ABI.soliditySHA3(
+ ["address", "uint256"],
+ [contractAddress, amount]
+ );
+ }
+
+ function signMessage(message, callback) {
+ web3.personal.sign(
+ "0x" + message.toString("hex"),
+ web3.eth.defaultAccount,
+ 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);
+ }
+
+
+Closing the Payment Channel
+---------------------------
+
+When Bob is ready to receive their funds, it is time to
+close the payment channel by calling a ``close`` function on the smart contract.
+Closing the channel pays the recipient the Ether they are owed and destroys the contract,
+sending any remaining Ether back to Alice.
+To close the channel, Bob needs to provide a message signed by Alice.
+
+The smart contract must verify that the message contains a valid signature from the sender.
+The process for doing this verification is the same as the process the recipient uses.
+The Solidity functions ``isValidSignature`` and ``recoverSigner`` work just like their
+JavaScript counterparts in the previous section. The latter is borrowed from the
+``ReceiverPays`` contract in the previous chapter.
+
+The ``close`` function can only be called by the payment channel recipient,
+who will naturally pass the most recent payment message because that message
+carries the highest total owed. If the sender were allowed to call this function,
+they could provide a message with a lower amount and cheat the recipient out of what they are owed.
+
+The function verifies the signed message matches the given parameters.
+If everything checks out, the recipient is sent their portion of the Ether,
+and the sender is sent the rest via a ``selfdestruct``.
+You can see the ``close`` function in the full contract.
+
+Channel Expiration
+-------------------
+
+Bob can close the payment channel at any time, but if they fail to do so,
+Alice needs a way to recover their escrowed funds. An *expiration* time was set
+at the time of contract deployment. Once that time is reached, Alice can call
+``claimTimeout`` to recover their funds. You can see the ``claimTimeout`` function in the
+full contract.
+
+After this function is called, Bob can no longer receive any Ether,
+so it is important that Bob closes the channel before the expiration is reached.
+
+
+The full contract
+-----------------
+
+::
+
+ pragma solidity ^0.4.24;
+
+ contract SimplePaymentChannel {
+ address public sender; // The account sending payments.
+ address public recipient; // The account receiving the payments.
+ uint256 public expiration; // Timeout in case the recipient never closes.
+
+ constructor (address _recipient, uint256 duration)
+ public
+ payable
+ {
+ sender = msg.sender;
+ recipient = _recipient;
+ expiration = now + duration;
+ }
+
+ function isValidSignature(uint256 amount, bytes signature)
+ internal
+ view
+ returns (bool)
+ {
+ bytes32 message = prefixed(keccak256(abi.encodePacked(this, amount)));
+
+ // check that the signature is from the payment sender
+ return recoverSigner(message, signature) == sender;
+ }
+
+ /// 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 {
+ require(msg.sender == recipient);
+ require(isValidSignature(amount, signature));
+
+ recipient.transfer(amount);
+ selfdestruct(sender);
+ }
+
+ /// the sender can extend the expiration at any time
+ function extend(uint256 newExpiration) public {
+ require(msg.sender == sender);
+ require(newExpiration > expiration);
+
+ expiration = newExpiration;
+ }
+
+ /// if the timeout is reached without the recipient closing the channel,
+ /// then the Ether is realeased back to the sender.
+ function clainTimeout() public {
+ require(now >= expiration);
+ selfdestruct(sender);
+ }
+
+ /// All functions below this are just taken from the chapter
+ /// 'creating and verifying signatures' chapter.
+
+ function splitSignature(bytes sig)
+ internal
+ pure
+ returns (uint8 v, bytes32 r, bytes32 s)
+ {
+ require(sig.length == 65);
+
+ assembly {
+ // first 32 bytes, after the length prefix
+ r := mload(add(sig, 32))
+ // second 32 bytes
+ s := mload(add(sig, 64))
+ // 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)
+ internal
+ pure
+ returns (address)
+ {
+ (uint8 v, bytes32 r, bytes32 s) = splitSignature(sig);
+
+ return ecrecover(message, v, r, s);
+ }
+
+ /// builds a prefixed hash to mimic the behavior of eth_sign.
+ function prefixed(bytes32 hash) internal pure returns (bytes32) {
+ return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", hash));
+ }
+ }
+
+
+Note: The function ``splitSignature`` is very simple and does not use all security checks.
+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
+------------------
+
+Unlike in our previous chapter, messages in a payment channel aren't
+redeemed right away. The recipient keeps track of the latest message and
+redeems it when it's time to close the payment channel. This means it's
+critical that the recipient perform their own verification of each message.
+Otherwise there is no guarantee that the recipient will be able to get paid
+in the end.
+
+The recipient should verify each message using the following process:
+
+ 1. Verify that the contact address in the message matches the payment channel.
+ 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**.
+The following code borrows the `constructMessage` function from the signing **JavaScript code**
+above:
+
+::
+
+ // this mimics the prefixing behavior of the eth_sign JSON-RPC method.
+ function prefixed(hash) {
+ return ethereumjs.ABI.soliditySHA3(
+ ["string", "bytes32"],
+ ["\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);
+ return signer.toLowerCase() ==
+ ethereumjs.Util.stripHexPrefix(expectedSigner).toLowerCase();
+ }
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index cd0dc2a4..63f8fac3 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -192,15 +192,9 @@ bool SyntaxChecker::visit(Throw const& _throwStatement)
bool SyntaxChecker::visit(UnaryOperation const& _operation)
{
- bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
if (_operation.getOperator() == Token::Add)
- {
- if (v050)
- m_errorReporter.syntaxError(_operation.location(), "Use of unary + is deprecated.");
- else
- m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated.");
- }
+ m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
+
return true;
}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index a6a85492..81547bdf 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -1073,10 +1073,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
solAssert(m_scope, "");
- if (v050)
- m_errorReporter.declarationError(varDecl.location(), errorText);
- else
- m_errorReporter.warning(varDecl.location(), errorText);
+ m_errorReporter.declarationError(varDecl.location(), errorText);
}
}
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index d703ae53..9906fa68 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -1250,13 +1250,12 @@ private:
};
/**
- * Definition of a variable as a statement inside a function. It requires a type name (which can
- * also be "var") but the actual assignment can be missing.
- * Examples: var a = 2; uint256 a;
- * As a second form, multiple variables can be declared, cannot have a type and must be assigned
- * right away. If the first or last component is unnamed, it can "consume" an arbitrary number
- * of components.
- * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d();
+ * Definition of one or more variables as a statement inside a function.
+ * If multiple variables are declared, a value has to be assigned directly.
+ * If only a single variable is declared, the value can be missing.
+ * Examples:
+ * uint[] memory a; uint a = 2;
+ * (uint a, bytes32 b, ) = f(); (, uint a, , StructName storage x) = g();
*/
class VariableDeclarationStatement: public Statement
{
@@ -1278,6 +1277,9 @@ public:
private:
/// List of variables, some of which can be empty pointers (unnamed components).
+ /// Note that the ``m_value`` member of these is unused. Instead, ``m_initialValue``
+ /// below is used, because the initial value can be a single expression assigned
+ /// to all variables.
std::vector<ASTPointer<VariableDeclaration>> m_variables;
/// The assigned expression / initial value.
ASTPointer<Expression> m_initialValue;
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 1cab0d65..822b8192 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -7878,6 +7878,7 @@ BOOST_AUTO_TEST_CASE(tuples)
char const* sourceCode = R"(
contract C {
uint[] data;
+ uint[] m_c;
function g() internal returns (uint a, uint b, uint[] storage c) {
return (1, 2, data);
}
@@ -7890,7 +7891,7 @@ BOOST_AUTO_TEST_CASE(tuples)
uint a; uint b;
(a, b) = this.h();
if (a != 5 || b != 6) return 1;
- uint[] storage c;
+ uint[] storage c = m_c;
(a, b, c) = g();
if (a != 1 || b != 2 || c[0] != 3) return 2;
(a, b) = (b, a);
@@ -9585,7 +9586,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array)
{ assembly { mstore(0, 7) return(0, 0x20) } }
mutex = 1;
// Avoid re-executing this function if we jump somewhere.
- function() internal returns (uint)[200] x;
+ function() internal returns (uint)[200] memory x;
x[0]();
return 2;
}
@@ -10113,7 +10114,7 @@ BOOST_AUTO_TEST_CASE(copy_internal_function_array_to_storage)
function() internal returns (uint)[20] x;
int mutex;
function one() public returns (uint) {
- function() internal returns (uint)[20] xmem;
+ function() internal returns (uint)[20] memory xmem;
x = xmem;
return 3;
}
diff --git a/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol
new file mode 100644
index 00000000..363d8147
--- /dev/null
+++ b/test/libsolidity/syntaxTests/array/uninitialized_storage_var.sol
@@ -0,0 +1,9 @@
+contract C {
+ function f() {
+ uint[] storage x;
+ uint[10] storage y;
+ }
+}
+// ----
+// DeclarationError: (31-47): Uninitialized storage pointer.
+// DeclarationError: (51-69): Uninitialized storage pointer.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol
index 5199e5d7..868d7bc8 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/206_storage_location_local_variables.sol
@@ -1,11 +1,9 @@
contract C {
- function f() public {
- uint[] storage x;
+ uint[] m_x;
+ function f() public view {
+ uint[] storage x = m_x;
uint[] memory y;
- uint[] memory z;
- x;y;z;
+ x;y;
}
}
// ----
-// Warning: (47-63): Uninitialized storage pointer.
-// Warning: (17-135): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol
index 80467491..edae7549 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/211_uninitialized_mapping_array_variable.sol
@@ -5,4 +5,4 @@ contract C {
}
}
// ----
-// Warning: (52-85): Uninitialized storage pointer.
+// DeclarationError: (52-85): Uninitialized storage pointer.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/212_uninitialized_mapping_array_variable_050.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/212_uninitialized_mapping_array_variable_050.sol
deleted file mode 100644
index f2028690..00000000
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/212_uninitialized_mapping_array_variable_050.sol
+++ /dev/null
@@ -1,9 +0,0 @@
-pragma experimental "v0.5.0";
-contract C {
- function f() pure public {
- mapping(uint => uint)[] storage x;
- x;
- }
-}
-// ----
-// DeclarationError: (82-115): Uninitialized storage pointer.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol
index 9d8d4834..a0b6f71e 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/233_non_initialized_references.sol
@@ -8,4 +8,4 @@ contract C {
}
}
// ----
-// Warning: (84-95): Uninitialized storage pointer.
+// DeclarationError: (84-95): Uninitialized storage pointer.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/234_non_initialized_references_050.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/234_non_initialized_references_050.sol
deleted file mode 100644
index c221b73c..00000000
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/234_non_initialized_references_050.sol
+++ /dev/null
@@ -1,11 +0,0 @@
-pragma experimental "v0.5.0";
-contract C {
- struct s {
- uint a;
- }
- function f() public {
- s storage x;
- }
-}
-// ----
-// DeclarationError: (114-125): Uninitialized storage pointer.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol
index eb7c6ea9..f635a214 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol
+++ b/test/libsolidity/syntaxTests/nameAndTypeResolution/308_rational_unary_plus_operation.sol
@@ -6,4 +6,4 @@ contract test {
}
}
// ----
-// Warning: (70-75): Use of unary + is deprecated.
+// SyntaxError: (70-75): Use of unary + is disallowed.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/310_rational_unary_plus_operation_v050.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/310_rational_unary_plus_operation_v050.sol
deleted file mode 100644
index 140655af..00000000
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/310_rational_unary_plus_operation_v050.sol
+++ /dev/null
@@ -1,10 +0,0 @@
-pragma experimental "v0.5.0";
-contract test {
- function f() pure public {
- ufixed16x2 a = +3.25;
- fixed16x2 b = -3.25;
- a; b;
- }
-}
-// ----
-// SyntaxError: (100-105): Use of unary + is deprecated.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/311_rational_unary_plus_assignment_v050.sol b/test/libsolidity/syntaxTests/nameAndTypeResolution/311_rational_unary_plus_assignment_v050.sol
deleted file mode 100644
index 7e5c0feb..00000000
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/311_rational_unary_plus_assignment_v050.sol
+++ /dev/null
@@ -1,9 +0,0 @@
-pragma experimental "v0.5.0";
-contract test {
- function f(uint x) pure public {
- uint y = +x;
- y;
- }
-}
-// ----
-// SyntaxError: (100-102): Use of unary + is deprecated.
diff --git a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol
index 95ef66d4..2b35ffda 100644
--- a/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol
+++ b/test/libsolidity/syntaxTests/parsing/arrays_in_expressions.sol
@@ -5,4 +5,4 @@ contract c {
// Warning: (39-46): Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning.
// Warning: (52-67): Variable is declared as a storage pointer. Use an explicit "storage" keyword to silence this warning.
// TypeError: (39-50): Type int_const 7 is not implicitly convertible to expected type contract c[10] storage pointer.
-// Warning: (52-67): Uninitialized storage pointer. Did you mean '<type> memory x'?
+// DeclarationError: (52-67): Uninitialized storage pointer. Did you mean '<type> memory x'?
diff --git a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol
index e311dd96..38de7b1c 100644
--- a/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol
+++ b/test/libsolidity/syntaxTests/parsing/location_specifiers_for_locals.sol
@@ -1,11 +1,9 @@
contract Foo {
- function f() public {
- uint[] storage x;
+ uint[] m_x;
+ function f() public view {
+ uint[] storage x = m_x;
uint[] memory y;
+ x; y;
}
}
// ----
-// Warning: (49-65): Uninitialized storage pointer.
-// Warning: (49-65): Unused local variable.
-// Warning: (75-90): Unused local variable.
-// Warning: (19-97): Function state mutability can be restricted to pure
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/309_rational_unary_plus_assignment.sol b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol
index a5bdd6c8..5646c43b 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/309_rational_unary_plus_assignment.sol
+++ b/test/libsolidity/syntaxTests/parsing/unary_plus_expression.sol
@@ -5,4 +5,4 @@ contract test {
}
}
// ----
-// Warning: (70-72): Use of unary + is deprecated.
+// SyntaxError: (70-72): Use of unary + is disallowed.