aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Changelog.md3
-rw-r--r--circle.yml30
-rw-r--r--docs/assembly.rst334
-rw-r--r--docs/control-structures.rst61
-rw-r--r--docs/installing-solidity.rst10
-rw-r--r--docs/julia.rst153
-rw-r--r--docs/miscellaneous.rst2
-rw-r--r--docs/using-the-compiler.rst3
-rw-r--r--libevmasm/Instruction.cpp6
-rw-r--r--libevmasm/Instruction.h5
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp33
-rw-r--r--libsolidity/analysis/DeclarationContainer.h6
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp93
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h30
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp50
-rw-r--r--libsolidity/analysis/ReferencesResolver.h7
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp35
-rw-r--r--libsolidity/ast/AST.cpp41
-rw-r--r--libsolidity/ast/AST.h47
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp14
-rw-r--r--libsolidity/interface/StandardCompiler.cpp22
-rwxr-xr-xscripts/install_deps.sh13
-rwxr-xr-xscripts/tests.sh100
-rwxr-xr-xtest/cmdlineTests.sh2
-rw-r--r--test/contracts/AuctionRegistrar.cpp9
-rw-r--r--test/contracts/FixedFeeRegistrar.cpp9
-rw-r--r--test/contracts/Wallet.cpp9
-rw-r--r--test/libsolidity/InlineAssembly.cpp14
-rw-r--r--test/libsolidity/SMTChecker.cpp6
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp48
-rw-r--r--test/libsolidity/SolidityExecutionFramework.h14
-rw-r--r--test/libsolidity/SolidityExpressionCompiler.cpp19
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp285
-rw-r--r--test/libsolidity/StandardCompiler.cpp133
-rw-r--r--test/libsolidity/ViewPureChecker.cpp2
35 files changed, 1215 insertions, 433 deletions
diff --git a/Changelog.md b/Changelog.md
index 8aa7ba0c..e027e8ad 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,11 +1,14 @@
### 0.4.21 (unreleased)
Features:
+ * C99/C++-style scoping rules (instead of JavaScript function scoping) take effect as experimental v0.5.0 feature.
* Code Generator: Assert that ``k != 0`` for ``molmod(a, b, k)`` and ``addmod(a, b, k)`` as experimental 0.5.0 feature.
* Standard JSON: Reject badly formatted invalid JSON inputs.
* Type Checker: Disallow uninitialized storage pointers as experimental 0.5.0 feature.
* Support and recommend using ``emit EventName();`` to call events explicitly.
* Syntax Analyser: Do not warn about experimental features if they do not concern code generation.
+ * Syntax Checker: Mark ``throw`` as an error as experimental 0.5.0 feature.
+ * Syntax Checker: Issue error if no visibility is specified on contract functions as experimental 0.5.0 feature.
Bugfixes:
* Assembly: Raise error on oversized number literals in assembly.
diff --git a/circle.yml b/circle.yml
index 94c711b0..add8a815 100644
--- a/circle.yml
+++ b/circle.yml
@@ -93,7 +93,7 @@ jobs:
name: Install build dependencies
command: |
apt-get -qq update
- apt-get -qy install ccache cmake libboost-all-dev libz3-dev libleveldb1v5
+ apt-get -qy install ccache cmake libboost-all-dev libz3-dev
- run:
name: Init submodules
command: |
@@ -114,15 +114,34 @@ jobs:
key: ccache-{{ arch }}-{{ .Branch }}
paths:
- ~/.ccache
+ - store_artifacts:
+ path: build/solc/solc
+ destination: solc
+ - persist_to_workspace:
+ root: build
+ paths:
+ - solc/solc
+ - test/soltest
+ - test/solfuzzer
+
+ test_x86:
+ docker:
+ - image: buildpack-deps:artful
+ steps:
+ - checkout
+ - attach_workspace:
+ at: build
+ - run:
+ name: Install dependencies
+ command: |
+ apt-get -qq update
+ apt-get -qy install libz3-dev libleveldb1v5
- run: mkdir -p test_results
- run:
name: Tests
command: scripts/tests.sh --junit_report test_results
- store_test_results:
path: test_results/
- - store_artifacts:
- path: build/solc/solc
- destination: solc
docs:
docker:
@@ -158,4 +177,7 @@ workflows:
requires:
- build_emscripten
- build_x86
+ - test_x86:
+ requires:
+ - build_x86
- docs
diff --git a/docs/assembly.rst b/docs/assembly.rst
index 35484ad4..cf9bf840 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -153,6 +153,8 @@ If an opcode takes arguments (always from the top of the stack), they are given
Note that the order of arguments can be seen to be reversed in non-functional style (explained below).
Opcodes marked with ``-`` do not push an item onto the stack, those marked with ``*`` are
special and all others push exactly one item onto the stack.
+Opcodes marked with ``F``, ``H``, ``B`` or ``C`` are present since Frontier, Homestead, Byzantium or Constantinople, respectively.
+Constantinople is still in planning and all instructions marked as such will result in an invalid instruction exception.
In the following, ``mem[a...b)`` signifies the bytes of memory starting at position ``a`` up to
(excluding) position ``b`` and ``storage[p]`` signifies the storage contents at position ``p``.
@@ -161,167 +163,173 @@ The opcodes ``pushi`` and ``jumpdest`` cannot be used directly.
In the grammar, opcodes are represented as pre-defined identifiers.
-+-------------------------+------+-----------------------------------------------------------------+
-| Instruction | | Explanation |
-+=========================+======+=================================================================+
-| stop + `-` | stop execution, identical to return(0,0) |
-+-------------------------+------+-----------------------------------------------------------------+
-| add(x, y) | | x + y |
-+-------------------------+------+-----------------------------------------------------------------+
-| sub(x, y) | | x - y |
-+-------------------------+------+-----------------------------------------------------------------+
-| mul(x, y) | | x * y |
-+-------------------------+------+-----------------------------------------------------------------+
-| div(x, y) | | x / y |
-+-------------------------+------+-----------------------------------------------------------------+
-| sdiv(x, y) | | x / y, for signed numbers in two's complement |
-+-------------------------+------+-----------------------------------------------------------------+
-| mod(x, y) | | x % y |
-+-------------------------+------+-----------------------------------------------------------------+
-| smod(x, y) | | x % y, for signed numbers in two's complement |
-+-------------------------+------+-----------------------------------------------------------------+
-| exp(x, y) | | x to the power of y |
-+-------------------------+------+-----------------------------------------------------------------+
-| not(x) | | ~x, every bit of x is negated |
-+-------------------------+------+-----------------------------------------------------------------+
-| lt(x, y) | | 1 if x < y, 0 otherwise |
-+-------------------------+------+-----------------------------------------------------------------+
-| gt(x, y) | | 1 if x > y, 0 otherwise |
-+-------------------------+------+-----------------------------------------------------------------+
-| slt(x, y) | | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
-+-------------------------+------+-----------------------------------------------------------------+
-| sgt(x, y) | | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
-+-------------------------+------+-----------------------------------------------------------------+
-| eq(x, y) | | 1 if x == y, 0 otherwise |
-+-------------------------+------+-----------------------------------------------------------------+
-| iszero(x) | | 1 if x == 0, 0 otherwise |
-+-------------------------+------+-----------------------------------------------------------------+
-| and(x, y) | | bitwise and of x and y |
-+-------------------------+------+-----------------------------------------------------------------+
-| or(x, y) | | bitwise or of x and y |
-+-------------------------+------+-----------------------------------------------------------------+
-| xor(x, y) | | bitwise xor of x and y |
-+-------------------------+------+-----------------------------------------------------------------+
-| byte(n, x) | | nth byte of x, where the most significant byte is the 0th byte |
-+-------------------------+------+-----------------------------------------------------------------+
-| addmod(x, y, m) | | (x + y) % m with arbitrary precision arithmetics |
-+-------------------------+------+-----------------------------------------------------------------+
-| mulmod(x, y, m) | | (x * y) % m with arbitrary precision arithmetics |
-+-------------------------+------+-----------------------------------------------------------------+
-| signextend(i, x) | | sign extend from (i*8+7)th bit counting from least significant |
-+-------------------------+------+-----------------------------------------------------------------+
-| keccak256(p, n) | | keccak(mem[p...(p+n))) |
-+-------------------------+------+-----------------------------------------------------------------+
-| sha3(p, n) | | keccak(mem[p...(p+n))) |
-+-------------------------+------+-----------------------------------------------------------------+
-| jump(label) | `-` | jump to label / code position |
-+-------------------------+------+-----------------------------------------------------------------+
-| jumpi(label, cond) | `-` | jump to label if cond is nonzero |
-+-------------------------+------+-----------------------------------------------------------------+
-| pc | | current position in code |
-+-------------------------+------+-----------------------------------------------------------------+
-| pop(x) | `-` | remove the element pushed by x |
-+-------------------------+------+-----------------------------------------------------------------+
-| dup1 ... dup16 | | copy ith stack slot to the top (counting from top) |
-+-------------------------+------+-----------------------------------------------------------------+
-| swap1 ... swap16 | `*` | swap topmost and ith stack slot below it |
-+-------------------------+------+-----------------------------------------------------------------+
-| mload(p) | | mem[p..(p+32)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| mstore(p, v) | `-` | mem[p..(p+32)) := v |
-+-------------------------+------+-----------------------------------------------------------------+
-| mstore8(p, v) | `-` | mem[p] := v & 0xff (only modifies a single byte) |
-+-------------------------+------+-----------------------------------------------------------------+
-| sload(p) | | storage[p] |
-+-------------------------+------+-----------------------------------------------------------------+
-| sstore(p, v) | `-` | storage[p] := v |
-+-------------------------+------+-----------------------------------------------------------------+
-| msize | | size of memory, i.e. largest accessed memory index |
-+-------------------------+------+-----------------------------------------------------------------+
-| gas | | gas still available to execution |
-+-------------------------+------+-----------------------------------------------------------------+
-| address | | address of the current contract / execution context |
-+-------------------------+------+-----------------------------------------------------------------+
-| balance(a) | | wei balance at address a |
-+-------------------------+------+-----------------------------------------------------------------+
-| caller | | call sender (excluding ``delegatecall``) |
-+-------------------------+------+-----------------------------------------------------------------+
-| callvalue | | wei sent together with the current call |
-+-------------------------+------+-----------------------------------------------------------------+
-| calldataload(p) | | call data starting from position p (32 bytes) |
-+-------------------------+------+-----------------------------------------------------------------+
-| calldatasize | | size of call data in bytes |
-+-------------------------+------+-----------------------------------------------------------------+
-| calldatacopy(t, f, s) | `-` | copy s bytes from calldata at position f to mem at position t |
-+-------------------------+------+-----------------------------------------------------------------+
-| codesize | | size of the code of the current contract / execution context |
-+-------------------------+------+-----------------------------------------------------------------+
-| codecopy(t, f, s) | `-` | copy s bytes from code at position f to mem at position t |
-+-------------------------+------+-----------------------------------------------------------------+
-| extcodesize(a) | | size of the code at address a |
-+-------------------------+------+-----------------------------------------------------------------+
-| extcodecopy(a, t, f, s) | `-` | like codecopy(t, f, s) but take code at address a |
-+-------------------------+------+-----------------------------------------------------------------+
-| returndatasize | | size of the last returndata |
-+-------------------------+------+-----------------------------------------------------------------+
-| returndatacopy(t, f, s) | `-` | copy s bytes from returndata at position f to mem at position t |
-+-------------------------+------+-----------------------------------------------------------------+
-| create(v, p, s) | | create new contract with code mem[p..(p+s)) and send v wei |
-| | | and return the new address |
-+-------------------------+------+-----------------------------------------------------------------+
-| create2(v, n, p, s) | | create new contract with code mem[p..(p+s)) at address |
-| | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
-| | | wei and return the new address |
-+-------------------------+------+-----------------------------------------------------------------+
-| call(g, a, v, in, | | call contract at address a with input mem[in..(in+insize)) |
-| insize, out, outsize) | | providing g gas and v wei and output area |
-| | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
-| | | and 1 on success |
-+-------------------------+------+-----------------------------------------------------------------+
-| callcode(g, a, v, in, | | identical to ``call`` but only use the code from a and stay |
-| insize, out, outsize) | | in the context of the current contract otherwise |
-+-------------------------+------+-----------------------------------------------------------------+
-| delegatecall(g, a, in, | | identical to ``callcode`` but also keep ``caller`` |
-| insize, out, outsize) | | and ``callvalue`` |
-+-------------------------+------+-----------------------------------------------------------------+
-| staticcall(g, a, in, | | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
-| insize, out, outsize) | | not allow state modifications |
-+-------------------------+------+-----------------------------------------------------------------+
-| return(p, s) | `-` | end execution, return data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| revert(p, s) | `-` | end execution, revert state changes, return data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| selfdestruct(a) | `-` | end execution, destroy current contract and send funds to a |
-+-------------------------+------+-----------------------------------------------------------------+
-| invalid | `-` | end execution with invalid instruction |
-+-------------------------+------+-----------------------------------------------------------------+
-| log0(p, s) | `-` | log without topics and data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| log1(p, s, t1) | `-` | log with topic t1 and data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| log2(p, s, t1, t2) | `-` | log with topics t1, t2 and data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| log3(p, s, t1, t2, t3) | `-` | log with topics t1, t2, t3 and data mem[p..(p+s)) |
-+-------------------------+------+-----------------------------------------------------------------+
-| log4(p, s, t1, t2, t3, | `-` | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
-| t4) | | |
-+-------------------------+------+-----------------------------------------------------------------+
-| origin | | transaction sender |
-+-------------------------+------+-----------------------------------------------------------------+
-| gasprice | | gas price of the transaction |
-+-------------------------+------+-----------------------------------------------------------------+
-| blockhash(b) | | hash of block nr b - only for last 256 blocks excluding current |
-+-------------------------+------+-----------------------------------------------------------------+
-| coinbase | | current mining beneficiary |
-+-------------------------+------+-----------------------------------------------------------------+
-| timestamp | | timestamp of the current block in seconds since the epoch |
-+-------------------------+------+-----------------------------------------------------------------+
-| number | | current block number |
-+-------------------------+------+-----------------------------------------------------------------+
-| difficulty | | difficulty of the current block |
-+-------------------------+------+-----------------------------------------------------------------+
-| gaslimit | | block gas limit of the current block |
-+-------------------------+------+-----------------------------------------------------------------+
++-------------------------+-----+---+-----------------------------------------------------------------+
+| Instruction | | | Explanation |
++=========================+=====+===+=================================================================+
+| stop + `-` | F | stop execution, identical to return(0,0) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| add(x, y) | | F | x + y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sub(x, y) | | F | x - y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mul(x, y) | | F | x * y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| div(x, y) | | F | x / y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sdiv(x, y) | | F | x / y, for signed numbers in two's complement |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mod(x, y) | | F | x % y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| smod(x, y) | | F | x % y, for signed numbers in two's complement |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| exp(x, y) | | F | x to the power of y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| not(x) | | F | ~x, every bit of x is negated |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| lt(x, y) | | F | 1 if x < y, 0 otherwise |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| gt(x, y) | | F | 1 if x > y, 0 otherwise |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| slt(x, y) | | F | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sgt(x, y) | | F | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| eq(x, y) | | F | 1 if x == y, 0 otherwise |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| iszero(x) | | F | 1 if x == 0, 0 otherwise |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| and(x, y) | | F | bitwise and of x and y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| or(x, y) | | F | bitwise or of x and y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| xor(x, y) | | F | bitwise xor of x and y |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| byte(n, x) | | F | nth byte of x, where the most significant byte is the 0th byte |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| shl(x, y) | | C | logical shift left y by x bits |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| shr(x, y) | | C | logical shift right y by x bits |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sar(x, y) | | C | arithmetic shift right y by x bits |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| addmod(x, y, m) | | F | (x + y) % m with arbitrary precision arithmetics |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mulmod(x, y, m) | | F | (x * y) % m with arbitrary precision arithmetics |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| signextend(i, x) | | F | sign extend from (i*8+7)th bit counting from least significant |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| keccak256(p, n) | | F | keccak(mem[p...(p+n))) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sha3(p, n) | | F | keccak(mem[p...(p+n))) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| jump(label) | `-` | F | jump to label / code position |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| jumpi(label, cond) | `-` | F | jump to label if cond is nonzero |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| pc | | F | current position in code |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| pop(x) | `-` | F | remove the element pushed by x |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| dup1 ... dup16 | | F | copy ith stack slot to the top (counting from top) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| swap1 ... swap16 | `*` | F | swap topmost and ith stack slot below it |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mload(p) | | F | mem[p..(p+32)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mstore(p, v) | `-` | F | mem[p..(p+32)) := v |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| mstore8(p, v) | `-` | F | mem[p] := v & 0xff (only modifies a single byte) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sload(p) | | F | storage[p] |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| sstore(p, v) | `-` | F | storage[p] := v |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| msize | | F | size of memory, i.e. largest accessed memory index |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| gas | | F | gas still available to execution |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| address | | F | address of the current contract / execution context |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| balance(a) | | F | wei balance at address a |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| caller | | F | call sender (excluding ``delegatecall``) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| callvalue | | F | wei sent together with the current call |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| calldataload(p) | | F | call data starting from position p (32 bytes) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| calldatasize | | F | size of call data in bytes |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| calldatacopy(t, f, s) | `-` | F | copy s bytes from calldata at position f to mem at position t |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| codesize | | F | size of the code of the current contract / execution context |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| codecopy(t, f, s) | `-` | F | copy s bytes from code at position f to mem at position t |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| extcodesize(a) | | F | size of the code at address a |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| extcodecopy(a, t, f, s) | `-` | F | like codecopy(t, f, s) but take code at address a |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| returndatasize | | B | size of the last returndata |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| returndatacopy(t, f, s) | `-` | B | copy s bytes from returndata at position f to mem at position t |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| create(v, p, s) | | F | create new contract with code mem[p..(p+s)) and send v wei |
+| | | | and return the new address |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| create2(v, n, p, s) | | C | create new contract with code mem[p..(p+s)) at address |
+| | | | keccak256(<address> . n . keccak256(mem[p..(p+s))) and send v |
+| | | | wei and return the new address |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| call(g, a, v, in, | | F | call contract at address a with input mem[in..(in+insize)) |
+| insize, out, outsize) | | | providing g gas and v wei and output area |
+| | | | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
+| | | | and 1 on success |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| callcode(g, a, v, in, | | F | identical to ``call`` but only use the code from a and stay |
+| insize, out, outsize) | | | in the context of the current contract otherwise |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| delegatecall(g, a, in, | | H | identical to ``callcode`` but also keep ``caller`` |
+| insize, out, outsize) | | | and ``callvalue`` |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| staticcall(g, a, in, | | B | identical to ``call(g, a, 0, in, insize, out, outsize)`` but do |
+| insize, out, outsize) | | | not allow state modifications |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| return(p, s) | `-` | F | end execution, return data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| revert(p, s) | `-` | B | end execution, revert state changes, return data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| selfdestruct(a) | `-` | F | end execution, destroy current contract and send funds to a |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| invalid | `-` | F | end execution with invalid instruction |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| log0(p, s) | `-` | F | log without topics and data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| log1(p, s, t1) | `-` | F | log with topic t1 and data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| log2(p, s, t1, t2) | `-` | F | log with topics t1, t2 and data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| log3(p, s, t1, t2, t3) | `-` | F | log with topics t1, t2, t3 and data mem[p..(p+s)) |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| log4(p, s, t1, t2, t3, | `-` | F | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
+| t4) | | | |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| origin | | F | transaction sender |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| gasprice | | F | gas price of the transaction |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| blockhash(b) | | F | hash of block nr b - only for last 256 blocks excluding current |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| coinbase | | F | current mining beneficiary |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| timestamp | | F | timestamp of the current block in seconds since the epoch |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| number | | F | current block number |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| difficulty | | F | difficulty of the current block |
++-------------------------+-----+---+-----------------------------------------------------------------+
+| gaslimit | | F | block gas limit of the current block |
++-------------------------+-----+---+-----------------------------------------------------------------+
Literals
--------
@@ -378,8 +386,8 @@ Functions external to inline assembly can also be accessed: The assembly will
push their entry label (with virtual function resolution applied). The calling semantics
in solidity are:
- - the caller pushes return label, arg1, arg2, ..., argn
- - the call returns with ret1, ret2, ..., retm
+ - the caller pushes ``return label``, ``arg1``, ``arg2``, ..., ``argn``
+ - the call returns with ``ret1``, ``ret2``, ..., ``retm``
This feature is still a bit cumbersome to use, because the stack offset essentially
changes during the call, and thus references to local variables will be wrong.
diff --git a/docs/control-structures.rst b/docs/control-structures.rst
index 7be92cfa..46e076e5 100644
--- a/docs/control-structures.rst
+++ b/docs/control-structures.rst
@@ -324,7 +324,8 @@ is ``false``. The default value for the ``uint`` or ``int`` types is ``0``. For
element will be initialized to the default value corresponding to its type. Finally, for dynamically-sized arrays, ``bytes``
and ``string``, the default value is an empty array or string.
-A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared.
+A variable declared anywhere within a function will be in scope for the *entire function*, regardless of where it is declared
+(this will change soon, see below).
This happens because Solidity inherits its scoping rules from JavaScript.
This is in contrast to many languages where variables are only scoped where they are declared until the end of the semantic block.
As a result, the following code is illegal and cause the compiler to throw an error, ``Identifier already declared``::
@@ -366,7 +367,9 @@ As a result, the following code is illegal and cause the compiler to throw an er
}
In addition to this, if a variable is declared, it will be initialized at the beginning of the function to its default value.
-As a result, the following code is legal, despite being poorly written::
+As a result, the following code is legal, despite being poorly written:
+
+::
pragma solidity ^0.4.0;
@@ -383,6 +386,60 @@ As a result, the following code is legal, despite being poorly written::
}
}
+Scoping starting from Version 0.5.0
+-----------------------------------
+
+Starting from version 0.5.0, Solidity will change to the more widespread scoping rules of C99
+(and many other languages): Variables are visible from the point right after their declaration
+until the end of a ``{ }``-block. As an exception to this rule, variables declared in the
+initialization part of a for-loop are only visible until the end of the for-loop.
+
+Variables and other items declared outside of a code block, for example functions, contracts,
+user-defined types, etc., do not change their scoping behaviour. This means you can
+use state variables before they are declared and call functions recursively.
+
+These rules are already introduced now as an experimental feature.
+
+As a consequence, the following examples will compile without warnings, since
+the two variables have the same name but disjoint scopes. In non-0.5.0-mode,
+they have the same scope (the function ``minimalScoping``) and thus it does
+not compile there.
+
+::
+
+ pragma solidity ^0.4.0;
+ pragma experimental "v0.5.0";
+ contract C {
+ function minimalScoping() pure public {
+ {
+ uint same2 = 0;
+ }
+
+ {
+ uint same2 = 0;
+ }
+ }
+ }
+
+As a special example of the C99 scoping rules, note that in the following,
+the first assignment to ``x`` will actually assign the outer and not the inner variable.
+In any case, you will get a warning about the outer variable being shadowed.
+
+::
+
+ pragma solidity ^0.4.0;
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public returns (uint) {
+ uint x = 1;
+ {
+ x = 2; // this will assign to the outer variable
+ uint x;
+ }
+ return x; // x has value 2
+ }
+ }
+
.. index:: ! exception, ! throw, ! assert, ! require, ! revert
Error handling: Assert, Require, Revert and Exceptions
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index 8f30f199..e26870f0 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -29,18 +29,20 @@ Further options on this page detail installing commandline Solidity compiler sof
on your computer. Choose a commandline compiler if you are working on a larger contract
or if you require more compilation options.
+.. _solcjs:
+
npm / Node.js
=============
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The
`solcjs` program has less features than all options further down this page. Our
-`Using the compiler <using-the-compiler.html>` documentation assumes you are using
+:ref:`commandline-compiler` documentation assumes you are using
the full-featured compiler, `solc`. So if you install `solcjs` from `npm` then you will
-stop reading the documentation here and then continue to <https://github.com/ethereum/solc-js>,
+stop reading the documentation here and then continue to `solc-js <https://github.com/ethereum/solc-js>`_.
-Note: The `solc-js <https://github.com/ethereum/solc-js>` project is derived from the C++
+Note: The solc-js project is derived from the C++
`solc` by using Emscripten. `solc-js` can be used in JavaScript projects directly (such as Remix).
-Please refer to the `solc-js <https://github.com/ethereum/solc-js>`_ repository for instructions.
+Please refer to the solc-js repository for instructions.
.. code:: bash
diff --git a/docs/julia.rst b/docs/julia.rst
index 9e961a9d..078bc55b 100644
--- a/docs/julia.rst
+++ b/docs/julia.rst
@@ -320,168 +320,169 @@ The following functions must be available:
+---------------------------------------------------------------------------------------------------------------+
| *Arithmetics* |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| addu256(x:u256, y:u256) -> z:u256 | x + y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| subu256(x:u256, y:u256) -> z:u256 | x - y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mulu256(x:u256, y:u256) -> z:u256 | x * y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| divu256(x:u256, y:u256) -> z:u256 | x / y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| divs256(x:s256, y:s256) -> z:s256 | x / y, for signed numbers in two's complement |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| modu256(x:u256, y:u256) -> z:u256 | x % y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mods256(x:s256, y:s256) -> z:s256 | x % y, for signed numbers in two's complement |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| signextendu256(i:u256, x:u256) -> z:u256 | sign extend from (i*8+7)th bit counting from least significant |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| expu256(x:u256, y:u256) -> z:u256 | x to the power of y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| addmodu256(x:u256, y:u256, m:u256) -> z:u256| (x + y) % m with arbitrary precision arithmetics |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mulmodu256(x:u256, y:u256, m:u256) -> z:u256| (x * y) % m with arbitrary precision arithmetics |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| ltu256(x:u256, y:u256) -> z:bool | 1 if x < y, 0 otherwise |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| gtu256(x:u256, y:u256) -> z:bool | 1 if x > y, 0 otherwise |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| sltu256(x:s256, y:s256) -> z:bool | 1 if x < y, 0 otherwise, for signed numbers in two's complement |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| sgtu256(x:s256, y:s256) -> z:bool | 1 if x > y, 0 otherwise, for signed numbers in two's complement |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| equ256(x:u256, y:u256) -> z:bool | 1 if x == y, 0 otherwise |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| notu256(x:u256) -> z:u256 | ~x, every bit of x is negated |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| andu256(x:u256, y:u256) -> z:u256 | bitwise and of x and y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| oru256(x:u256, y:u256) -> z:u256 | bitwise or of x and y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| xoru256(x:u256, y:u256) -> z:u256 | bitwise xor of x and y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| shlu256(x:u256, y:u256) -> z:u256 | logical left shift of x by y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| shru256(x:u256, y:u256) -> z:u256 | logical right shift of x by y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| saru256(x:u256, y:u256) -> z:u256 | arithmetic right shift of x by y |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| byte(n:u256, x:u256) -> v:u256 | nth byte of x, where the most significant byte is the 0th byte |
-| Cannot this be just replaced by and256(shr256(n, x), 0xff) and let it be optimised out by the EVM backend? |
-+---------------------------------------------------------------------------------------------------------------+
+| | Cannot this be just replaced by and256(shr256(n, x), 0xff) and |
+| | let it be optimised out by the EVM backend? |
++---------------------------------------------+-----------------------------------------------------------------+
| *Memory and storage* |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mload(p:u256) -> v:u256 | mem[p..(p+32)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mstore(p:u256, v:u256) | mem[p..(p+32)) := v |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| mstore8(p:u256, v:u256) | mem[p] := v & 0xff - only modifies a single byte |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| sload(p:u256) -> v:u256 | storage[p] |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| sstore(p:u256, v:u256) | storage[p] := v |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| msize() -> size:u256 | size of memory, i.e. largest accessed memory index, albeit due |
| | due to the memory extension function, which extends by words, |
| | this will always be a multiple of 32 bytes |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| *Execution control* |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| create(v:u256, p:u256, s:u256) | create new contract with code mem[p..(p+s)) and send v wei |
| | and return the new address |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| call(g:u256, a:u256, v:u256, in:u256, | call contract at address a with input mem[in..(in+insize)) |
| insize:u256, out:u256, | providing g gas and v wei and output area |
| outsize:u256) | mem[out..(out+outsize)) returning 0 on error (eg. out of gas) |
| -> r:u256 | and 1 on success |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| callcode(g:u256, a:u256, v:u256, in:u256, | identical to ``call`` but only use the code from a |
| insize:u256, out:u256, | and stay in the context of the |
| outsize:u256) -> r:u256 | current contract otherwise |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| delegatecall(g:u256, a:u256, in:u256, | identical to ``callcode``, |
| insize:u256, out:u256, | but also keep ``caller`` |
| outsize:u256) -> r:u256 | and ``callvalue`` |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| stop() | stop execution, identical to return(0,0) |
-| Perhaps it would make sense retiring this as it equals to return(0,0). It can be an optimisation by the EVM |
-| backend. |
-+---------------------------------------------------------------------------------------------------------------+
+| | Perhaps it would make sense retiring this as it equals to |
+| | return(0,0). It can be an optimisation by the EVM backend. |
++---------------------------------------------+-----------------------------------------------------------------+
| abort() | abort (equals to invalid instruction on EVM) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| return(p:u256, s:u256) | end execution, return data mem[p..(p+s)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| revert(p:u256, s:u256) | end execution, revert state changes, return data mem[p..(p+s)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| selfdestruct(a:u256) | end execution, destroy current contract and send funds to a |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| log0(p:u256, s:u256) | log without topics and data mem[p..(p+s)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| log1(p:u256, s:u256, t1:u256) | log with topic t1 and data mem[p..(p+s)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| log2(p:u256, s:u256, t1:u256, t2:u256) | log with topics t1, t2 and data mem[p..(p+s)) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| log3(p:u256, s:u256, t1:u256, t2:u256, | log with topics t, t2, t3 and data mem[p..(p+s)) |
| t3:u256) | |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| log4(p:u256, s:u256, t1:u256, t2:u256, | log with topics t1, t2, t3, t4 and data mem[p..(p+s)) |
| t3:u256, t4:u256) | |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| *State queries* |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blockcoinbase() -> address:u256 | current mining beneficiary |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blockdifficulty() -> difficulty:u256 | difficulty of the current block |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blockgaslimit() -> limit:u256 | block gas limit of the current block |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blockhash(b:u256) -> hash:u256 | hash of block nr b - only for last 256 blocks excluding current |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blocknumber() -> block:u256 | current block number |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| blocktimestamp() -> timestamp:u256 | timestamp of the current block in seconds since the epoch |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| txorigin() -> address:u256 | transaction sender |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| txgasprice() -> price:u256 | gas price of the transaction |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| gasleft() -> gas:u256 | gas still available to execution |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| balance(a:u256) -> v:u256 | wei balance at address a |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| this() -> address:u256 | address of the current contract / execution context |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| caller() -> address:u256 | call sender (excluding delegatecall) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| callvalue() -> v:u256 | wei sent together with the current call |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| calldataload(p:u256) -> v:u256 | call data starting from position p (32 bytes) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| calldatasize() -> v:u256 | size of call data in bytes |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| calldatacopy(t:u256, f:u256, s:u256) | copy s bytes from calldata at position f to mem at position t |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| codesize() -> size:u256 | size of the code of the current contract / execution context |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| codecopy(t:u256, f:u256, s:u256) | copy s bytes from code at position f to mem at position t |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| extcodesize(a:u256) -> size:u256 | size of the code at address a |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| extcodecopy(a:u256, t:u256, f:u256, s:u256) | like codecopy(t, f, s) but take code at address a |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| *Others* |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| discardu256(unused:u256) | discard value |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| splitu256tou64(x:u256) -> (x1:u64, x2:u64, | split u256 to four u64's |
| x3:u64, x4:u64) | |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| combineu64tou256(x1:u64, x2:u64, x3:u64, | combine four u64's into a single u256 |
| x4:u64) -> (x:u256) | |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
| sha3(p:u256, s:u256) -> v:u256 | keccak(mem[p...(p+s))) |
-+---------------------------------------------------------------------------------------------------------------+
++---------------------------------------------+-----------------------------------------------------------------+
Backends
--------
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 328ec6ea..b5d605ac 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -219,7 +219,7 @@ This means the following source mappings represent the same information:
``1:2:1;1:9:1;2:1:2;2:1:2;2:1:2``
-``1:2:1;:9;2::2;;``
+``1:2:1;:9;2:1:2;;``
***************
Tips and Tricks
diff --git a/docs/using-the-compiler.rst b/docs/using-the-compiler.rst
index 42cc807a..66e3ac35 100644
--- a/docs/using-the-compiler.rst
+++ b/docs/using-the-compiler.rst
@@ -9,6 +9,9 @@ Using the compiler
Using the Commandline Compiler
******************************
+.. note::
+ This section doesn't apply to :ref:`solcjs <solcjs>`.
+
One of the build targets of the Solidity repository is ``solc``, the solidity commandline compiler.
Using ``solc --help`` provides you with an explanation of all options. The compiler can produce various outputs, ranging from simple binaries and assembly over an abstract syntax tree (parse tree) to estimations of gas usage.
If you only want to compile a single file, you run it as ``solc --bin sourceFile.sol`` and it will print the binary. Before you deploy your contract, activate the optimizer while compiling using ``solc --optimize --bin sourceFile.sol``. If you want to get some of the more advanced output variants of ``solc``, it is probably better to tell it to output everything to separate files using ``solc -o outputDirectory --bin --ast --asm sourceFile.sol``.
diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp
index b38981d2..a677a631 100644
--- a/libevmasm/Instruction.cpp
+++ b/libevmasm/Instruction.cpp
@@ -50,6 +50,9 @@ const std::map<std::string, Instruction> dev::solidity::c_instructions =
{ "OR", Instruction::OR },
{ "XOR", Instruction::XOR },
{ "BYTE", Instruction::BYTE },
+ { "SHL", Instruction::SHL },
+ { "SHR", Instruction::SHR },
+ { "SAR", Instruction::SAR },
{ "ADDMOD", Instruction::ADDMOD },
{ "MULMOD", Instruction::MULMOD },
{ "SIGNEXTEND", Instruction::SIGNEXTEND },
@@ -190,6 +193,9 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ Instruction::OR, { "OR", 0, 2, 1, false, Tier::VeryLow } },
{ Instruction::XOR, { "XOR", 0, 2, 1, false, Tier::VeryLow } },
{ Instruction::BYTE, { "BYTE", 0, 2, 1, false, Tier::VeryLow } },
+ { Instruction::SHL, { "SHL", 0, 2, 1, false, Tier::VeryLow } },
+ { Instruction::SHR, { "SHR", 0, 2, 1, false, Tier::VeryLow } },
+ { Instruction::SAR, { "SAR", 0, 2, 1, false, Tier::VeryLow } },
{ Instruction::ADDMOD, { "ADDMOD", 0, 3, 1, false, Tier::Mid } },
{ Instruction::MULMOD, { "MULMOD", 0, 3, 1, false, Tier::Mid } },
{ Instruction::SIGNEXTEND, { "SIGNEXTEND", 0, 2, 1, false, Tier::Low } },
diff --git a/libevmasm/Instruction.h b/libevmasm/Instruction.h
index d9c53900..be788ddb 100644
--- a/libevmasm/Instruction.h
+++ b/libevmasm/Instruction.h
@@ -59,8 +59,11 @@ enum class Instruction: uint8_t
AND, ///< bitwise AND operation
OR, ///< bitwise OR operation
XOR, ///< bitwise XOR operation
- NOT, ///< bitwise NOT opertation
+ NOT, ///< bitwise NOT operation
BYTE, ///< retrieve single byte from word
+ SHL, ///< bitwise SHL operation
+ SHR, ///< bitwise SHR operation
+ SAR, ///< bitwise SAR operation
KECCAK256 = 0x20, ///< compute KECCAK-256 hash
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index 7508ad9e..c7ba78d6 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -79,6 +79,17 @@ Declaration const* DeclarationContainer::conflictingDeclaration(
return nullptr;
}
+void DeclarationContainer::activateVariable(ASTString const& _name)
+{
+ solAssert(
+ m_invisibleDeclarations.count(_name) && m_invisibleDeclarations.at(_name).size() == 1,
+ "Tried to activate a non-inactive variable or multiple inactive variables with the same name."
+ );
+ solAssert(m_declarations.count(_name) == 0 || m_declarations.at(_name).empty(), "");
+ m_declarations[_name].emplace_back(m_invisibleDeclarations.at(_name).front());
+ m_invisibleDeclarations.erase(_name);
+}
+
bool DeclarationContainer::registerDeclaration(
Declaration const& _declaration,
ASTString const* _name,
@@ -106,15 +117,17 @@ bool DeclarationContainer::registerDeclaration(
return true;
}
-vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
+vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
- auto result = m_declarations.find(_name);
- if (result != m_declarations.end())
- return result->second;
- if (_recursive && m_enclosingContainer)
- return m_enclosingContainer->resolveName(_name, true);
- return vector<Declaration const*>({});
+ vector<Declaration const*> result;
+ if (m_declarations.count(_name))
+ result = m_declarations.at(_name);
+ if (_alsoInvisible && m_invisibleDeclarations.count(_name))
+ result += m_invisibleDeclarations.at(_name);
+ if (result.empty() && _recursive && m_enclosingContainer)
+ result = m_enclosingContainer->resolveName(_name, true, _alsoInvisible);
+ return result;
}
vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
@@ -129,6 +142,12 @@ vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) con
if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
similar.push_back(declarationName);
}
+ for (auto const& declaration: m_invisibleDeclarations)
+ {
+ string const& declarationName = declaration.first;
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ similar.push_back(declarationName);
+ }
if (m_enclosingContainer)
similar += m_enclosingContainer->similarNames(_name);
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index f9b1bda4..e4b3320a 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -51,13 +51,17 @@ public:
/// @param _update if true, replaces a potential declaration that is already present
/// @returns false if the name was already declared.
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
- std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false) const;
+ std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const;
ASTNode const* enclosingNode() const { return m_enclosingNode; }
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
std::map<ASTString, std::vector<Declaration const*>> const& declarations() const { return m_declarations; }
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
+ /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
+ /// VariableDeclarationStatements.
+ void activateVariable(ASTString const& _name);
+
/// @returns existing declaration names similar to @a _name.
/// Searches this and all parent containers.
std::vector<ASTString> similarNames(ASTString const& _name) const;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 662792a3..2f675135 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -50,12 +50,13 @@ NameAndTypeResolver::NameAndTypeResolver(
m_scopes[nullptr]->registerDeclaration(*declaration);
}
-bool NameAndTypeResolver::registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope)
+bool NameAndTypeResolver::registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope)
{
+ bool useC99Scoping = _sourceUnit.annotation().experimentalFeatures.count(ExperimentalFeature::V050);
// The helper registers all declarations in m_scopes as a side-effect of its construction.
try
{
- DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, m_errorReporter, _currentScope);
+ DeclarationRegistrationHelper registrar(m_scopes, _sourceUnit, useC99Scoping, m_errorReporter, _currentScope);
}
catch (FatalError const&)
{
@@ -106,7 +107,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
else
for (Declaration const* declaration: declarations)
if (!DeclarationRegistrationHelper::registerDeclaration(
- target, *declaration, alias.second.get(), &imp->location(), true, m_errorReporter
+ target, *declaration, alias.second.get(), &imp->location(), true, false, m_errorReporter
))
error = true;
}
@@ -114,7 +115,7 @@ bool NameAndTypeResolver::performImports(SourceUnit& _sourceUnit, map<string, So
for (auto const& nameAndDeclaration: scope->second->declarations())
for (auto const& declaration: nameAndDeclaration.second)
if (!DeclarationRegistrationHelper::registerDeclaration(
- target, *declaration, &nameAndDeclaration.first, &imp->location(), true, m_errorReporter
+ target, *declaration, &nameAndDeclaration.first, &imp->location(), true, false, m_errorReporter
))
error = true;
}
@@ -151,6 +152,12 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
return true;
}
+void NameAndTypeResolver::activateVariable(string const& _name)
+{
+ solAssert(m_currentScope, "");
+ m_currentScope->activateVariable(_name);
+}
+
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
{
auto iterator = m_scopes.find(_scope);
@@ -159,15 +166,15 @@ vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _na
return iterator->second->resolveName(_name, false);
}
-vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _recursive) const
+vector<Declaration const*> NameAndTypeResolver::nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles) const
{
- return m_currentScope->resolveName(_name, _recursive);
+ return m_currentScope->resolveName(_name, true, _includeInvisibles);
}
-Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path, bool _recursive) const
+Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> const& _path) const
{
solAssert(!_path.empty(), "");
- vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), _recursive);
+ vector<Declaration const*> candidates = m_currentScope->resolveName(_path.front(), true);
for (size_t i = 1; i < _path.size() && candidates.size() == 1; i++)
{
if (!m_scopes.count(candidates.front()))
@@ -229,7 +236,7 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
for (auto const& instruction: c_instructions)
{
string const instructionName{boost::algorithm::to_lower_copy(instruction.first)};
- auto declarations = nameFromCurrentScope(instructionName);
+ auto declarations = nameFromCurrentScope(instructionName, true);
for (Declaration const* const declaration: declarations)
{
solAssert(!!declaration, "");
@@ -244,19 +251,24 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
}
}
+void NameAndTypeResolver::setScope(ASTNode const* _node)
+{
+ m_currentScope = m_scopes[_node].get();
+}
+
bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode)
{
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(&_node))
{
bool success = true;
- m_currentScope = m_scopes[contract->scope()].get();
+ setScope(contract->scope());
solAssert(!!m_currentScope, "");
for (ASTPointer<InheritanceSpecifier> const& baseContract: contract->baseContracts())
if (!resolveNamesAndTypes(*baseContract, true))
success = false;
- m_currentScope = m_scopes[contract].get();
+ setScope(contract);
if (success)
{
@@ -273,7 +285,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
// these can contain code, only resolve parameters for now
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
- m_currentScope = m_scopes[contract].get();
+ setScope(contract);
if (!resolveNamesAndTypes(*node, false))
{
success = false;
@@ -287,12 +299,12 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
if (!_resolveInsideCode)
return success;
- m_currentScope = m_scopes[contract].get();
+ setScope(contract);
// now resolve references inside the code
for (ASTPointer<ASTNode> const& node: contract->subNodes())
{
- m_currentScope = m_scopes[contract].get();
+ setScope(contract);
if (!resolveNamesAndTypes(*node, true))
success = false;
}
@@ -301,7 +313,7 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
else
{
if (m_scopes.count(&_node))
- m_currentScope = m_scopes[&_node].get();
+ setScope(&_node);
return ReferencesResolver(m_errorReporter, *this, _resolveInsideCode).resolve(_node);
}
}
@@ -434,9 +446,11 @@ string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
+ bool _useC99Scoping,
ErrorReporter& _errorReporter,
ASTNode const* _currentScope
):
+ m_useC99Scoping(_useC99Scoping),
m_scopes(_scopes),
m_currentScope(_currentScope),
m_errorReporter(_errorReporter)
@@ -451,6 +465,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
string const* _name,
SourceLocation const* _errorLocation,
bool _warnOnShadow,
+ bool _inactive,
ErrorReporter& _errorReporter
)
{
@@ -460,10 +475,13 @@ bool DeclarationRegistrationHelper::registerDeclaration(
string name = _name ? *_name : _declaration.name();
Declaration const* shadowedDeclaration = nullptr;
if (_warnOnShadow && !name.empty() && _container.enclosingContainer())
- for (auto const* decl: _container.enclosingContainer()->resolveName(name, true))
+ for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true))
shadowedDeclaration = decl;
- if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract()))
+ // We use "invisible" for both inactive variables in blocks and for members invisible in contracts.
+ // They cannot both be true at the same time.
+ solAssert(!(_inactive && !_declaration.isVisibleInContract()), "");
+ if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive))
{
SourceLocation firstDeclarationLocation;
SourceLocation secondDeclarationLocation;
@@ -605,6 +623,34 @@ void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
closeCurrentScope();
}
+bool DeclarationRegistrationHelper::visit(Block& _block)
+{
+ _block.setScope(m_currentScope);
+ if (m_useC99Scoping)
+ enterNewSubScope(_block);
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(Block&)
+{
+ if (m_useC99Scoping)
+ closeCurrentScope();
+}
+
+bool DeclarationRegistrationHelper::visit(ForStatement& _for)
+{
+ _for.setScope(m_currentScope);
+ if (m_useC99Scoping)
+ enterNewSubScope(_for);
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ForStatement&)
+{
+ if (m_useC99Scoping)
+ closeCurrentScope();
+}
+
void DeclarationRegistrationHelper::endVisit(VariableDeclarationStatement& _variableDeclarationStatement)
{
// Register the local variables with the function
@@ -632,14 +678,14 @@ void DeclarationRegistrationHelper::endVisit(EventDefinition&)
closeCurrentScope();
}
-void DeclarationRegistrationHelper::enterNewSubScope(Declaration const& _declaration)
+void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
{
map<ASTNode const*, shared_ptr<DeclarationContainer>>::iterator iter;
bool newlyAdded;
shared_ptr<DeclarationContainer> container(new DeclarationContainer(m_currentScope, m_scopes[m_currentScope].get()));
- tie(iter, newlyAdded) = m_scopes.emplace(&_declaration, move(container));
+ tie(iter, newlyAdded) = m_scopes.emplace(&_subScope, move(container));
solAssert(newlyAdded, "Unable to add new scope.");
- m_currentScope = &_declaration;
+ m_currentScope = &_subScope;
}
void DeclarationRegistrationHelper::closeCurrentScope()
@@ -667,7 +713,12 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
if (fun->isConstructor())
warnAboutShadowing = false;
- registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, m_errorReporter);
+ // Register declaration as inactive if we are in block scope and C99 mode.
+ bool inactive =
+ m_useC99Scoping &&
+ (dynamic_cast<Block const*>(m_currentScope) || dynamic_cast<ForStatement const*>(m_currentScope));
+
+ registerDeclaration(*m_scopes[m_currentScope], _declaration, nullptr, nullptr, warnAboutShadowing, inactive, m_errorReporter);
_declaration.setScope(m_currentScope);
if (_opensScope)
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index 9aea07ab..3d10fbd8 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -56,7 +56,7 @@ public:
/// @returns false in case of error.
/// @param _currentScope should be nullptr but can be used to inject new declarations into
/// existing scopes, used by the snippets feature.
- bool registerDeclarations(ASTNode& _sourceUnit, ASTNode const* _currentScope = nullptr);
+ bool registerDeclarations(SourceUnit& _sourceUnit, ASTNode const* _currentScope = nullptr);
/// Applies the effect of import directives.
bool performImports(SourceUnit& _sourceUnit, std::map<std::string, SourceUnit const*> const& _sourceUnits);
/// Resolves all names and types referenced from the given AST Node.
@@ -69,20 +69,24 @@ public:
/// that create their own scope.
/// @returns false in case of error.
bool updateDeclaration(Declaration const& _declaration);
+ /// Activates a previously inactive (invisible) variable. To be used in C99 scpoing for
+ /// VariableDeclarationStatements.
+ void activateVariable(std::string const& _name);
/// Resolves the given @a _name inside the scope @a _scope. If @a _scope is omitted,
/// the global scope is used (i.e. the one containing only the pre-defined global variables).
/// @returns a pointer to the declaration on success or nullptr on failure.
+ /// SHOULD only be used for testing.
std::vector<Declaration const*> resolveName(ASTString const& _name, ASTNode const* _scope = nullptr) const;
- /// Resolves a name in the "current" scope. Should only be called during the initial
- /// resolving phase.
- std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _recursive = true) const;
+ /// Resolves a name in the "current" scope, but also searches parent scopes.
+ /// Should only be called during the initial resolving phase.
+ std::vector<Declaration const*> nameFromCurrentScope(ASTString const& _name, bool _includeInvisibles = false) const;
- /// Resolves a path starting from the "current" scope. Should only be called during the initial
- /// resolving phase.
+ /// Resolves a path starting from the "current" scope, but also searches parent scopes.
+ /// Should only be called during the initial resolving phase.
/// @note Returns a null pointer if any component in the path was not unique or not found.
- Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path, bool _recursive = true) const;
+ Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
/// returns the vector of declarations without repetitions
std::vector<Declaration const*> cleanedDeclarations(
@@ -96,6 +100,9 @@ public:
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
std::string similarNameSuggestions(ASTString const& _name) const;
+ /// Sets the current scope.
+ void setScope(ASTNode const* _node);
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
@@ -135,6 +142,7 @@ public:
DeclarationRegistrationHelper(
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
+ bool _useC99Scoping,
ErrorReporter& _errorReporter,
ASTNode const* _currentScope = nullptr
);
@@ -145,6 +153,7 @@ public:
std::string const* _name,
SourceLocation const* _errorLocation,
bool _warnOnShadow,
+ bool _inactive,
ErrorReporter& _errorReporter
);
@@ -163,12 +172,16 @@ private:
void endVisit(FunctionDefinition& _function) override;
bool visit(ModifierDefinition& _modifier) override;
void endVisit(ModifierDefinition& _modifier) override;
+ bool visit(Block& _block) override;
+ void endVisit(Block& _block) override;
+ bool visit(ForStatement& _forLoop) override;
+ void endVisit(ForStatement& _forLoop) override;
void endVisit(VariableDeclarationStatement& _variableDeclarationStatement) override;
bool visit(VariableDeclaration& _declaration) override;
bool visit(EventDefinition& _event) override;
void endVisit(EventDefinition& _event) override;
- void enterNewSubScope(Declaration const& _declaration);
+ void enterNewSubScope(ASTNode& _subScope);
void closeCurrentScope();
void registerDeclaration(Declaration& _declaration, bool _opensScope);
@@ -177,6 +190,7 @@ private:
/// @returns the canonical name of the current scope.
std::string currentCanonicalName() const;
+ bool m_useC99Scoping = false;
std::map<ASTNode const*, std::shared_ptr<DeclarationContainer>>& m_scopes;
ASTNode const* m_currentScope = nullptr;
VariableScope* m_currentFunction = nullptr;
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 0bb5e3fe..985c44d0 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -43,6 +43,56 @@ bool ReferencesResolver::resolve(ASTNode const& _root)
return !m_errorOccurred;
}
+bool ReferencesResolver::visit(Block const& _block)
+{
+ if (!m_resolveInsideCode)
+ return false;
+ m_experimental050Mode = _block.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+ // C99-scoped variables
+ if (m_experimental050Mode)
+ m_resolver.setScope(&_block);
+ return true;
+}
+
+void ReferencesResolver::endVisit(Block const& _block)
+{
+ if (!m_resolveInsideCode)
+ return;
+
+ // C99-scoped variables
+ if (m_experimental050Mode)
+ m_resolver.setScope(_block.scope());
+}
+
+bool ReferencesResolver::visit(ForStatement const& _for)
+{
+ if (!m_resolveInsideCode)
+ return false;
+ m_experimental050Mode = _for.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+ // C99-scoped variables
+ if (m_experimental050Mode)
+ m_resolver.setScope(&_for);
+ return true;
+}
+
+void ReferencesResolver::endVisit(ForStatement const& _for)
+{
+ if (!m_resolveInsideCode)
+ return;
+ if (m_experimental050Mode)
+ m_resolver.setScope(_for.scope());
+}
+
+void ReferencesResolver::endVisit(VariableDeclarationStatement const& _varDeclStatement)
+{
+ if (!m_resolveInsideCode)
+ return;
+ if (m_experimental050Mode)
+ for (auto const& var: _varDeclStatement.declarations())
+ if (var)
+ m_resolver.activateVariable(var->name());
+}
+
bool ReferencesResolver::visit(Identifier const& _identifier)
{
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
diff --git a/libsolidity/analysis/ReferencesResolver.h b/libsolidity/analysis/ReferencesResolver.h
index fef2e73f..4e8f54b5 100644
--- a/libsolidity/analysis/ReferencesResolver.h
+++ b/libsolidity/analysis/ReferencesResolver.h
@@ -57,7 +57,11 @@ public:
bool resolve(ASTNode const& _root);
private:
- virtual bool visit(Block const&) override { return m_resolveInsideCode; }
+ virtual bool visit(Block const& _block) override;
+ virtual void endVisit(Block const& _block) override;
+ virtual bool visit(ForStatement const& _for) override;
+ virtual void endVisit(ForStatement const& _for) override;
+ virtual void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
virtual bool visit(Identifier const& _identifier) override;
virtual bool visit(ElementaryTypeName const& _typeName) override;
virtual bool visit(FunctionDefinition const& _functionDefinition) override;
@@ -90,6 +94,7 @@ private:
std::vector<ParameterList const*> m_returnParameters;
bool const m_resolveInsideCode;
bool m_errorOccurred = false;
+ bool m_experimental050Mode = false;
};
}
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 74834ba4..ddac194b 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -174,10 +174,18 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
bool SyntaxChecker::visit(Throw const& _throwStatement)
{
- m_errorReporter.warning(
- _throwStatement.location(),
- "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
- );
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (v050)
+ m_errorReporter.syntaxError(
+ _throwStatement.location(),
+ "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
+ );
+ else
+ m_errorReporter.warning(
+ _throwStatement.location(),
+ "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
+ );
return true;
}
@@ -204,13 +212,20 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
if (_function.noVisibilitySpecified())
- m_errorReporter.warning(
- _function.location(),
- "No visibility specified. Defaulting to \"" +
- Declaration::visibilityToString(_function.visibility()) +
- "\"."
- );
+ {
+ if (v050)
+ m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
+ else
+ m_errorReporter.warning(
+ _function.location(),
+ "No visibility specified. Defaulting to \"" +
+ Declaration::visibilityToString(_function.visibility()) +
+ "\"."
+ );
+ }
return true;
}
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 8da6964e..27220b1f 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -96,20 +96,6 @@ set<SourceUnit const*> SourceUnit::referencedSourceUnits(bool _recurse, set<Sour
return sourceUnits;
}
-SourceUnit const& Declaration::sourceUnit() const
-{
- solAssert(!!m_scope, "");
- ASTNode const* scope = m_scope;
- while (dynamic_cast<Declaration const*>(scope) && dynamic_cast<Declaration const*>(scope)->m_scope)
- scope = dynamic_cast<Declaration const*>(scope)->m_scope;
- return dynamic_cast<SourceUnit const&>(*scope);
-}
-
-string Declaration::sourceUnitName() const
-{
- return sourceUnit().annotation().path;
-}
-
ImportAnnotation& ImportDirective::annotation() const
{
if (!m_annotation)
@@ -408,12 +394,36 @@ UserDefinedTypeNameAnnotation& UserDefinedTypeName::annotation() const
return dynamic_cast<UserDefinedTypeNameAnnotation&>(*m_annotation);
}
+SourceUnit const& Scopable::sourceUnit() const
+{
+ ASTNode const* s = scope();
+ solAssert(s, "");
+ // will not always be a declaratoion
+ while (dynamic_cast<Scopable const*>(s) && dynamic_cast<Scopable const*>(s)->scope())
+ s = dynamic_cast<Scopable const*>(s)->scope();
+ return dynamic_cast<SourceUnit const&>(*s);
+}
+
+string Scopable::sourceUnitName() const
+{
+ return sourceUnit().annotation().path;
+}
+
bool VariableDeclaration::isLValue() const
{
// External function parameters and constant declared variables are Read-Only
return !isExternalCallableParameter() && !m_isConstant;
}
+bool VariableDeclaration::isLocalVariable() const
+{
+ auto s = scope();
+ return
+ dynamic_cast<CallableDeclaration const*>(s) ||
+ dynamic_cast<Block const*>(s) ||
+ dynamic_cast<ForStatement const*>(s);
+}
+
bool VariableDeclaration::isCallableParameter() const
{
auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
@@ -459,8 +469,7 @@ bool VariableDeclaration::isExternalCallableParameter() const
bool VariableDeclaration::canHaveAutoType() const
{
- auto const* callable = dynamic_cast<CallableDeclaration const*>(scope());
- return (!!callable && !isCallableParameter());
+ return isLocalVariable() && !isCallableParameter();
}
TypePointer VariableDeclaration::type() const
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index c0d55aec..863ad2fe 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -140,9 +140,32 @@ private:
};
/**
+ * Abstract class that is added to each AST node that is stored inside a scope
+ * (including scopes).
+ */
+class Scopable
+{
+public:
+ /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
+ /// Available only after name and type resolution step.
+ ASTNode const* scope() const { return m_scope; }
+ void setScope(ASTNode const* _scope) { m_scope = _scope; }
+
+ /// @returns the source unit this scopable is present in.
+ SourceUnit const& sourceUnit() const;
+
+ /// @returns the source name this scopable is present in.
+ /// Can be combined with annotation().canonicalName (if present) to form a globally unique name.
+ std::string sourceUnitName() const;
+
+protected:
+ ASTNode const* m_scope = nullptr;
+};
+
+/**
* Abstract AST class for a declaration (contract, function, struct, variable, import directive).
*/
-class Declaration: public ASTNode
+class Declaration: public ASTNode, public Scopable
{
public:
/// Visibility ordered from restricted to unrestricted.
@@ -171,7 +194,7 @@ public:
ASTPointer<ASTString> const& _name,
Visibility _visibility = Visibility::Default
):
- ASTNode(_location), m_name(_name), m_visibility(_visibility), m_scope(nullptr) {}
+ ASTNode(_location), m_name(_name), m_visibility(_visibility) {}
/// @returns the declared name.
ASTString const& name() const { return *m_name; }
@@ -181,17 +204,6 @@ public:
virtual bool isVisibleInContract() const { return visibility() != Visibility::External; }
bool isVisibleInDerivedContracts() const { return isVisibleInContract() && visibility() >= Visibility::Internal; }
- /// @returns the scope this declaration resides in. Can be nullptr if it is the global scope.
- /// Available only after name and type resolution step.
- ASTNode const* scope() const { return m_scope; }
- void setScope(ASTNode const* _scope) { m_scope = _scope; }
-
- /// @returns the source unit this declaration is present in.
- SourceUnit const& sourceUnit() const;
-
- /// @returns the source name this declaration is present in.
- /// Can be combined with annotation().canonicalName to form a globally unique name.
- std::string sourceUnitName() const;
std::string fullyQualifiedName() const { return sourceUnitName() + ":" + name(); }
virtual bool isLValue() const { return false; }
@@ -213,7 +225,6 @@ protected:
private:
ASTPointer<ASTString> m_name;
Visibility m_visibility;
- ASTNode const* m_scope;
};
/**
@@ -289,6 +300,8 @@ private:
/**
* Abstract class that is added to each AST node that can store local variables.
+ * Local variables in functions are always added to functions, even though they are not
+ * in scope for the whole function.
*/
class VariableScope
{
@@ -662,7 +675,7 @@ public:
virtual bool isLValue() const override;
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
- bool isLocalVariable() const { return !!dynamic_cast<CallableDeclaration const*>(scope()); }
+ bool isLocalVariable() const;
/// @returns true if this variable is a parameter or return parameter of a function.
bool isCallableParameter() const;
/// @returns true if this variable is a return parameter of a function.
@@ -1004,7 +1017,7 @@ private:
/**
* Brace-enclosed block containing zero or more statements.
*/
-class Block: public Statement
+class Block: public Statement, public Scopable
{
public:
Block(
@@ -1111,7 +1124,7 @@ private:
/**
* For loop statement
*/
-class ForStatement: public BreakableStatement
+class ForStatement: public BreakableStatement, public Scopable
{
public:
ForStatement(
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index a05ac57d..1030523a 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -548,6 +548,20 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
"the Metropolis hard fork. Before that it acts as an invalid instruction."
);
+ static set<solidity::Instruction> experimentalInstructions{
+ solidity::Instruction::SHL,
+ solidity::Instruction::SHR,
+ solidity::Instruction::SAR
+ };
+ if (experimentalInstructions.count(_instr))
+ m_errorReporter.warning(
+ _location,
+ "The \"" +
+ boost::to_lower_copy(instructionInfo(_instr).name)
+ + "\" instruction is only available after " +
+ "the Constantinople hard fork. Before that it acts as an invalid instruction."
+ );
+
if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
m_errorReporter.warning(
_location,
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index fb973d51..8c64c164 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -327,13 +327,29 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
m_compilerStack.setOptimiserSettings(optimize, optimizeRuns);
map<string, h160> libraries;
- Json::Value jsonLibraries = settings.get("libraries", Json::Value());
+ Json::Value jsonLibraries = settings.get("libraries", Json::Value(Json::objectValue));
+ if (!jsonLibraries.isObject())
+ return formatFatalError("JSONError", "\"libraries\" is not a JSON object.");
for (auto const& sourceName: jsonLibraries.getMemberNames())
{
auto const& jsonSourceName = jsonLibraries[sourceName];
+ if (!jsonSourceName.isObject())
+ return formatFatalError("JSONError", "library entry is not a JSON object.");
for (auto const& library: jsonSourceName.getMemberNames())
- // @TODO use libraries only for the given source
- libraries[library] = h160(jsonSourceName[library].asString());
+ {
+ try
+ {
+ // @TODO use libraries only for the given source
+ libraries[library] = h160(jsonSourceName[library].asString());
+ }
+ catch (dev::BadHexCharacter)
+ {
+ return formatFatalError(
+ "JSONError",
+ "Invalid library address (\"" + jsonSourceName[library].asString() + "\") supplied."
+ );
+ }
+ }
}
m_compilerStack.setLibraries(libraries);
diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh
index d62cffb7..e884ed65 100755
--- a/scripts/install_deps.sh
+++ b/scripts/install_deps.sh
@@ -168,11 +168,12 @@ case $(uname -s) in
# Debian
#------------------------------------------------------------------------------
- Debian)
+ Debian*)
#Debian
+ . /etc/os-release
install_z3=""
- case $(lsb_release -cs) in
- wheezy)
+ case $VERSION_ID in
+ 7)
#wheezy
echo "Installing solidity dependencies on Debian Wheezy (7.x)."
echo "ERROR - 'install_deps.sh' doesn't have Debian Wheezy support yet."
@@ -182,16 +183,16 @@ case $(uname -s) in
echo "See also https://github.com/ethereum/webthree-umbrella/issues/495 where we are working through Alpine support."
exit 1
;;
- jessie)
+ 8)
#jessie
echo "Installing solidity dependencies on Debian Jesse (8.x)."
;;
- stretch)
+ 9)
#stretch
echo "Installing solidity dependencies on Debian Stretch (9.x)."
install_z3="libz3-dev"
;;
- buster)
+ 10)
#buster
echo "Installing solidity dependencies on Debian Buster (10.x)."
install_z3="libz3-dev"
diff --git a/scripts/tests.sh b/scripts/tests.sh
index d414643b..3c80adc5 100755
--- a/scripts/tests.sh
+++ b/scripts/tests.sh
@@ -45,54 +45,66 @@ else
fi
echo "Running commandline tests..."
-"$REPO_ROOT/test/cmdlineTests.sh"
+"$REPO_ROOT/test/cmdlineTests.sh" &
+CMDLINE_PID=$!
+# Only run in parallel if this is run on CI infrastructure
+if [ -z "$CI" ]
+then
+ wait $CMDLINE_PID
+fi
-# This conditional is only needed because we don't have a working Homebrew
-# install for `eth` at the time of writing, so we unzip the ZIP file locally
-# instead. This will go away soon.
-if [[ "$OSTYPE" == "darwin"* ]]; then
- ETH_PATH="$REPO_ROOT/eth"
-elif [ -z $CI ]; then
- ETH_PATH="eth"
-else
- mkdir -p /tmp/test
- ETH_BINARY=eth_byzantium_artful
- ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
- if grep -i trusty /etc/lsb-release >/dev/null 2>&1
- then
- ETH_BINARY=eth_byzantium2
- ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f"
+function download_eth()
+{
+ if [[ "$OSTYPE" == "darwin"* ]]; then
+ ETH_PATH="$REPO_ROOT/eth"
+ elif [ -z $CI ]; then
+ ETH_PATH="eth"
+ else
+ mkdir -p /tmp/test
+ ETH_BINARY=eth_byzantium_artful
+ ETH_HASH="e527dd3e3dc17b983529dd7dcfb74a0d3a5aed4e"
+ if grep -i trusty /etc/lsb-release >/dev/null 2>&1
+ then
+ ETH_BINARY=eth_byzantium2
+ ETH_HASH="4dc3f208475f622be7c8e53bee720e14cd254c6f"
+ fi
+ wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY
+ test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth"
+ sync
+ chmod +x /tmp/test/eth
+ sync # Otherwise we might get a "text file busy" error
+ ETH_PATH="/tmp/test/eth"
fi
- wget -q -O /tmp/test/eth https://github.com/ethereum/cpp-ethereum/releases/download/solidityTester/$ETH_BINARY
- test "$(shasum /tmp/test/eth)" = "$ETH_HASH /tmp/test/eth"
- sync
- chmod +x /tmp/test/eth
- sync # Otherwise we might get a "text file busy" error
- ETH_PATH="/tmp/test/eth"
-fi
-# This trailing ampersand directs the shell to run the command in the background,
-# that is, it is forked and run in a separate sub-shell, as a job,
-# asynchronously. The shell will immediately return the return status of 0 for
-# true and continue as normal, either processing further commands in a script
-# or returning the cursor focus back to the user in a Linux terminal.
-$ETH_PATH --test -d /tmp/test &
-ETH_PID=$!
+}
+
+# $1: data directory
+# echos the PID
+function run_eth()
+{
+ $ETH_PATH --test -d "$1" >/dev/null 2>&1 &
+ echo $!
+ # Wait until the IPC endpoint is available.
+ while [ ! -S "$1"/geth.ipc ] ; do sleep 1; done
+ sleep 2
+}
+
+download_eth
+ETH_PID=$(run_eth /tmp/test)
+
+progress="--show-progress"
+if [ "$CI" ]
+then
+ progress=""
+fi
-# Wait until the IPC endpoint is available. That won't be available instantly.
-# The node needs to get a little way into its startup sequence before the IPC
-# is available and is ready for the unit-tests to start talking to it.
-while [ ! -S /tmp/test/geth.ipc ]; do sleep 2; done
-echo "--> IPC available."
-sleep 2
-# And then run the Solidity unit-tests (once without optimization, once with),
-# pointing to that IPC endpoint.
echo "--> Running tests without optimizer..."
- "$REPO_ROOT"/build/test/soltest --show-progress $testargs_no_opt -- --ipcpath /tmp/test/geth.ipc && \
- echo "--> Running tests WITH optimizer..." && \
- "$REPO_ROOT"/build/test/soltest --show-progress $testargs_opt -- --optimize --ipcpath /tmp/test/geth.ipc
-ERROR_CODE=$?
+"$REPO_ROOT"/build/test/soltest $testargs_no_opt $progress -- --ipcpath /tmp/test/geth.ipc
+echo "--> Running tests WITH optimizer..."
+"$REPO_ROOT"/build/test/soltest $testargs_opt $progress -- --optimize --ipcpath /tmp/test/geth.ipc
+
+wait $CMDLINE_PID
+
pkill "$ETH_PID" || true
sleep 4
-pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true
-exit $ERROR_CODE
+pgrep "$ETH_PID" && pkill -9 "$ETH_PID" || true \ No newline at end of file
diff --git a/test/cmdlineTests.sh b/test/cmdlineTests.sh
index f2c1f2c3..e86e0ad4 100755
--- a/test/cmdlineTests.sh
+++ b/test/cmdlineTests.sh
@@ -187,4 +187,4 @@ TMPDIR=$(mktemp -d)
done
)
rm -rf "$TMPDIR"
-echo "Done."
+echo "Commandline tests successful."
diff --git a/test/contracts/AuctionRegistrar.cpp b/test/contracts/AuctionRegistrar.cpp
index c9c744af..5e4991e2 100644
--- a/test/contracts/AuctionRegistrar.cpp
+++ b/test/contracts/AuctionRegistrar.cpp
@@ -220,13 +220,8 @@ protected:
void deployRegistrar()
{
if (!s_compiledRegistrar)
- {
- m_compiler.reset(false);
- m_compiler.addSource("", registrarCode);
- m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns);
- BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
- s_compiledRegistrar.reset(new bytes(m_compiler.object("GlobalRegistrar").bytecode));
- }
+ s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "GlobalRegistrar")));
+
sendMessage(*s_compiledRegistrar, true);
BOOST_REQUIRE(!m_output.empty());
}
diff --git a/test/contracts/FixedFeeRegistrar.cpp b/test/contracts/FixedFeeRegistrar.cpp
index 8327999d..a3a27c37 100644
--- a/test/contracts/FixedFeeRegistrar.cpp
+++ b/test/contracts/FixedFeeRegistrar.cpp
@@ -132,13 +132,8 @@ protected:
void deployRegistrar()
{
if (!s_compiledRegistrar)
- {
- m_compiler.reset(false);
- m_compiler.addSource("", registrarCode);
- m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns);
- BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
- s_compiledRegistrar.reset(new bytes(m_compiler.object("FixedFeeRegistrar").bytecode));
- }
+ s_compiledRegistrar.reset(new bytes(compileContract(registrarCode, "FixedFeeRegistrar")));
+
sendMessage(*s_compiledRegistrar, true);
BOOST_REQUIRE(!m_output.empty());
}
diff --git a/test/contracts/Wallet.cpp b/test/contracts/Wallet.cpp
index 90334ad6..1031e8f1 100644
--- a/test/contracts/Wallet.cpp
+++ b/test/contracts/Wallet.cpp
@@ -447,13 +447,8 @@ protected:
)
{
if (!s_compiledWallet)
- {
- m_compiler.reset(false);
- m_compiler.addSource("", walletCode);
- m_compiler.setOptimiserSettings(m_optimize, m_optimizeRuns);
- BOOST_REQUIRE_MESSAGE(m_compiler.compile(), "Compiling contract failed");
- s_compiledWallet.reset(new bytes(m_compiler.object("Wallet").bytecode));
- }
+ s_compiledWallet.reset(new bytes(compileContract(walletCode, "Wallet")));
+
bytes args = encodeArgs(u256(0x60), _required, _dailyLimit, u256(_owners.size()), _owners);
sendMessage(*s_compiledWallet + args, true, _value);
BOOST_REQUIRE(!m_output.empty());
diff --git a/test/libsolidity/InlineAssembly.cpp b/test/libsolidity/InlineAssembly.cpp
index 45fb54f8..ea120657 100644
--- a/test/libsolidity/InlineAssembly.cpp
+++ b/test/libsolidity/InlineAssembly.cpp
@@ -774,6 +774,20 @@ BOOST_AUTO_TEST_CASE(create2)
BOOST_CHECK(successAssemble("{ pop(create2(10, 0x123, 32, 64)) }"));
}
+BOOST_AUTO_TEST_CASE(shift)
+{
+ BOOST_CHECK(successAssemble("{ pop(shl(10, 32)) }"));
+ BOOST_CHECK(successAssemble("{ pop(shr(10, 32)) }"));
+ BOOST_CHECK(successAssemble("{ pop(sar(10, 32)) }"));
+}
+
+BOOST_AUTO_TEST_CASE(shift_constantinople_warning)
+{
+ CHECK_PARSE_WARNING("{ pop(shl(10, 32)) }", Warning, "The \"shl\" instruction is only available after the Constantinople hard fork");
+ CHECK_PARSE_WARNING("{ pop(shr(10, 32)) }", Warning, "The \"shr\" instruction is only available after the Constantinople hard fork");
+ CHECK_PARSE_WARNING("{ pop(sar(10, 32)) }", Warning, "The \"sar\" instruction is only available after the Constantinople hard fork");
+}
+
BOOST_AUTO_TEST_CASE(jump_warning)
{
CHECK_PARSE_WARNING("{ 1 jump }", Warning, "Jump instructions");
diff --git a/test/libsolidity/SMTChecker.cpp b/test/libsolidity/SMTChecker.cpp
index 5088ab94..12b5f439 100644
--- a/test/libsolidity/SMTChecker.cpp
+++ b/test/libsolidity/SMTChecker.cpp
@@ -466,7 +466,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
text = R"(
contract C {
function f(uint x) public pure {
- for (uint y = 2; x < 10; ) {
+ uint y;
+ for (y = 2; x < 10; ) {
y = 3;
}
assert(y == 3);
@@ -477,7 +478,8 @@ BOOST_AUTO_TEST_CASE(for_loop)
text = R"(
contract C {
function f(uint x) public pure {
- for (uint y = 2; x < 10; ) {
+ uint y;
+ for (y = 2; x < 10; ) {
y = 3;
}
assert(y == 2);
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 3882e4ea..c352a2c2 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -284,6 +284,54 @@ BOOST_AUTO_TEST_CASE(conditional_expression_functions)
ABI_CHECK(callContractFunction("f(bool)", false), encodeArgs(u256(2)));
}
+BOOST_AUTO_TEST_CASE(C99_scoping_activation)
+{
+ char const* sourceCode = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public returns (uint) {
+ uint x = 7;
+ {
+ x = 3; // This should still assign to the outer variable
+ uint x;
+ x = 4; // This should assign to the new one
+ }
+ return x;
+ }
+ function g() pure public returns (uint x) {
+ x = 7;
+ {
+ x = 3;
+ uint x;
+ return x; // This returns the new variable, i.e. 0
+ }
+ }
+ function h() pure public returns (uint x, uint a, uint b) {
+ x = 7;
+ {
+ x = 3;
+ a = x; // This should read from the outer
+ uint x = 4;
+ b = x;
+ }
+ }
+ function i() pure public returns (uint x, uint a) {
+ x = 7;
+ {
+ x = 3;
+ uint x = x; // This should read from the outer and assign to the inner
+ a = x;
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(3));
+ ABI_CHECK(callContractFunction("g()"), encodeArgs(0));
+ ABI_CHECK(callContractFunction("h()"), encodeArgs(3, 3, 4));
+ ABI_CHECK(callContractFunction("i()"), encodeArgs(3, 3));
+}
+
BOOST_AUTO_TEST_CASE(recursive_calls)
{
char const* sourceCode = R"(
diff --git a/test/libsolidity/SolidityExecutionFramework.h b/test/libsolidity/SolidityExecutionFramework.h
index b853d558..f562721d 100644
--- a/test/libsolidity/SolidityExecutionFramework.h
+++ b/test/libsolidity/SolidityExecutionFramework.h
@@ -52,6 +52,17 @@ public:
std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>()
) override
{
+ bytes bytecode = compileContract(_sourceCode, _contractName, _libraryAddresses);
+ sendMessage(bytecode + _arguments, true, _value);
+ return m_output;
+ }
+
+ bytes compileContract(
+ std::string const& _sourceCode,
+ std::string const& _contractName = "",
+ std::map<std::string, dev::test::Address> const& _libraryAddresses = std::map<std::string, dev::test::Address>()
+ )
+ {
// Silence compiler version warning
std::string sourceCode = "pragma solidity >=0.0;\n" + _sourceCode;
m_compiler.reset(false);
@@ -72,8 +83,7 @@ public:
}
eth::LinkerObject obj = m_compiler.object(_contractName.empty() ? m_compiler.lastContractName() : _contractName);
BOOST_REQUIRE(obj.linkReferences.empty());
- sendMessage(obj.bytecode + _arguments, true, _value);
- return m_output;
+ return obj.bytecode;
}
protected:
diff --git a/test/libsolidity/SolidityExpressionCompiler.cpp b/test/libsolidity/SolidityExpressionCompiler.cpp
index 67747386..e2a0c3cd 100644
--- a/test/libsolidity/SolidityExpressionCompiler.cpp
+++ b/test/libsolidity/SolidityExpressionCompiler.cpp
@@ -322,10 +322,10 @@ BOOST_AUTO_TEST_CASE(arithmetics)
{
char const* sourceCode = R"(
contract test {
- function f(uint y) { var x = ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }
+ function f(uint y) { ((((((((y ^ 8) & 7) | 6) - 5) + 4) % 3) / 2) * 1); }
}
)";
- bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bytes expectation({byte(Instruction::PUSH1), 0x1,
byte(Instruction::PUSH1), 0x2,
byte(Instruction::PUSH1), 0x3,
@@ -334,7 +334,7 @@ BOOST_AUTO_TEST_CASE(arithmetics)
byte(Instruction::PUSH1), 0x6,
byte(Instruction::PUSH1), 0x7,
byte(Instruction::PUSH1), 0x8,
- byte(Instruction::DUP10),
+ byte(Instruction::DUP9),
byte(Instruction::XOR),
byte(Instruction::AND),
byte(Instruction::OR),
@@ -364,13 +364,13 @@ BOOST_AUTO_TEST_CASE(unary_operators)
{
char const* sourceCode = R"(
contract test {
- function f(int y) { var x = !(~+- y == 2); }
+ function f(int y) { !(~+- y == 2); }
}
)";
- bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}, {"test", "f", "x"}});
+ bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "y"}});
bytes expectation({byte(Instruction::PUSH1), 0x2,
- byte(Instruction::DUP3),
+ byte(Instruction::DUP2),
byte(Instruction::PUSH1), 0x0,
byte(Instruction::SUB),
byte(Instruction::NOT),
@@ -383,7 +383,7 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
{
char const* sourceCode = R"(
contract test {
- function f(uint a) { var x = --a ^ (a-- ^ (++a ^ a++)); }
+ function f(uint a) returns (uint x) { x = --a ^ (a-- ^ (++a ^ a++)); }
}
)";
bytes code = compileFirstExpression(sourceCode, {}, {{"test", "f", "a"}, {"test", "f", "x"}});
@@ -426,7 +426,10 @@ BOOST_AUTO_TEST_CASE(unary_inc_dec)
byte(Instruction::POP), // second ++
// Stack here: a x a^(a+2)^(a+2)
byte(Instruction::DUP3), // will change
- byte(Instruction::XOR)});
+ byte(Instruction::XOR),
+ byte(Instruction::SWAP1),
+ byte(Instruction::POP),
+ byte(Instruction::DUP1)});
// Stack here: a x a^(a+2)^(a+2)^a
BOOST_CHECK_EQUAL_COLLECTIONS(code.begin(), code.end(), expectation.begin(), expectation.end());
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index 93abee0d..8c2d853c 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -76,15 +76,236 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
BOOST_AUTO_TEST_CASE(double_variable_declaration)
{
- char const* text = R"(
+ string text = R"(
contract test {
- function f() public {
+ function f() pure public {
uint256 x;
if (true) { uint256 x; }
}
}
)";
- CHECK_ERROR(text, DeclarationError, "Identifier already declared.");
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared");
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration_050)
+{
+ string text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ uint256 x;
+ if (true) { uint256 x; }
+ }
+ }
+ )";
+ CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
+ "This declaration shadows an existing declaration.",
+ "Experimental features",
+ "Unused local variable",
+ "Unused local variable"
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope)
+{
+ string text = R"(
+ contract test {
+ function f() pure public {
+ { uint x; }
+ { uint x; }
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared");
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_050)
+{
+ string text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ { uint x; }
+ { uint x; }
+ }
+ }
+ )";
+ CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
+ "Experimental features",
+ "Unused local variable",
+ "Unused local variable"
+ }));
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation)
+{
+ string text = R"(
+ contract test {
+ function f() pure public {
+ { uint x; }
+ uint x;
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Identifier already declared");
+}
+
+BOOST_AUTO_TEST_CASE(double_variable_declaration_disjoint_scope_activation_050)
+{
+ string text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ { uint x; }
+ uint x;
+ }
+ }
+ )";
+ CHECK_WARNING_ALLOW_MULTI(text, (vector<string>{
+ "Experimental features",
+ "Unused local variable",
+ "Unused local variable"
+ }));
+}
+BOOST_AUTO_TEST_CASE(scoping_old)
+{
+ char const* text = R"(
+ contract test {
+ function f() pure public {
+ x = 4;
+ uint256 x = 2;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(scoping)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() public {
+ {
+ uint256 x;
+ }
+ x = 2;
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_activation_old)
+{
+ char const* text = R"(
+ contract test {
+ function f() pure public {
+ x = 3;
+ uint x;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(scoping_activation)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ x = 3;
+ uint x;
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_self_use)
+{
+ char const* text = R"(
+ contract test {
+ function f() pure public {
+ uint a = a;
+ }
+ }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(scoping_self_use_050)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ uint a = a;
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_for)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++){
+ x = 2;
+ }
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Experimental features");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_for2)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++)
+ x = 2;
+ }
+ }
+ )";
+ CHECK_WARNING(text, "Experimental features");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_for3)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ for (uint x = 0; x < 10; x ++){
+ x = 2;
+ }
+ x = 4;
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
+}
+
+BOOST_AUTO_TEST_CASE(scoping_for_decl_in_body)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract test {
+ function f() pure public {
+ for (;; y++){
+ uint y = 3;
+ }
+ }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier");
}
BOOST_AUTO_TEST_CASE(name_shadowing)
@@ -1004,7 +1225,7 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation)
{
char const* text = R"(
contract B {
- function f() mod1(2, true) mod2("0123456") public { }
+ function f() mod1(2, true) mod2("0123456") pure public { }
modifier mod1(uint a, bool b) { if (b) _; }
modifier mod2(bytes7 a) { while (a == "1234567") _; }
}
@@ -1039,11 +1260,23 @@ BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables)
{
char const* text = R"(
contract B {
- function f() mod(x) public { uint x = 7; }
+ function f() mod(x) pure public { uint x = 7; }
modifier mod(uint a) { if (a > 0) _; }
}
)";
- CHECK_SUCCESS(text);
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(function_modifier_invocation_local_variables050)
+{
+ char const* text = R"(
+ pragma experimental "v0.5.0";
+ contract B {
+ function f() mod(x) pure public { uint x = 7; }
+ modifier mod(uint a) { if (a > 0) _; }
+ }
+ )";
+ CHECK_ERROR(text, DeclarationError, "Undeclared identifier.");
}
BOOST_AUTO_TEST_CASE(function_modifier_double_invocation)
@@ -6332,7 +6565,16 @@ BOOST_AUTO_TEST_CASE(warn_about_throw)
}
}
)";
- CHECK_WARNING(text, "\"throw\" is deprecated");
+ CHECK_WARNING(text, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"");
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure public {
+ throw;
+ }
+ }
+ )";
+ CHECK_ERROR(text, SyntaxError, "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"");
}
BOOST_AUTO_TEST_CASE(bare_revert)
@@ -7732,7 +7974,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
char const* text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.balance;
}
}
@@ -7741,7 +7983,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.transfer;
}
}
@@ -7750,7 +7992,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.send;
}
}
@@ -7759,7 +8001,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.call;
}
}
@@ -7768,7 +8010,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.callcode;
}
}
@@ -7777,7 +8019,7 @@ BOOST_AUTO_TEST_CASE(no_address_members_on_contract)
text = R"(
pragma experimental "v0.5.0";
contract C {
- function f() {
+ function f() public {
this.delegatecall;
}
}
@@ -7861,6 +8103,23 @@ BOOST_AUTO_TEST_CASE(getter_is_memory_type)
}
}
+BOOST_AUTO_TEST_CASE(require_visibility_specifiers)
+{
+ char const* text = R"(
+ contract C {
+ function f() pure { }
+ }
+ )";
+ CHECK_WARNING(text, "No visibility specified. Defaulting to");
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract C {
+ function f() pure { }
+ }
+ )";
+ CHECK_ERROR(text, SyntaxError, "No visibility specified.");
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index 404f709d..0bb94172 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -570,6 +570,139 @@ BOOST_AUTO_TEST_CASE(library_filename_with_colon)
BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["git:library.sol"]["L"][0].isObject());
}
+BOOST_AUTO_TEST_CASE(libraries_invalid_top_level)
+{
+ char const* input = R"(
+ {
+ "language": "Solidity",
+ "settings": {
+ "libraries": "42"
+ },
+ "sources": {
+ "empty": {
+ "content": ""
+ }
+ }
+ }
+ )";
+ Json::Value result = compile(input);
+ BOOST_CHECK(containsError(result, "JSONError", "\"libraries\" is not a JSON object."));
+}
+
+BOOST_AUTO_TEST_CASE(libraries_invalid_entry)
+{
+ char const* input = R"(
+ {
+ "language": "Solidity",
+ "settings": {
+ "libraries": {
+ "L": "42"
+ }
+ },
+ "sources": {
+ "empty": {
+ "content": ""
+ }
+ }
+ }
+ )";
+ Json::Value result = compile(input);
+ BOOST_CHECK(containsError(result, "JSONError", "library entry is not a JSON object."));
+}
+
+BOOST_AUTO_TEST_CASE(libraries_invalid_hex)
+{
+ char const* input = R"(
+ {
+ "language": "Solidity",
+ "settings": {
+ "libraries": {
+ "library.sol": {
+ "L": "0x4200000000000000000000000000000000000xx1"
+ }
+ }
+ },
+ "sources": {
+ "empty": {
+ "content": ""
+ }
+ }
+ }
+ )";
+ Json::Value result = compile(input);
+ BOOST_CHECK(containsError(result, "JSONError", "Invalid library address (\"0x4200000000000000000000000000000000000xx1\") supplied."));
+}
+
+BOOST_AUTO_TEST_CASE(libraries_various_addresses)
+{
+ char const* input = R"(
+ {
+ "language": "Solidity",
+ "settings": {
+ "libraries": {
+ "library.sol": {
+ "L": 42,
+ "L3": "42",
+ "L4": "0x42",
+ "L5": "0x4200000000000000000000000000000000000001",
+ "L6": "4200000000000000000000000000000000000001"
+ }
+ }
+ },
+ "sources": {
+ "empty": {
+ "content": ""
+ }
+ }
+ }
+ )";
+ Json::Value result = compile(input);
+ BOOST_CHECK(containsAtMostWarnings(result));
+}
+
+BOOST_AUTO_TEST_CASE(library_linking)
+{
+ char const* input = R"(
+ {
+ "language": "Solidity",
+ "settings": {
+ "libraries": {
+ "library.sol": {
+ "L": "0x4200000000000000000000000000000000000001"
+ }
+ },
+ "outputSelection": {
+ "fileA": {
+ "A": [
+ "evm.bytecode"
+ ]
+ }
+ }
+ },
+ "sources": {
+ "fileA": {
+ "content": "import \"library.sol\"; import \"library2.sol\"; contract A { function f() returns (uint) { L2.g(); return L.g(); } }"
+ },
+ "library.sol": {
+ "content": "library L { function g() returns (uint) { return 1; } }"
+ },
+ "library2.sol": {
+ "content": "library L2 { function g() { } }"
+ }
+ }
+ }
+ )";
+ Json::Value result = compile(input);
+ BOOST_CHECK(containsAtMostWarnings(result));
+ Json::Value contract = getContractResult(result, "fileA", "A");
+ BOOST_CHECK(contract.isObject());
+ BOOST_CHECK(contract["evm"]["bytecode"].isObject());
+ BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"].isObject());
+ BOOST_CHECK(!contract["evm"]["bytecode"]["linkReferences"]["library.sol"].isObject());
+ BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"].isObject());
+ BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"].isArray());
+ BOOST_CHECK(contract["evm"]["bytecode"]["linkReferences"]["library2.sol"]["L2"][0].isObject());
+}
BOOST_AUTO_TEST_SUITE_END()
diff --git a/test/libsolidity/ViewPureChecker.cpp b/test/libsolidity/ViewPureChecker.cpp
index 3a03c877..2599ca28 100644
--- a/test/libsolidity/ViewPureChecker.cpp
+++ b/test/libsolidity/ViewPureChecker.cpp
@@ -148,7 +148,7 @@ BOOST_AUTO_TEST_CASE(environment_access)
BOOST_AUTO_TEST_CASE(view_error_for_050)
{
CHECK_ERROR(
- "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view { x = 2; } }",
+ "pragma experimental \"v0.5.0\"; contract C { uint x; function f() view public { x = 2; } }",
TypeError,
"Function declared as view, but this expression (potentially) modifies the state and thus requires non-payable (the default) or payable."
);