aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CODING_STYLE.md262
-rw-r--r--Changelog.md9
-rw-r--r--circle.yml16
-rw-r--r--docs/abi-spec.rst6
-rw-r--r--docs/assembly.rst5
-rw-r--r--docs/contracts.rst40
-rw-r--r--docs/contributing.rst55
-rw-r--r--docs/installing-solidity.rst1
-rw-r--r--docs/introduction-to-smart-contracts.rst8
-rw-r--r--docs/miscellaneous.rst11
-rw-r--r--docs/types.rst2
-rw-r--r--libdevcore/Algorithms.h76
-rw-r--r--libevmasm/Assembly.cpp4
-rw-r--r--libevmasm/CommonSubexpressionEliminator.h8
-rw-r--r--libevmasm/Instruction.cpp2
-rw-r--r--libevmasm/PeepholeOptimiser.cpp47
-rw-r--r--libevmasm/SemanticInformation.cpp7
-rw-r--r--libevmasm/SemanticInformation.h3
-rw-r--r--libsolidity/analysis/PostTypeChecker.cpp36
-rw-r--r--libsolidity/analysis/PostTypeChecker.h5
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp10
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp16
-rw-r--r--libsolidity/analysis/TypeChecker.cpp62
-rw-r--r--libsolidity/ast/AST.h14
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp2
-rw-r--r--libsolidity/ast/AST_accept.h6
-rw-r--r--libsolidity/ast/Types.cpp21
-rw-r--r--libsolidity/ast/Types.h13
-rw-r--r--libsolidity/codegen/ArrayUtils.cpp49
-rw-r--r--libsolidity/codegen/ArrayUtils.h6
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp66
-rw-r--r--libsolidity/codegen/CompilerUtils.h7
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp10
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp50
-rw-r--r--libsolidity/formal/SymbolicBoolVariable.cpp6
-rw-r--r--libsolidity/formal/SymbolicBoolVariable.h3
-rw-r--r--libsolidity/formal/SymbolicIntVariable.cpp6
-rw-r--r--libsolidity/formal/SymbolicIntVariable.h3
-rw-r--r--libsolidity/formal/SymbolicVariable.cpp4
-rw-r--r--libsolidity/formal/SymbolicVariable.h8
-rw-r--r--libsolidity/parsing/Parser.cpp40
-rw-r--r--libsolidity/parsing/Parser.h7
-rwxr-xr-xscripts/cpp-ethereum/build.sh17
-rw-r--r--scripts/cpp-ethereum/eth_artful.docker7
-rw-r--r--scripts/cpp-ethereum/eth_trusty.docker7
-rw-r--r--std/StandardToken.sol4
-rw-r--r--std/owned.sol4
-rw-r--r--test/RPCSession.cpp6
-rw-r--r--test/libevmasm/Optimiser.cpp114
-rw-r--r--test/libsolidity/AnalysisFramework.cpp28
-rw-r--r--test/libsolidity/AnalysisFramework.h7
-rw-r--r--test/libsolidity/JSONCompiler.cpp8
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp194
-rw-r--r--test/libsolidity/SolidityNameAndTypeResolution.cpp216
-rw-r--r--test/libsolidity/SolidityOptimizer.cpp64
-rw-r--r--test/libsolidity/SolidityParser.cpp20
-rw-r--r--test/libsolidity/StandardCompiler.cpp6
-rw-r--r--test/libsolidity/SyntaxTest.cpp98
-rw-r--r--test/libsolidity/SyntaxTest.h31
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol5
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol10
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol11
-rw-r--r--test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol6
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol7
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol9
-rw-r--r--test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol5
-rw-r--r--test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol10
-rw-r--r--test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol5
-rw-r--r--test/libsolidity/syntaxTests/parsing/smoke_test.sol4
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol15
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol15
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol7
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol7
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol8
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol8
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol12
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol4
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol7
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_default.sol6
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_default050.sol7
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_external050.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_internal.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_private.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_public.sol5
-rw-r--r--test/libsolidity/syntaxTests/visibility/interface/function_public050.sol6
-rw-r--r--test/tools/isoltest.cpp65
86 files changed, 1582 insertions, 520 deletions
diff --git a/CODING_STYLE.md b/CODING_STYLE.md
new file mode 100644
index 00000000..2cc9ac70
--- /dev/null
+++ b/CODING_STYLE.md
@@ -0,0 +1,262 @@
+0. Formatting
+
+GOLDEN RULE: Follow the style of the existing code when you make changes.
+
+a. Use tabs for leading indentation
+- tab stops are every 4 characters (only relevant for line length).
+- One indentation level -> exactly one byte (i.e. a tab character) in the source file.
+b. Line widths:
+- Lines should be at most 99 characters wide to make diff views readable and reduce merge conflicts.
+- Lines of comments should be formatted according to ease of viewing, but simplicity is to be preferred over beauty.
+c. Single-statement blocks should not have braces, unless required for clarity.
+d. Never place condition bodies on same line as condition.
+e. Space between keyword and opening parenthesis, but not following opening parenthesis or before final parenthesis.
+f. No spaces for unary operators, `->` or `.`.
+g. No space before ':' but one after it, except in the ternary operator: one on both sides.
+h. Add spaces around all other operators.
+i. Braces, when used, always have their own lines and are at same indentation level as "parent" scope.
+j. If lines are broken, a list of elements enclosed with parentheses (of any kind) and separated by a
+ separator (of any kind) are formatted such that there is exactly one element per line, followed by
+ the separator, the opening parenthesis is on the first line, followed by a line break and the closing
+ parenthesis is on a line of its own (unindented). See example below.
+
+(WRONG)
+if( a==b[ i ] ) { printf ("Hello\n"); }
+foo->bar(someLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName);
+cout << "some very long string that contains completely irrelevant text that talks about this and that and contains the words \"lorem\" and \"ipsum\"" << endl;
+
+(RIGHT)
+if (a == b[i])
+ printf("Hello\n"); // NOTE spaces used instead of tab here for clarity - first byte should be '\t'.
+foo->bar(
+ someLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName,
+ anotherLongVariableName
+);
+cout <<
+ "some very long string that contains completely irrelevant " <<
+ "text that talks about this and that and contains the words " <<
+ "\"lorem\" and \"ipsum\"" <<
+ endl;
+
+
+
+1. Namespaces;
+
+a. No "using namespace" declarations in header files.
+b. All symbols should be declared in a namespace except for final applications.
+c. Use anonymous namespaces for helpers whose scope is a cpp file only.
+d. Preprocessor symbols should be prefixed with the namespace in all-caps and an underscore.
+
+(WRONG)
+#include <cassert>
+using namespace std;
+tuple<float, float> meanAndSigma(vector<float> const& _v);
+
+(CORRECT)
+#include <cassert>
+std::tuple<float, float> meanAndSigma(std::vector<float> const& _v);
+
+
+
+2. Preprocessor;
+
+a. File comment is always at top, and includes:
+- Copyright.
+- License (e.g. see COPYING).
+b. Never use #ifdef/#define/#endif file guards. Prefer #pragma once as first line below file comment.
+c. Prefer static const variable to value macros.
+d. Prefer inline constexpr functions to function macros.
+e. Split complex macro on multiple lines with '\'.
+
+
+
+3. Capitalization;
+
+GOLDEN RULE: Preprocessor: ALL_CAPS; C++: camelCase.
+
+a. Use camelCase for splitting words in names, except where obviously extending STL/boost functionality in which case follow those naming conventions.
+b. The following entities' first alpha is upper case:
+- Type names.
+- Template parameters.
+- Enum members.
+- static const variables that form an external API.
+c. All preprocessor symbols (macros, macro arguments) in full uppercase with underscore word separation.
+
+
+All other entities' first alpha is lower case.
+
+
+
+4. Variable prefixes:
+
+a. Leading underscore "_" to parameter names.
+- Exception: "o_parameterName" when it is used exclusively for output. See 6(f).
+- Exception: "io_parameterName" when it is used for both input and output. See 6(f).
+b. Leading "g_" to global (non-const) variables.
+c. Leading "s_" to static (non-const, non-global) variables.
+
+
+
+5. Assertions:
+
+- use `solAssert` and `solUnimplementedAssert` generously to check assumptions
+ that span across different parts of the code base, for example before dereferencing
+ a pointer.
+
+
+6. Declarations:
+
+a. {Typename} + {qualifiers} + {name}.
+b. Only one per line.
+c. Associate */& with type, not variable (at ends with parser, but more readable, and safe if in conjunction with (b)).
+d. Favour declarations close to use; don't habitually declare at top of scope ala C.
+e. Pass non-trivial parameters as const reference, unless the data is to be copied into the function, then either pass by const reference or by value and use std::move.
+f. If a function returns multiple values, use std::tuple (std::pair acceptable) or better introduce a struct type. Do not use */& arguments.
+g. Use parameters of pointer type only if ``nullptr`` is a valid argument, use references otherwise. Often, ``boost::optional`` is better suited than a raw pointer.
+h. Never use a macro where adequate non-preprocessor C++ can be written.
+i. Only use ``auto`` if the type is very long and rather irrelevant.
+j. Do not pass bools: prefer enumerations instead.
+k. Prefer enum class to straight enum.
+l. Always initialize POD variables, even if their value is overwritten later.
+
+
+(WRONG)
+const double d = 0;
+int i, j;
+char *s;
+float meanAndSigma(std::vector<float> _v, float* _sigma, bool _approximate);
+Derived* x(dynamic_cast<Derived*>(base));
+for (map<ComplexTypeOne, ComplexTypeTwo>::iterator i = l.begin(); i != l.end(); ++l) {}
+
+
+(CORRECT)
+enum class Accuracy
+{
+ Approximate,
+ Exact
+};
+struct MeanSigma
+{
+ float mean;
+ float standardDeviation;
+};
+double const d = 0;
+int i;
+int j;
+char* s;
+MeanAndSigma ms meanAndSigma(std::vector<float> const& _v, Accuracy _a);
+Derived* x = dynamic_cast<Derived*>(base);
+for (auto i = x->begin(); i != x->end(); ++i) {}
+
+
+7. Structs & classes
+
+a. Structs to be used when all members public and no virtual functions.
+- In this case, members should be named naturally and not prefixed with 'm_'
+b. Classes to be used in all other circumstances.
+
+
+
+8. Members:
+
+a. One member per line only.
+b. Private, non-static, non-const fields prefixed with m_.
+c. Avoid public fields, except in structs.
+d. Use override, final and const as much as possible.
+e. No implementations with the class declaration, except:
+- template or force-inline method (though prefer implementation at bottom of header file).
+- one-line implementation (in which case include it in same line as declaration).
+f. For a property 'foo'
+- Member: m_foo;
+- Getter: foo() [ also: for booleans, isFoo() ];
+- Setter: setFoo();
+
+
+
+9. Naming
+
+a. Avoid unpronouncable names
+b. Names should be shortened only if they are extremely common, but shortening should be generally avoided
+c. Avoid prefixes of initials (e.g. do not use IMyInterface, CMyImplementation)
+c. Find short, memorable & (at least semi-) descriptive names for commonly used classes or name-fragments.
+- A dictionary and thesaurus are your friends.
+- Spell correctly.
+- Think carefully about the class's purpose.
+- Imagine it as an isolated component to try to decontextualise it when considering its name.
+- Don't be trapped into naming it (purely) in terms of its implementation.
+
+
+
+10. Type-definitions
+
+a. Prefer 'using' to 'typedef'. e.g. using ints = std::vector<int>; rather than typedef std::vector<int> ints;
+b. Generally avoid shortening a standard form that already includes all important information:
+- e.g. stick to shared_ptr<X> rather than shortening to ptr<X>.
+c. Where there are exceptions to this (due to excessive use and clear meaning), note the change prominently and use it consistently.
+- e.g. using Guard = std::lock_guard<std::mutex>; ///< Guard is used throughout the codebase since it is clear in meaning and used commonly.
+d. In general expressions should be roughly as important/semantically meaningful as the space they occupy.
+e. Avoid introducing aliases for types unless they are very complicated. Consider the number of items a brain can keep track of at the same time.
+
+
+
+11. Commenting
+
+a. Comments should be doxygen-compilable, using @notation rather than \notation.
+b. Document the interface, not the implementation.
+- Documentation should be able to remain completely unchanged, even if the method is reimplemented.
+- Comment in terms of the method properties and intended alteration to class state (or what aspects of the state it reports).
+- Be careful to scrutinise documentation that extends only to intended purpose and usage.
+- Reject documentation that is simply an English transaction of the implementation.
+c. Avoid in-code comments. Instead, try to extract blocks of functionality into functions. This often already eliminates the need for an in-code comment.
+
+
+12. Include Headers
+
+Includes should go in increasing order of generality (libsolidity -> libevmasm -> libdevcore -> boost -> STL).
+The corresponding .h file should be the first include in the respective .cpp file.
+Insert empty lines between blocks of include files.
+
+Example:
+
+```
+#include <libsolidity/codegen/ExpressionCompiler.h>
+
+#include <libsolidity/ast/AST.h>
+#include <libsolidity/codegen/CompilerContext.h>
+#include <libsolidity/codegen/CompilerUtils.h>
+#include <libsolidity/codegen/LValue.h>
+
+#include <libevmasm/GasMeter.h>
+
+#include <libdevcore/Common.h>
+#include <libdevcore/SHA3.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+#include <boost/algorithm/string/replace.hpp>
+
+#include <utility>
+#include <numeric>
+```
+
+See http://stackoverflow.com/questions/614302/c-header-order/614333#614333 for the reason: this makes it easier to find missing includes in header files.
+
+
+13. Recommended reading
+
+Herb Sutter and Bjarne Stroustrup
+- "C++ Core Guidelines" (https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md)
+
+Herb Sutter and Andrei Alexandrescu
+- "C++ Coding Standards: 101 Rules, Guidelines, and Best Practices"
+
+Scott Meyers
+- "Effective C++: 55 Specific Ways to Improve Your Programs and Designs (3rd Edition)"
+- "More Effective C++: 35 New Ways to Improve Your Programs and Designs"
+- "Effective Modern C++: 42 Specific Ways to Improve Your Use of C++11 and C++14"
diff --git a/Changelog.md b/Changelog.md
index d35a2b45..d6860bdf 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,10 +1,17 @@
### 0.4.22 (unreleased)
Features:
+ * Code Generator: Initialize arrays without using ``msize()``.
+ * Code Generator: More specialized and thus optimized implementation for ``x.push(...)``
* Commandline interface: Error when missing or inaccessible file detected. Suppress it with the ``--ignore-missing`` flag.
* General: Support accessing dynamic return data in post-byzantium EVMs.
* Interfaces: Allow overriding external functions in interfaces with public in an implementing contract.
+ * Optimizer: Remove useless ``SWAP1`` instruction preceding a commutative instruction (such as ``ADD``, ``MUL``, etc).
+ * Optimizer: Replace comparison operators (``LT``, ``GT``, etc) with opposites if preceded by ``SWAP1``, e.g. ``SWAP1 LT`` is replaced with ``GT``.
+ * Optimizer: Optimize across ``mload`` if ``msize()`` is not used.
* Syntax Checker: Issue warning for empty structs (or error as experimental 0.5.0 feature).
+ * General: Introduce new constructor syntax using the ``constructor`` keyword as experimental 0.5.0 feature.
+ * Inheritance: Error when using empty parenthesis for base class constructors that require arguments as experimental 0.5.0 feature.
Bugfixes:
* Code Generator: Allow ``block.blockhash`` without being called.
@@ -12,9 +19,11 @@ Bugfixes:
* Code Generator: Properly skip unneeded storage array cleanup when not reducing length.
* Code Generator: Bugfix in modifier lookup in libraries.
* Code Generator: Implement packed encoding of external function types.
+ * Code Generator: Treat empty base constructor argument list as not provided.
* Commandline interface: Support ``--evm-version constantinople`` properly.
* DocString Parser: Fix error message for empty descriptions.
* Standard JSON: Support ``constantinople`` as ``evmVersion`` properly.
+ * Type Checker: Fix detection of recursive structs.
* Type System: Improve error message when attempting to shift by a fractional amount.
* Type System: Make external library functions accessible.
* Type System: Prevent encoding of weird types.
diff --git a/circle.yml b/circle.yml
index 2b34b1aa..96e62f7b 100644
--- a/circle.yml
+++ b/circle.yml
@@ -1,3 +1,10 @@
+defaults:
+ # The default for tags is to not run, so we have to explicitly match a filter.
+ - build_on_tags: &build_on_tags
+ filters:
+ tags:
+ only: /.*/
+
version: 2
jobs:
build_emscripten:
@@ -157,15 +164,18 @@ workflows:
version: 2
build_all:
jobs:
- - build_emscripten
+ - build_emscripten: *build_on_tags
- test_emscripten_solcjs:
+ <<: *build_on_tags
requires:
- build_emscripten
- test_emscripten_external:
+ <<: *build_on_tags
requires:
- build_emscripten
- - build_x86
+ - build_x86: *build_on_tags
- test_x86:
+ <<: *build_on_tags
requires:
- build_x86
- - docs
+ - docs: *build_on_tags
diff --git a/docs/abi-spec.rst b/docs/abi-spec.rst
index 4d84a7da..98301fdc 100644
--- a/docs/abi-spec.rst
+++ b/docs/abi-spec.rst
@@ -54,7 +54,7 @@ The following elementary types exist:
- ``ufixed<M>x<N>``: unsigned variant of ``fixed<M>x<N>``.
-- ``fixed``, ``ufixed``: synonyms for ``fixed128x19``, ``ufixed128x19`` respectively. For computing the function selector, ``fixed128x19`` and ``ufixed128x19`` have to be used.
+- ``fixed``, ``ufixed``: synonyms for ``fixed128x18``, ``ufixed128x18`` respectively. For computing the function selector, ``fixed128x18`` and ``ufixed128x18`` have to be used.
- ``bytes<M>``: binary type of ``M`` bytes, ``0 < M <= 32``.
@@ -164,9 +164,9 @@ on the type of ``X`` being
- ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` for negative ``X`` and with zero bytes for positive ``X`` such that the length is 32 bytes.
- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false``
- ``fixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``.
-- ``fixed``: as in the ``fixed128x19`` case
+- ``fixed``: as in the ``fixed128x18`` case
- ``ufixed<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``uint256``.
-- ``ufixed``: as in the ``ufixed128x19`` case
+- ``ufixed``: as in the ``ufixed128x18`` case
- ``bytes<M>``: ``enc(X)`` is the sequence of bytes in ``X`` padded with trailing zero-bytes to a length of 32 bytes.
Note that for any ``X``, ``len(enc(X))`` is a multiple of 32.
diff --git a/docs/assembly.rst b/docs/assembly.rst
index cf9bf840..705cd1b8 100644
--- a/docs/assembly.rst
+++ b/docs/assembly.rst
@@ -647,6 +647,11 @@ Solidity manages memory in a very simple way: There is a "free memory pointer"
at position ``0x40`` in memory. If you want to allocate memory, just use the memory
from that point on and update the pointer accordingly.
+The first 64 bytes of memory can be used as "scratch space" for short-term
+allocation. The 32 bytes after the free memory pointer (i.e. starting at ``0x60``)
+is meant to be zero permanently and is used as the initial value for
+empty dynamic memory arrays.
+
Elements in memory arrays in Solidity always occupy multiples of 32 bytes (yes, this is
even true for ``byte[]``, but not for ``bytes`` and ``string``). Multi-dimensional memory
arrays are pointers to memory arrays. The length of a dynamic array is stored at the
diff --git a/docs/contracts.rst b/docs/contracts.rst
index 121c4de0..f8a44fb3 100644
--- a/docs/contracts.rst
+++ b/docs/contracts.rst
@@ -40,7 +40,7 @@ This means that cyclic creation dependencies are impossible.
::
- pragma solidity ^0.4.16;
+ pragma solidity >0.4.21;
contract OwnedToken {
// TokenCreator is a contract type that is defined below.
@@ -52,7 +52,7 @@ This means that cyclic creation dependencies are impossible.
// This is the constructor which registers the
// creator and the assigned name.
- function OwnedToken(bytes32 _name) public {
+ constructor(bytes32 _name) public {
// State variables are accessed via their name
// and not via e.g. this.owner. This also applies
// to functions and especially in the constructors,
@@ -679,7 +679,7 @@ candidate, resolution fails.
}
}
-Calling ``f(50)`` would create a type error since ``250`` can be implicitly converted both to ``uint8``
+Calling ``f(50)`` would create a type error since ``50`` can be implicitly converted both to ``uint8``
and ``uint256`` types. On another hand ``f(256)`` would resolve to ``f(uint256)`` overload as ``256`` cannot be implicitly
converted to ``uint8``.
@@ -803,7 +803,7 @@ as topics. The event call above can be performed in the same way as
}
where the long hexadecimal number is equal to
-``keccak256("Deposit(address,hash256,uint256)")``, the signature of the event.
+``keccak256("Deposit(address,bytes32,uint256)")``, the signature of the event.
Additional Resources for Understanding Events
==============================================
@@ -976,8 +976,31 @@ virtual method lookup.
Constructors
============
-A constructor is an optional function with the same name as the contract which is executed upon contract creation.
-Constructor functions can be either ``public`` or ``internal``.
+A constructor is an optional function declared with the ``constructor`` keyword which is executed upon contract creation.
+Constructor functions can be either ``public`` or ``internal``. If there is no constructor, the contract will assume the
+default constructor: ``contructor() public {}``.
+
+
+::
+
+ pragma solidity >0.4.21;
+
+ contract A {
+ uint public a;
+
+ constructor(uint _a) internal {
+ a = _a;
+ }
+ }
+
+ contract B is A(1) {
+ constructor() public {}
+ }
+
+A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
+
+.. note ::
+ Prior to version 0.4.22, constructors were defined as functions with the same name as the contract. This syntax is now deprecated.
::
@@ -995,7 +1018,6 @@ Constructor functions can be either ``public`` or ``internal``.
function B() public {}
}
-A constructor set as ``internal`` causes the contract to be marked as :ref:`abstract <abstract-contract>`.
.. index:: ! base;constructor
@@ -1009,11 +1031,11 @@ the base constructors. This can be done in two ways::
contract Base {
uint x;
- function Base(uint _x) public { x = _x; }
+ constructor(uint _x) public { x = _x; }
}
contract Derived is Base(7) {
- function Derived(uint _y) Base(_y * _y) public {
+ constructor(uint _y) Base(_y * _y) public {
}
}
diff --git a/docs/contributing.rst b/docs/contributing.rst
index 1bcaed7c..6717a8b9 100644
--- a/docs/contributing.rst
+++ b/docs/contributing.rst
@@ -55,8 +55,8 @@ However, if you are making a larger change, please consult with the `Solidity De
focused on compiler and language development instead of language use) first.
-Finally, please make sure you respect the `coding standards
-<https://raw.githubusercontent.com/ethereum/cpp-ethereum/develop/CodingStandards.txt>`_
+Finally, please make sure you respect the `coding style
+<https://raw.githubusercontent.com/ethereum/solidity/develop/CODING_STYLE.md>`_
for this project. Also, even though we do CI testing, please test your code and
ensure that it builds locally before submitting a pull request.
@@ -170,6 +170,57 @@ and re-run the test. It will now pass again:
Please choose a name for the contract file, that is self-explainatory in the sense of what is been tested, e.g. ``double_variable_declaration.sol``.
Do not put more than one contract into a single file. ``isoltest`` is currently not able to recognize them individually.
+
+Running the Fuzzer via AFL
+==========================
+
+Fuzzing is a technique that runs programs on more or less random inputs to find exceptional execution
+states (segmentation faults, exceptions, etc). Modern fuzzers are clever and do a directed search
+inside the input. We have a specialized binary called ``solfuzzer`` which takes source code as input
+and fails whenever it encounters an internal compiler error, segmentation fault or similar, but
+does not fail if e.g. the code contains an error. This way, internal problems in the compiler
+can be found by fuzzing tools.
+
+We mainly use `AFL <http://lcamtuf.coredump.cx/afl/>`_ for fuzzing. You need to download and
+build AFL manually. Next, build Solidity (or just the ``solfuzzer`` binary) with AFL as your compiler:
+
+::
+
+ cd build
+ # if needed
+ make clean
+ cmake .. -DCMAKE_C_COMPILER=path/to/afl-gcc -DCMAKE_CXX_COMPILER=path/to/afl-g++
+ make solfuzzer
+
+Next, you need some example source files. This will make it much easer for the fuzzer
+to find errors. You can either copy some files from the syntax tests or extract test files
+from the documentation or the other tests:
+
+::
+
+ mkdir /tmp/test_cases
+ cd /tmp/test_cases
+ # extract from tests:
+ path/to/solidity/scripts/isolate_tests.py path/to/solidity/test/libsolidity/SolidityEndToEndTest.cpp
+ # extract from documentation:
+ path/to/solidity/scripts/isolate_tests.py path/to/solidity/docs docs
+
+The AFL documentation states that the corpus (the initial input files) should not be
+too large. The files themselves should not be larger than 1 kB and there should be
+at most one input file per functionality, so better start with a small number of
+input files. There is also a tool called ``afl-cmin`` that can trim input files
+that result in similar behaviour of the binary.
+
+Now run the fuzzer (the ``-m`` extends the size of memory to 60 MB):
+
+::
+
+ afl-fuzz -m 60 -i /tmp/test_cases -o /tmp/fuzzer_reports -- /path/to/solfuzzer
+
+The fuzzer will create source files that lead to failures in ``/tmp/fuzzer_reports``.
+Often it finds many similar source files that produce the same error. You can
+use the tool ``scripts/uniqueErrors.sh`` to filter out the unique errors.
+
Whiskers
========
diff --git a/docs/installing-solidity.rst b/docs/installing-solidity.rst
index e26870f0..6726ded9 100644
--- a/docs/installing-solidity.rst
+++ b/docs/installing-solidity.rst
@@ -122,7 +122,6 @@ We will re-add the pre-built bottles soon.
brew upgrade
brew tap ethereum/ethereum
brew install solidity
- brew linkapps solidity
If you need a specific version of Solidity you can install a
Homebrew formula directly from Github.
diff --git a/docs/introduction-to-smart-contracts.rst b/docs/introduction-to-smart-contracts.rst
index 56f0fe3e..84b1fff8 100644
--- a/docs/introduction-to-smart-contracts.rst
+++ b/docs/introduction-to-smart-contracts.rst
@@ -326,7 +326,13 @@ EVM bytecode and executed. The output of this execution is
permanently stored as the code of the contract.
This means that in order to create a contract, you do not
send the actual code of the contract, but in fact code that
-returns that code.
+returns that code when executed.
+
+.. note::
+ While a contract is being created, its code is still empty.
+ Because of that, you should not call back into the
+ contract under construction until its constructor has
+ finished executing.
.. index:: ! gas, ! gas price
diff --git a/docs/miscellaneous.rst b/docs/miscellaneous.rst
index 01154854..20400aa2 100644
--- a/docs/miscellaneous.rst
+++ b/docs/miscellaneous.rst
@@ -64,12 +64,15 @@ The position of ``data[4][9].b`` is at ``keccak256(uint256(9) . keccak256(uint25
Layout in Memory
****************
-Solidity reserves three 256-bit slots:
+Solidity reserves four 32 byte slots:
-- 0 - 64: scratch space for hashing methods
-- 64 - 96: currently allocated memory size (aka. free memory pointer)
+- ``0x00`` - ``0x3f``: scratch space for hashing methods
+- ``0x40`` - ``0x5f``: currently allocated memory size (aka. free memory pointer)
+- ``0x60`` - ``0x7f``: zero slot
-Scratch space can be used between statements (ie. within inline assembly).
+Scratch space can be used between statements (ie. within inline assembly). The zero slot
+is used as initial value for dynamic memory arrays and should never be written to
+(the free memory pointer points to ``0x80`` initially).
Solidity always places new objects at the free memory pointer and memory is never freed (this might change in the future).
diff --git a/docs/types.rst b/docs/types.rst
index e704687e..5de6d07e 100644
--- a/docs/types.rst
+++ b/docs/types.rst
@@ -81,7 +81,7 @@ Fixed Point Numbers
``fixed`` / ``ufixed``: Signed and unsigned fixed point number of various sizes. Keywords ``ufixedMxN`` and ``fixedMxN``, where ``M`` represents the number of bits taken by
the type and ``N`` represents how many decimal points are available. ``M`` must be divisible by 8 and goes from 8 to 256 bits. ``N`` must be between 0 and 80, inclusive.
-``ufixed`` and ``fixed`` are aliases for ``ufixed128x19`` and ``fixed128x19``, respectively.
+``ufixed`` and ``fixed`` are aliases for ``ufixed128x18`` and ``fixed128x18``, respectively.
Operators:
diff --git a/libdevcore/Algorithms.h b/libdevcore/Algorithms.h
new file mode 100644
index 00000000..b2540668
--- /dev/null
+++ b/libdevcore/Algorithms.h
@@ -0,0 +1,76 @@
+/*
+ This file is part of solidity.
+
+ solidity is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ solidity is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with solidity. If not, see <http://www.gnu.org/licenses/>.
+*/
+#pragma once
+
+
+#include <functional>
+#include <set>
+
+namespace dev
+{
+
+/**
+ * Detector for cycles in directed graphs. It returns the first
+ * vertex on the path towards a cycle or a nullptr if there is
+ * no reachable cycle starting from a given vertex.
+ */
+template <typename V>
+class CycleDetector
+{
+public:
+ /// Initializes the cycle detector
+ /// @param _visit function that is given the current vertex
+ /// and is supposed to call @a run on all
+ /// adjacent vertices.
+ explicit CycleDetector(std::function<void(V const&, CycleDetector&)> _visit):
+ m_visit(std::move(_visit))
+ { }
+
+ /// Recursively perform cycle detection starting
+ /// (or continuing) with @param _vertex
+ /// @returns the first vertex on the path towards a cycle from @a _vertex
+ /// or nullptr if no cycle is reachable from @a _vertex.
+ V const* run(V const& _vertex)
+ {
+ if (m_firstCycleVertex)
+ return m_firstCycleVertex;
+ if (m_processed.count(&_vertex))
+ return nullptr;
+ else if (m_processing.count(&_vertex))
+ return m_firstCycleVertex = &_vertex;
+ m_processing.insert(&_vertex);
+
+ m_depth++;
+ m_visit(_vertex, *this);
+ m_depth--;
+ if (m_firstCycleVertex && m_depth == 1)
+ m_firstCycleVertex = &_vertex;
+
+ m_processing.erase(&_vertex);
+ m_processed.insert(&_vertex);
+ return m_firstCycleVertex;
+ }
+
+private:
+ std::function<void(V const&, CycleDetector&)> m_visit;
+ std::set<V const*> m_processing;
+ std::set<V const*> m_processed;
+ size_t m_depth = 0;
+ V const* m_firstCycleVertex = nullptr;
+};
+
+}
diff --git a/libevmasm/Assembly.cpp b/libevmasm/Assembly.cpp
index bd4ebf59..b71bc80c 100644
--- a/libevmasm/Assembly.cpp
+++ b/libevmasm/Assembly.cpp
@@ -438,13 +438,15 @@ map<u256, u256> Assembly::optimiseInternal(
// function types that can be stored in storage.
AssemblyItems optimisedItems;
+ bool usesMSize = (find(m_items.begin(), m_items.end(), AssemblyItem(Instruction::MSIZE)) != m_items.end());
+
auto iter = m_items.begin();
while (iter != m_items.end())
{
KnownState emptyState;
CommonSubexpressionEliminator eliminator(emptyState);
auto orig = iter;
- iter = eliminator.feedItems(iter, m_items.end());
+ iter = eliminator.feedItems(iter, m_items.end(), usesMSize);
bool shouldReplace = false;
AssemblyItems optimisedChunk;
try
diff --git a/libevmasm/CommonSubexpressionEliminator.h b/libevmasm/CommonSubexpressionEliminator.h
index 0b957a0e..b20de246 100644
--- a/libevmasm/CommonSubexpressionEliminator.h
+++ b/libevmasm/CommonSubexpressionEliminator.h
@@ -65,8 +65,9 @@ public:
/// Feeds AssemblyItems into the eliminator and @returns the iterator pointing at the first
/// item that must be fed into a new instance of the eliminator.
+ /// @param _msizeImportant if false, do not consider modification of MSIZE a side-effect
template <class _AssemblyItemIterator>
- _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end);
+ _AssemblyItemIterator feedItems(_AssemblyItemIterator _iterator, _AssemblyItemIterator _end, bool _msizeImportant);
/// @returns the resulting items after optimization.
AssemblyItems getOptimizedItems();
@@ -168,11 +169,12 @@ private:
template <class _AssemblyItemIterator>
_AssemblyItemIterator CommonSubexpressionEliminator::feedItems(
_AssemblyItemIterator _iterator,
- _AssemblyItemIterator _end
+ _AssemblyItemIterator _end,
+ bool _msizeImportant
)
{
assertThrow(!m_breakingItem, OptimizerException, "Invalid use of CommonSubexpressionEliminator.");
- for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator); ++_iterator)
+ for (; _iterator != _end && !SemanticInformation::breaksCSEAnalysisBlock(*_iterator, _msizeImportant); ++_iterator)
feedItem(*_iterator);
if (_iterator != _end)
m_breakingItem = &(*_iterator++);
diff --git a/libevmasm/Instruction.cpp b/libevmasm/Instruction.cpp
index a677a631..f9bbad2c 100644
--- a/libevmasm/Instruction.cpp
+++ b/libevmasm/Instruction.cpp
@@ -199,7 +199,7 @@ static const std::map<Instruction, InstructionInfo> c_instructionInfo =
{ 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 } },
- { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, false, Tier::Special } },
+ { Instruction::KECCAK256, { "KECCAK256", 0, 2, 1, true, Tier::Special } },
{ Instruction::ADDRESS, { "ADDRESS", 0, 0, 1, false, Tier::Base } },
{ Instruction::BALANCE, { "BALANCE", 0, 1, 1, false, Tier::Balance } },
{ Instruction::ORIGIN, { "ORIGIN", 0, 0, 1, false, Tier::Base } },
diff --git a/libevmasm/PeepholeOptimiser.cpp b/libevmasm/PeepholeOptimiser.cpp
index 168d1109..8a39de24 100644
--- a/libevmasm/PeepholeOptimiser.cpp
+++ b/libevmasm/PeepholeOptimiser.cpp
@@ -154,6 +154,51 @@ struct DoublePush: SimplePeepholeOptimizerMethod<DoublePush, 2>
}
};
+struct CommutativeSwap: SimplePeepholeOptimizerMethod<CommutativeSwap, 2>
+{
+ static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
+ {
+ // Remove SWAP1 if following instruction is commutative
+ if (
+ _swap.type() == Operation &&
+ _swap.instruction() == Instruction::SWAP1 &&
+ SemanticInformation::isCommutativeOperation(_op)
+ )
+ {
+ *_out = _op;
+ return true;
+ }
+ else
+ return false;
+ }
+};
+
+struct SwapComparison: SimplePeepholeOptimizerMethod<SwapComparison, 2>
+{
+ static bool applySimple(AssemblyItem const& _swap, AssemblyItem const& _op, std::back_insert_iterator<AssemblyItems> _out)
+ {
+ map<Instruction, Instruction> swappableOps{
+ { Instruction::LT, Instruction::GT },
+ { Instruction::GT, Instruction::LT },
+ { Instruction::SLT, Instruction::SGT },
+ { Instruction::SGT, Instruction::SLT }
+ };
+
+ if (
+ _swap.type() == Operation &&
+ _swap.instruction() == Instruction::SWAP1 &&
+ _op.type() == Operation &&
+ swappableOps.count(_op.instruction())
+ )
+ {
+ *_out = swappableOps.at(_op.instruction());
+ return true;
+ }
+ else
+ return false;
+ }
+};
+
struct JumpToNext: SimplePeepholeOptimizerMethod<JumpToNext, 3>
{
static size_t applySimple(
@@ -260,7 +305,7 @@ bool PeepholeOptimiser::optimise()
{
OptimiserState state {m_items, 0, std::back_inserter(m_optimisedItems)};
while (state.i < m_items.size())
- applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
+ applyMethods(state, PushPop(), OpPop(), DoublePush(), DoubleSwap(), CommutativeSwap(), SwapComparison(), JumpToNext(), UnreachableCode(), TagConjunctions(), Identity());
if (m_optimisedItems.size() < m_items.size() || (
m_optimisedItems.size() == m_items.size() && (
eth::bytesRequired(m_optimisedItems, 3) < eth::bytesRequired(m_items, 3) ||
diff --git a/libevmasm/SemanticInformation.cpp b/libevmasm/SemanticInformation.cpp
index 03870f7c..71267ee8 100644
--- a/libevmasm/SemanticInformation.cpp
+++ b/libevmasm/SemanticInformation.cpp
@@ -28,7 +28,7 @@ using namespace std;
using namespace dev;
using namespace dev::eth;
-bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
+bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant)
{
switch (_item.type())
{
@@ -59,6 +59,11 @@ bool SemanticInformation::breaksCSEAnalysisBlock(AssemblyItem const& _item)
return false;
if (_item.instruction() == Instruction::MSTORE)
return false;
+ if (!_msizeImportant && (
+ _item.instruction() == Instruction::MLOAD ||
+ _item.instruction() == Instruction::KECCAK256
+ ))
+ return false;
//@todo: We do not handle the following memory instructions for now:
// calldatacopy, codecopy, extcodecopy, mstore8,
// msize (note that msize also depends on memory read access)
diff --git a/libevmasm/SemanticInformation.h b/libevmasm/SemanticInformation.h
index 83656252..8bdc70be 100644
--- a/libevmasm/SemanticInformation.h
+++ b/libevmasm/SemanticInformation.h
@@ -38,7 +38,8 @@ class AssemblyItem;
struct SemanticInformation
{
/// @returns true if the given items starts a new block for common subexpression analysis.
- static bool breaksCSEAnalysisBlock(AssemblyItem const& _item);
+ /// @param _msizeImportant if false, consider an operation non-breaking if its only side-effect is that it modifies msize.
+ static bool breaksCSEAnalysisBlock(AssemblyItem const& _item, bool _msizeImportant);
/// @returns true if the item is a two-argument operation whose value does not depend on the
/// order of its arguments.
static bool isCommutativeOperation(AssemblyItem const& _item);
diff --git a/libsolidity/analysis/PostTypeChecker.cpp b/libsolidity/analysis/PostTypeChecker.cpp
index fbc72e52..19d0b708 100644
--- a/libsolidity/analysis/PostTypeChecker.cpp
+++ b/libsolidity/analysis/PostTypeChecker.cpp
@@ -21,6 +21,8 @@
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
+#include <libdevcore/Algorithms.h>
+
#include <boost/range/adaptor/map.hpp>
#include <memory>
@@ -47,7 +49,7 @@ void PostTypeChecker::endVisit(ContractDefinition const&)
{
solAssert(!m_currentConstVariable, "");
for (auto declaration: m_constVariables)
- if (auto identifier = findCycle(declaration))
+ if (auto identifier = findCycle(*declaration))
m_errorReporter.typeError(
declaration->location(),
"The value of the constant " + declaration->name() +
@@ -87,20 +89,24 @@ bool PostTypeChecker::visit(Identifier const& _identifier)
return true;
}
-VariableDeclaration const* PostTypeChecker::findCycle(
- VariableDeclaration const* _startingFrom,
- set<VariableDeclaration const*> const& _seen
-)
+VariableDeclaration const* PostTypeChecker::findCycle(VariableDeclaration const& _startingFrom)
{
- if (_seen.count(_startingFrom))
- return _startingFrom;
- else if (m_constVariableDependencies.count(_startingFrom))
+ auto visitor = [&](VariableDeclaration const& _variable, CycleDetector<VariableDeclaration>& _cycleDetector)
{
- set<VariableDeclaration const*> seen(_seen);
- seen.insert(_startingFrom);
- for (auto v: m_constVariableDependencies[_startingFrom])
- if (findCycle(v, seen))
- return v;
- }
- return nullptr;
+ // Iterating through the dependencies needs to be deterministic and thus cannot
+ // depend on the memory layout.
+ // Because of that, we sort by AST node id.
+ vector<VariableDeclaration const*> dependencies(
+ m_constVariableDependencies[&_variable].begin(),
+ m_constVariableDependencies[&_variable].end()
+ );
+ sort(dependencies.begin(), dependencies.end(), [](VariableDeclaration const* _a, VariableDeclaration const* _b) -> bool
+ {
+ return _a->id() < _b->id();
+ });
+ for (auto v: dependencies)
+ if (_cycleDetector.run(*v))
+ return;
+ };
+ return CycleDetector<VariableDeclaration>(visitor).run(_startingFrom);
}
diff --git a/libsolidity/analysis/PostTypeChecker.h b/libsolidity/analysis/PostTypeChecker.h
index bafc1ae6..4f9dac6e 100644
--- a/libsolidity/analysis/PostTypeChecker.h
+++ b/libsolidity/analysis/PostTypeChecker.h
@@ -55,10 +55,7 @@ private:
virtual bool visit(Identifier const& _identifier) override;
- VariableDeclaration const* findCycle(
- VariableDeclaration const* _startingFrom,
- std::set<VariableDeclaration const*> const& _seen = std::set<VariableDeclaration const*>{}
- );
+ VariableDeclaration const* findCycle(VariableDeclaration const& _startingFrom);
ErrorReporter& m_errorReporter;
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 6aee260e..d96f8748 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -50,6 +50,16 @@ void StaticAnalyzer::endVisit(ContractDefinition const&)
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
{
+ const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface;
+
+ if (_function.noVisibilitySpecified())
+ m_errorReporter.warning(
+ _function.location(),
+ "No visibility specified. Defaulting to \"" +
+ Declaration::visibilityToString(_function.visibility()) +
+ "\". " +
+ (isInterface ? "In interfaces it defaults to external." : "")
+ );
if (_function.isImplemented())
m_currentFunction = &_function;
else
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index 3a32810b..343b4ba8 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -214,16 +214,22 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
{
bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
- if (_function.noVisibilitySpecified())
+ if (v050 && _function.noVisibilitySpecified())
+ m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
+
+ if (_function.isOldStyleConstructor())
{
if (v050)
- m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
+ m_errorReporter.syntaxError(
+ _function.location(),
+ "Functions are not allowed to have the same name as the contract. "
+ "If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
+ );
else
m_errorReporter.warning(
_function.location(),
- "No visibility specified. Defaulting to \"" +
- Declaration::visibilityToString(_function.visibility()) +
- "\"."
+ "Defining constructors as functions with the same name as the contract is deprecated. "
+ "Use \"constructor(...) { ... }\" instead."
);
}
return true;
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 620dfca4..a252742d 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -320,7 +320,7 @@ void TypeChecker::checkContractAbstractConstructors(ContractDefinition const& _c
{
auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
solAssert(baseContract, "");
- if (!base->arguments().empty())
+ if (base->arguments() && !base->arguments()->empty())
argumentsNeeded.erase(baseContract);
}
}
@@ -506,30 +506,46 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
// Interfaces do not have constructors, so there are zero parameters.
parameterTypes = ContractType(*base).newExpressionType()->parameterTypes();
- if (!arguments.empty() && parameterTypes.size() != arguments.size())
+ if (arguments)
{
- m_errorReporter.typeError(
- _inheritance.location(),
- "Wrong argument count for constructor call: " +
- toString(arguments.size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- "."
- );
- return;
- }
+ bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
- for (size_t i = 0; i < arguments.size(); ++i)
- if (!type(*arguments[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
- m_errorReporter.typeError(
- arguments[i]->location(),
- "Invalid type for argument in constructor call. "
- "Invalid implicit conversion from " +
- type(*arguments[i])->toString() +
- " to " +
- parameterTypes[i]->toString() +
- " requested."
- );
+ if (parameterTypes.size() != arguments->size())
+ {
+ if (arguments->size() == 0 && !v050)
+ m_errorReporter.warning(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments->size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+ else
+ {
+ m_errorReporter.typeError(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments->size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ "."
+ );
+ return;
+ }
+ }
+ for (size_t i = 0; i < arguments->size(); ++i)
+ if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
+ m_errorReporter.typeError(
+ (*arguments)[i]->location(),
+ "Invalid type for argument in constructor call. "
+ "Invalid implicit conversion from " +
+ type(*(*arguments)[i])->toString() +
+ " to " +
+ parameterTypes[i]->toString() +
+ " requested."
+ );
+ }
}
void TypeChecker::endVisit(UsingForDirective const& _usingFor)
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index 9c67d354..bc85349b 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -425,19 +425,22 @@ public:
InheritanceSpecifier(
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _baseName,
- std::vector<ASTPointer<Expression>> _arguments
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
):
- ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
+ ASTNode(_location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {}
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
UserDefinedTypeName const& name() const { return *m_baseName; }
- std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
+ // Returns nullptr if no argument list was given (``C``).
+ // If an argument list is given (``C(...)``), the arguments are returned
+ // as a vector of expressions. Note that this vector can be empty (``C()``).
+ std::vector<ASTPointer<Expression>> const* arguments() const { return m_arguments.get(); }
private:
ASTPointer<UserDefinedTypeName> m_baseName;
- std::vector<ASTPointer<Expression>> m_arguments;
+ std::unique_ptr<std::vector<ASTPointer<Expression>>> m_arguments;
};
/**
@@ -607,7 +610,8 @@ public:
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
- bool isFallback() const { return name().empty(); }
+ bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); }
+ bool isFallback() const { return !m_isConstructor && name().empty(); }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& returnParameters() const { return m_returnParameters->parameters(); }
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index 4fef67c3..94932eca 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -268,7 +268,7 @@ bool ASTJsonConverter::visit(InheritanceSpecifier const& _node)
{
setJsonNode(_node, "InheritanceSpecifier", {
make_pair("baseName", toJson(_node.name())),
- make_pair("arguments", toJson(_node.arguments()))
+ make_pair("arguments", _node.arguments() ? toJson(*_node.arguments()) : Json::Value(Json::arrayValue))
});
return false;
}
diff --git a/libsolidity/ast/AST_accept.h b/libsolidity/ast/AST_accept.h
index 70ee997e..dac414fc 100644
--- a/libsolidity/ast/AST_accept.h
+++ b/libsolidity/ast/AST_accept.h
@@ -94,7 +94,8 @@ void InheritanceSpecifier::accept(ASTVisitor& _visitor)
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
@@ -104,7 +105,8 @@ void InheritanceSpecifier::accept(ASTConstVisitor& _visitor) const
if (_visitor.visit(*this))
{
m_baseName->accept(_visitor);
- listAccept(m_arguments, _visitor);
+ if (m_arguments)
+ listAccept(*m_arguments, _visitor);
}
_visitor.endVisit(*this);
}
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 4c462d09..ac1d3b01 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -28,6 +28,7 @@
#include <libdevcore/CommonData.h>
#include <libdevcore/SHA3.h>
#include <libdevcore/UTF8.h>
+#include <libdevcore/Algorithms.h>
#include <boost/algorithm/string/join.hpp>
#include <boost/algorithm/string/replace.hpp>
@@ -208,9 +209,9 @@ TypePointer Type::fromElementaryTypeName(ElementaryTypeNameToken const& _type)
case Token::UInt:
return make_shared<IntegerType>(256, IntegerType::Modifier::Unsigned);
case Token::Fixed:
- return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed);
+ return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed);
case Token::UFixed:
- return make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned);
+ return make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned);
case Token::Byte:
return make_shared<FixedBytesType>(1);
case Token::Address:
@@ -1971,25 +1972,19 @@ bool StructType::recursive() const
{
if (!m_recursive.is_initialized())
{
- set<StructDefinition const*> structsSeen;
- function<bool(StructType const*)> check = [&](StructType const* t) -> bool
+ auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector)
{
- StructDefinition const* str = &t->structDefinition();
- if (structsSeen.count(str))
- return true;
- structsSeen.insert(str);
- for (ASTPointer<VariableDeclaration> const& variable: str->members())
+ for (ASTPointer<VariableDeclaration> const& variable: _struct.members())
{
Type const* memberType = variable->annotation().type.get();
while (dynamic_cast<ArrayType const*>(memberType))
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType().get();
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
- if (check(innerStruct))
- return true;
+ if (_cycleDetector.run(innerStruct->structDefinition()))
+ return;
}
- return false;
};
- m_recursive = check(this);
+ m_recursive = (CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr);
}
return *m_recursive;
}
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index b7e64891..2c392705 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -229,6 +229,9 @@ public:
/// i.e. it behaves differently in lvalue context and in value context.
virtual bool isValueType() const { return false; }
virtual unsigned sizeOnStack() const { return 1; }
+ /// If it is possible to initialize such a value in memory by just writing zeros
+ /// of the size memoryHeadSize().
+ virtual bool hasSimpleZeroValueInMemory() const { return true; }
/// @returns the mobile (in contrast to static) type corresponding to the given type.
/// This returns the corresponding IntegerType or FixedPointType for RationalNumberType
/// and the pointer type for storage reference types.
@@ -568,6 +571,7 @@ public:
virtual TypePointer mobileType() const override { return copyForLocation(m_location, true); }
virtual bool dataStoredIn(DataLocation _location) const override { return m_location == _location; }
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
/// Storage references can be pointers or bound references. In general, local variables are of
/// pointer type, state variables are bound references. Assignments to pointers or deleting
@@ -855,6 +859,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
virtual TypePointer mobileType() const override;
/// Converts components to their temporary types and performs some wildcard matching.
virtual TypePointer closestTemporaryType(TypePointer const& _targetType) const override;
@@ -999,6 +1004,7 @@ public:
virtual bool isValueType() const override { return true; }
virtual bool canLiveOutsideStorage() const override { return m_kind == Kind::Internal || m_kind == Kind::External; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { return false; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
virtual TypePointer encodingType() const override;
virtual TypePointer interfaceType(bool _inLibrary) const override;
@@ -1104,6 +1110,8 @@ public:
return _inLibrary ? shared_from_this() : TypePointer();
}
virtual bool dataStoredIn(DataLocation _location) const override { return _location == DataLocation::Storage; }
+ /// Cannot be stored in memory, but just in case.
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
TypePointer const& keyType() const { return m_keyType; }
TypePointer const& valueType() const { return m_valueType; }
@@ -1132,6 +1140,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override;
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string toString(bool _short) const override { return "type(" + m_actualType->toString(_short) + ")"; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const* _currentScope) const override;
@@ -1154,6 +1163,7 @@ public:
virtual u256 storageSize() const override;
virtual bool canLiveOutsideStorage() const override { return false; }
virtual unsigned sizeOnStack() const override { return 0; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string richIdentifier() const override;
virtual bool operator==(Type const& _other) const override;
virtual std::string toString(bool _short) const override;
@@ -1179,6 +1189,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
@@ -1209,6 +1220,7 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual bool canBeStored() const override { return false; }
virtual bool canLiveOutsideStorage() const override { return true; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual unsigned sizeOnStack() const override { return 0; }
virtual MemberList::MemberMap nativeMembers(ContractDefinition const*) const override;
@@ -1238,6 +1250,7 @@ public:
virtual bool canLiveOutsideStorage() const override { return false; }
virtual bool isValueType() const override { return true; }
virtual unsigned sizeOnStack() const override { return 1; }
+ virtual bool hasSimpleZeroValueInMemory() const override { solAssert(false, ""); }
virtual std::string toString(bool) const override { return "inaccessible dynamic type"; }
virtual TypePointer decodingType() const override { return std::make_shared<IntegerType>(256); }
};
diff --git a/libsolidity/codegen/ArrayUtils.cpp b/libsolidity/codegen/ArrayUtils.cpp
index 4703fc1f..0fe66d2d 100644
--- a/libsolidity/codegen/ArrayUtils.cpp
+++ b/libsolidity/codegen/ArrayUtils.cpp
@@ -774,6 +774,55 @@ void ArrayUtils::resizeDynamicArray(ArrayType const& _typeIn) const
);
}
+void ArrayUtils::incrementDynamicArraySize(ArrayType const& _type) const
+{
+ solAssert(_type.location() == DataLocation::Storage, "");
+ solAssert(_type.isDynamicallySized(), "");
+ if (!_type.isByteArray() && _type.baseType()->storageBytes() < 32)
+ solAssert(_type.baseType()->isValueType(), "Invalid storage size for non-value type.");
+
+ if (_type.isByteArray())
+ {
+ // We almost always just add 2 (length of byte arrays is shifted left by one)
+ // except for the case where we transition from a short byte array
+ // to a long byte array, there we have to copy.
+ // This happens if the length is exactly 31, which means that the
+ // lowest-order byte (we actually use a mask with fewer bits) must
+ // be (31*2+0) = 62
+
+ m_context.appendInlineAssembly(R"({
+ let data := sload(ref)
+ let shifted_length := and(data, 63)
+ // We have to copy if length is exactly 31, because that marks
+ // the transition between in-place and out-of-place storage.
+ switch shifted_length
+ case 62
+ {
+ mstore(0, ref)
+ let data_area := keccak256(0, 0x20)
+ sstore(data_area, and(data, not(0xff)))
+ // New length is 32, encoded as (32 * 2 + 1)
+ sstore(ref, 65)
+ // Replace ref variable by new length
+ ref := 32
+ }
+ default
+ {
+ sstore(ref, add(data, 2))
+ // Replace ref variable by new length
+ if iszero(and(data, 1)) { data := shifted_length }
+ ref := add(div(data, 2), 1)
+ }
+ })", {"ref"});
+ }
+ else
+ m_context.appendInlineAssembly(R"({
+ let new_length := add(sload(ref), 1)
+ sstore(ref, new_length)
+ ref := new_length
+ })", {"ref"});
+}
+
void ArrayUtils::clearStorageLoop(TypePointer const& _type) const
{
m_context.callLowLevelFunction(
diff --git a/libsolidity/codegen/ArrayUtils.h b/libsolidity/codegen/ArrayUtils.h
index f3ddc4ee..99786397 100644
--- a/libsolidity/codegen/ArrayUtils.h
+++ b/libsolidity/codegen/ArrayUtils.h
@@ -67,6 +67,12 @@ public:
/// Stack pre: reference (excludes byte offset) new_length
/// Stack post:
void resizeDynamicArray(ArrayType const& _type) const;
+ /// Increments the size of a dynamic array by one.
+ /// Does not touch the new data element. In case of a byte array, this might move the
+ /// data.
+ /// Stack pre: reference (excludes byte offset)
+ /// Stack post: new_length
+ void incrementDynamicArraySize(ArrayType const& _type) const;
/// Appends a loop that clears a sequence of storage slots of the given type (excluding end).
/// Stack pre: end_ref start_ref
/// Stack post: end_ref
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 676d5d4e..79aef7b0 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -21,6 +21,7 @@
*/
#include <libsolidity/codegen/CompilerUtils.h>
+
#include <libsolidity/ast/AST.h>
#include <libsolidity/codegen/ArrayUtils.h>
#include <libsolidity/codegen/LValue.h>
@@ -39,11 +40,17 @@ namespace solidity
const unsigned CompilerUtils::dataStartOffset = 4;
const size_t CompilerUtils::freeMemoryPointer = 64;
+const size_t CompilerUtils::zeroPointer = CompilerUtils::freeMemoryPointer + 32;
+const size_t CompilerUtils::generalPurposeMemoryStart = CompilerUtils::zeroPointer + 32;
const unsigned CompilerUtils::identityContractAddress = 4;
+static_assert(CompilerUtils::freeMemoryPointer >= 64, "Free memory pointer must not overlap with scratch area.");
+static_assert(CompilerUtils::zeroPointer >= CompilerUtils::freeMemoryPointer + 32, "Zero pointer must not overlap with free memory pointer.");
+static_assert(CompilerUtils::generalPurposeMemoryStart >= CompilerUtils::zeroPointer + 32, "General purpose memory must not overlap with zero area.");
+
void CompilerUtils::initialiseFreeMemoryPointer()
{
- m_context << u256(freeMemoryPointer + 32);
+ m_context << u256(generalPurposeMemoryStart);
storeFreeMemoryPointer();
}
@@ -495,14 +502,34 @@ void CompilerUtils::abiDecodeV2(TypePointers const& _parameterTypes, bool _fromM
void CompilerUtils::zeroInitialiseMemoryArray(ArrayType const& _type)
{
- auto repeat = m_context.newTag();
- m_context << repeat;
- pushZeroValue(*_type.baseType());
- storeInMemoryDynamic(*_type.baseType());
- m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1;
- m_context << Instruction::SUB << Instruction::SWAP1;
- m_context << Instruction::DUP2;
- m_context.appendConditionalJumpTo(repeat);
+ if (_type.baseType()->hasSimpleZeroValueInMemory())
+ {
+ solAssert(_type.baseType()->isValueType(), "");
+ Whiskers templ(R"({
+ let size := mul(length, <element_size>)
+ // cheap way of zero-initializing a memory range
+ codecopy(memptr, codesize(), size)
+ memptr := add(memptr, size)
+ })");
+ templ("element_size", to_string(_type.baseType()->memoryHeadSize()));
+ m_context.appendInlineAssembly(templ.render(), {"length", "memptr"});
+ }
+ else
+ {
+ // TODO: Potential optimization:
+ // When we create a new multi-dimensional dynamic array, each element
+ // is initialized to an empty array. It actually does not hurt
+ // to re-use exactly the same empty array for all elements. Currently,
+ // a new one is created each time.
+ auto repeat = m_context.newTag();
+ m_context << repeat;
+ pushZeroValue(*_type.baseType());
+ storeInMemoryDynamic(*_type.baseType());
+ m_context << Instruction::SWAP1 << u256(1) << Instruction::SWAP1;
+ m_context << Instruction::SUB << Instruction::SWAP1;
+ m_context << Instruction::DUP2;
+ m_context.appendConditionalJumpTo(repeat);
+ }
m_context << Instruction::SWAP1 << Instruction::POP;
}
@@ -1031,6 +1058,13 @@ void CompilerUtils::pushZeroValue(Type const& _type)
return;
}
solAssert(referenceType->location() == DataLocation::Memory, "");
+ if (auto arrayType = dynamic_cast<ArrayType const*>(&_type))
+ if (arrayType->isDynamicallySized())
+ {
+ // Push a memory location that is (hopefully) always zero.
+ pushZeroPointer();
+ return;
+ }
TypePointer type = _type.shared_from_this();
m_context.callLowLevelFunction(
@@ -1051,13 +1085,8 @@ void CompilerUtils::pushZeroValue(Type const& _type)
}
else if (auto arrayType = dynamic_cast<ArrayType const*>(type.get()))
{
- if (arrayType->isDynamicallySized())
- {
- // zero length
- _context << u256(0);
- utils.storeInMemoryDynamic(IntegerType(256));
- }
- else if (arrayType->length() > 0)
+ solAssert(!arrayType->isDynamicallySized(), "");
+ if (arrayType->length() > 0)
{
_context << arrayType->length() << Instruction::SWAP1;
// stack: items_to_do memory_pos
@@ -1074,6 +1103,11 @@ void CompilerUtils::pushZeroValue(Type const& _type)
);
}
+void CompilerUtils::pushZeroPointer()
+{
+ m_context << u256(zeroPointer);
+}
+
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
{
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 389673ef..a32c5c6e 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -210,6 +210,9 @@ public:
/// Creates a zero-value for the given type and puts it onto the stack. This might allocate
/// memory for memory references.
void pushZeroValue(Type const& _type);
+ /// Pushes a pointer to the stack that points to a (potentially shared) location in memory
+ /// that always contains a zero. It is not allowed to write there.
+ void pushZeroPointer();
/// Moves the value that is at the top of the stack to a stack variable.
void moveToStackVariable(VariableDeclaration const& _variable);
@@ -255,6 +258,10 @@ public:
/// Position of the free-memory-pointer in memory;
static const size_t freeMemoryPointer;
+ /// Position of the memory slot that is always zero.
+ static const size_t zeroPointer;
+ /// Starting offset for memory available to the user (aka the contract).
+ static const size_t generalPurposeMemoryStart;
private:
/// Address of the precompiled identity contract.
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 791edc65..d3a7e4ea 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -143,8 +143,9 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
for (auto const& modifier: constructor->modifiers())
{
auto baseContract = dynamic_cast<ContractDefinition const*>(
- modifier->name()->annotation().referencedDeclaration);
- if (baseContract)
+ modifier->name()->annotation().referencedDeclaration
+ );
+ if (baseContract && !modifier->arguments().empty())
if (m_baseArguments.count(baseContract->constructor()) == 0)
m_baseArguments[baseContract->constructor()] = &modifier->arguments();
}
@@ -156,8 +157,8 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
);
solAssert(baseContract, "");
- if (m_baseArguments.count(baseContract->constructor()) == 0)
- m_baseArguments[baseContract->constructor()] = &base->arguments();
+ if (!m_baseArguments.count(baseContract->constructor()) && base->arguments() && !base->arguments()->empty())
+ m_baseArguments[baseContract->constructor()] = base->arguments();
}
}
// Initialization of state variables in base-to-derived order.
@@ -238,6 +239,7 @@ void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _construc
solAssert(m_baseArguments.count(&_constructor), "");
std::vector<ASTPointer<Expression>> const* arguments = m_baseArguments[&_constructor];
solAssert(arguments, "");
+ solAssert(arguments->size() == constructorType.parameterTypes().size(), "");
for (unsigned i = 0; i < arguments->size(); ++i)
compileExpression(*(arguments->at(i)), constructorType.parameterTypes()[i]);
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 9e2d30d5..57d49ac6 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -821,24 +821,27 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
function.kind() == FunctionType::Kind::ArrayPush ?
make_shared<ArrayType>(DataLocation::Storage, paramType) :
make_shared<ArrayType>(DataLocation::Storage);
- // get the current length
- ArrayUtils(m_context).retrieveLength(*arrayType);
- m_context << Instruction::DUP1;
- // stack: ArrayReference currentLength currentLength
- m_context << u256(1) << Instruction::ADD;
- // stack: ArrayReference currentLength newLength
- m_context << Instruction::DUP3 << Instruction::DUP2;
- ArrayUtils(m_context).resizeDynamicArray(*arrayType);
- m_context << Instruction::SWAP2 << Instruction::SWAP1;
- // stack: newLength ArrayReference oldLength
- ArrayUtils(m_context).accessIndex(*arrayType, false);
- // stack: newLength storageSlot slotOffset
+ // stack: ArrayReference
arguments[0]->accept(*this);
+ TypePointer const& argType = arguments[0]->annotation().type;
+ // stack: ArrayReference argValue
+ utils().moveToStackTop(argType->sizeOnStack(), 1);
+ // stack: argValue ArrayReference
+ m_context << Instruction::DUP1;
+ ArrayUtils(m_context).incrementDynamicArraySize(*arrayType);
+ // stack: argValue ArrayReference newLength
+ m_context << Instruction::SWAP1;
+ // stack: argValue newLength ArrayReference
+ m_context << u256(1) << Instruction::DUP3 << Instruction::SUB;
+ // stack: argValue newLength ArrayReference (newLength-1)
+ ArrayUtils(m_context).accessIndex(*arrayType, false);
+ // stack: argValue newLength storageSlot slotOffset
+ utils().moveToStackTop(3, argType->sizeOnStack());
// stack: newLength storageSlot slotOffset argValue
TypePointer type = arguments[0]->annotation().type->closestTemporaryType(arrayType->baseType());
solAssert(type, "");
- utils().convertType(*arguments[0]->annotation().type, *type);
+ utils().convertType(*argType, *type);
utils().moveToStackTop(1 + type->sizeOnStack());
utils().moveToStackTop(1 + type->sizeOnStack());
// stack: newLength argValue storageSlot slotOffset
@@ -850,8 +853,6 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
}
case FunctionType::Kind::ObjectCreation:
{
- // Will allocate at the end of memory (MSIZE) and not write at all unless the base
- // type is dynamically sized.
ArrayType const& arrayType = dynamic_cast<ArrayType const&>(*_functionCall.annotation().type);
_functionCall.expression().accept(*this);
solAssert(arguments.size() == 1, "");
@@ -861,15 +862,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
utils().convertType(*arguments[0]->annotation().type, IntegerType(256));
// Stack: requested_length
- // Allocate at max(MSIZE, freeMemoryPointer)
utils().fetchFreeMemoryPointer();
- m_context << Instruction::DUP1 << Instruction::MSIZE;
- m_context << Instruction::LT;
- auto initialise = m_context.appendConditionalJump();
- // Free memory pointer does not point to empty memory, use MSIZE.
- m_context << Instruction::POP;
- m_context << Instruction::MSIZE;
- m_context << initialise;
// Stack: requested_length memptr
m_context << Instruction::SWAP1;
@@ -894,13 +887,10 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
// Check if length is zero
m_context << Instruction::DUP1 << Instruction::ISZERO;
auto skipInit = m_context.appendConditionalJump();
-
- // We only have to initialise if the base type is a not a value type.
- if (dynamic_cast<ReferenceType const*>(arrayType.baseType().get()))
- {
- m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
- utils().zeroInitialiseMemoryArray(arrayType);
- }
+ // Always initialize because the free memory pointer might point at
+ // a dirty memory area.
+ m_context << Instruction::DUP2 << u256(32) << Instruction::ADD;
+ utils().zeroInitialiseMemoryArray(arrayType);
m_context << skipInit;
m_context << Instruction::POP;
break;
diff --git a/libsolidity/formal/SymbolicBoolVariable.cpp b/libsolidity/formal/SymbolicBoolVariable.cpp
index e5c56e46..5cf22d7d 100644
--- a/libsolidity/formal/SymbolicBoolVariable.cpp
+++ b/libsolidity/formal/SymbolicBoolVariable.cpp
@@ -30,7 +30,11 @@ SymbolicBoolVariable::SymbolicBoolVariable(
SymbolicVariable(_decl, _interface)
{
solAssert(m_declaration.type()->category() == Type::Category::Bool, "");
- m_expression = make_shared<smt::Expression>(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Bool));
+}
+
+smt::Expression SymbolicBoolVariable::valueAtSequence(int _seq) const
+{
+ return m_interface.newBool(uniqueSymbol(_seq));
}
void SymbolicBoolVariable::setZeroValue(int _seq)
diff --git a/libsolidity/formal/SymbolicBoolVariable.h b/libsolidity/formal/SymbolicBoolVariable.h
index 3510b770..678f97d9 100644
--- a/libsolidity/formal/SymbolicBoolVariable.h
+++ b/libsolidity/formal/SymbolicBoolVariable.h
@@ -41,6 +41,9 @@ public:
void setZeroValue(int _seq);
/// Does nothing since the SMT solver already knows the valid values.
void setUnknownValue(int _seq);
+
+protected:
+ smt::Expression valueAtSequence(int _seq) const;
};
}
diff --git a/libsolidity/formal/SymbolicIntVariable.cpp b/libsolidity/formal/SymbolicIntVariable.cpp
index eb7b1c17..5e71fdcc 100644
--- a/libsolidity/formal/SymbolicIntVariable.cpp
+++ b/libsolidity/formal/SymbolicIntVariable.cpp
@@ -30,7 +30,11 @@ SymbolicIntVariable::SymbolicIntVariable(
SymbolicVariable(_decl, _interface)
{
solAssert(m_declaration.type()->category() == Type::Category::Integer, "");
- m_expression = make_shared<smt::Expression>(m_interface.newFunction(uniqueSymbol(), smt::Sort::Int, smt::Sort::Int));
+}
+
+smt::Expression SymbolicIntVariable::valueAtSequence(int _seq) const
+{
+ return m_interface.newInteger(uniqueSymbol(_seq));
}
void SymbolicIntVariable::setZeroValue(int _seq)
diff --git a/libsolidity/formal/SymbolicIntVariable.h b/libsolidity/formal/SymbolicIntVariable.h
index eb36b899..d591e8db 100644
--- a/libsolidity/formal/SymbolicIntVariable.h
+++ b/libsolidity/formal/SymbolicIntVariable.h
@@ -44,6 +44,9 @@ public:
static smt::Expression minValue(IntegerType const& _t);
static smt::Expression maxValue(IntegerType const& _t);
+
+protected:
+ smt::Expression valueAtSequence(int _seq) const;
};
}
diff --git a/libsolidity/formal/SymbolicVariable.cpp b/libsolidity/formal/SymbolicVariable.cpp
index d59b55b1..caefa3a3 100644
--- a/libsolidity/formal/SymbolicVariable.cpp
+++ b/libsolidity/formal/SymbolicVariable.cpp
@@ -32,9 +32,9 @@ SymbolicVariable::SymbolicVariable(
{
}
-string SymbolicVariable::uniqueSymbol() const
+string SymbolicVariable::uniqueSymbol(int _seq) const
{
- return m_declaration.name() + "_" + to_string(m_declaration.id());
+ return m_declaration.name() + "_" + to_string(m_declaration.id()) + "_" + to_string(_seq);
}
diff --git a/libsolidity/formal/SymbolicVariable.h b/libsolidity/formal/SymbolicVariable.h
index 75eb9fa5..e4e4ea8d 100644
--- a/libsolidity/formal/SymbolicVariable.h
+++ b/libsolidity/formal/SymbolicVariable.h
@@ -46,7 +46,7 @@ public:
return valueAtSequence(_seq);
}
- std::string uniqueSymbol() const;
+ std::string uniqueSymbol(int _seq) const;
/// Sets the var to the default value of its type.
virtual void setZeroValue(int _seq) = 0;
@@ -55,13 +55,9 @@ public:
virtual void setUnknownValue(int _seq) = 0;
protected:
- smt::Expression valueAtSequence(int _seq) const
- {
- return (*m_expression)(_seq);
- }
+ virtual smt::Expression valueAtSequence(int _seq) const = 0;
Declaration const& m_declaration;
- std::shared_ptr<smt::Expression> m_expression = nullptr;
smt::SolverInterface& m_interface;
};
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 8c97f55f..9a7731d8 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -238,7 +238,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp
Token::Value currentTokenValue = m_scanner->currentToken();
if (currentTokenValue == Token::RBrace)
break;
- else if (currentTokenValue == Token::Function)
+ else if (
+ currentTokenValue == Token::Function ||
+ (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor")
+ )
// This can be a function or a state variable of function type (especially
// complicated to distinguish fallback function from function type state variable)
subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get()));
@@ -283,17 +286,17 @@ ASTPointer<InheritanceSpecifier> Parser::parseInheritanceSpecifier()
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
ASTPointer<UserDefinedTypeName> name(parseUserDefinedTypeName());
- vector<ASTPointer<Expression>> arguments;
+ unique_ptr<vector<ASTPointer<Expression>>> arguments;
if (m_scanner->currentToken() == Token::LParen)
{
m_scanner->next();
- arguments = parseFunctionCallListArguments();
+ arguments.reset(new vector<ASTPointer<Expression>>(parseFunctionCallListArguments()));
nodeFactory.markEndPosition();
expectToken(Token::RParen);
}
else
nodeFactory.setEndPositionFromNode(name);
- return nodeFactory.createNode<InheritanceSpecifier>(name, arguments);
+ return nodeFactory.createNode<InheritanceSpecifier>(name, std::move(arguments));
}
Declaration::Visibility Parser::parseVisibilitySpecifier(Token::Value _token)
@@ -329,15 +332,31 @@ StateMutability Parser::parseStateMutability(Token::Value _token)
return stateMutability;
}
-Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
+Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(
+ bool _forceEmptyName,
+ bool _allowModifiers,
+ ASTString const* _contractName
+)
{
RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
- expectToken(Token::Function);
- if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
- result.name = make_shared<ASTString>(); // anonymous function
+
+ result.isConstructor = false;
+
+ if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor")
+ result.isConstructor = true;
+ else if (m_scanner->currentToken() != Token::Function)
+ solAssert(false, "Function or constructor expected.");
+ m_scanner->next();
+
+ if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen)
+ result.name = make_shared<ASTString>();
else
result.name = expectIdentifierToken();
+
+ if (!result.name->empty() && _contractName && *result.name == *_contractName)
+ result.isConstructor = true;
+
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
result.parameters = parseParameterList(options);
@@ -407,7 +426,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- FunctionHeaderParserResult header = parseFunctionHeader(false, true);
+ FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName);
if (
!header.modifiers.empty() ||
@@ -426,12 +445,11 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
}
else
m_scanner->next(); // just consume the ';'
- bool const c_isConstructor = (_contractName && *header.name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(
header.name,
header.visibility,
header.stateMutability,
- c_isConstructor,
+ header.isConstructor,
docstring,
header.parameters,
header.modifiers,
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 3f780af9..eb120a61 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -56,6 +56,7 @@ private:
/// This struct is shared for parsing a function header and a function type.
struct FunctionHeaderParserResult
{
+ bool isConstructor;
ASTPointer<ASTString> name;
ASTPointer<ParameterList> parameters;
ASTPointer<ParameterList> returnParameters;
@@ -73,7 +74,11 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
StateMutability parseStateMutability(Token::Value _token);
- FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
+ FunctionHeaderParserResult parseFunctionHeader(
+ bool _forceEmptyName,
+ bool _allowModifiers,
+ ASTString const* _contractName = nullptr
+ );
ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
diff --git a/scripts/cpp-ethereum/build.sh b/scripts/cpp-ethereum/build.sh
new file mode 100755
index 00000000..23ed1290
--- /dev/null
+++ b/scripts/cpp-ethereum/build.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env sh
+
+# Script to build the eth binary from latest develop
+# for ubuntu trusty and ubuntu artful.
+# Requires docker.
+
+set -e
+
+REPO_ROOT="$(dirname "$0")"/../..
+
+for rel in artful trusty
+do
+ docker build -t eth_$rel -f "$REPO_ROOT"/scripts/cpp-ethereum/eth_$rel.docker .
+ tmp_container=$(docker create eth_$rel sh)
+ echo "Built eth ($rel) at $REPO_ROOT/build/eth_$rel"
+ docker cp ${tmp_container}:/build/eth/eth "$REPO_ROOT"/build/eth_$rel
+done \ No newline at end of file
diff --git a/scripts/cpp-ethereum/eth_artful.docker b/scripts/cpp-ethereum/eth_artful.docker
new file mode 100644
index 00000000..7ce9faae
--- /dev/null
+++ b/scripts/cpp-ethereum/eth_artful.docker
@@ -0,0 +1,7 @@
+FROM ubuntu:artful
+
+RUN apt update
+RUN apt -y install libleveldb-dev cmake g++ git
+RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1
+RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off
+RUN cd /build && make eth
diff --git a/scripts/cpp-ethereum/eth_trusty.docker b/scripts/cpp-ethereum/eth_trusty.docker
new file mode 100644
index 00000000..0c4d7a02
--- /dev/null
+++ b/scripts/cpp-ethereum/eth_trusty.docker
@@ -0,0 +1,7 @@
+FROM ubuntu:trusty
+
+RUN apt update
+RUN apt -y install libleveldb-dev cmake g++ git
+RUN git clone --recursive https://github.com/ethereum/cpp-ethereum --branch develop --single-branch --depth 1
+RUN mkdir /build && cd /build && cmake /cpp-ethereum -DCMAKE_BUILD_TYPE=RelWithDebInfo -DTOOLS=Off -DTESTS=Off
+RUN cd /build && make eth
diff --git a/std/StandardToken.sol b/std/StandardToken.sol
index 1b218d67..5afc9747 100644
--- a/std/StandardToken.sol
+++ b/std/StandardToken.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.0;
+pragma solidity >0.4.21;
import "./Token.sol";
@@ -8,7 +8,7 @@ contract StandardToken is Token {
mapping (address =>
mapping (address => uint256)) m_allowance;
- function StandardToken(address _initialOwner, uint256 _supply) public {
+ constructor(address _initialOwner, uint256 _supply) public {
supply = _supply;
balance[_initialOwner] = _supply;
}
diff --git a/std/owned.sol b/std/owned.sol
index ee9860d3..8e1d5917 100644
--- a/std/owned.sol
+++ b/std/owned.sol
@@ -1,4 +1,4 @@
-pragma solidity ^0.4.0;
+pragma solidity >0.4.21;
contract owned {
address owner;
@@ -9,7 +9,7 @@ contract owned {
}
}
- function owned() public {
+ constructor() public {
owner = msg.sender;
}
}
diff --git a/test/RPCSession.cpp b/test/RPCSession.cpp
index 03b1341c..f4eae865 100644
--- a/test/RPCSession.cpp
+++ b/test/RPCSession.cpp
@@ -226,6 +226,8 @@ void RPCSession::test_setChainParams(vector<string> const& _accounts)
forks += "\"EIP158ForkBlock\": \"0x00\",\n";
if (test::Options::get().evmVersion() >= solidity::EVMVersion::byzantium())
forks += "\"byzantiumForkBlock\": \"0x00\",\n";
+ if (test::Options::get().evmVersion() >= solidity::EVMVersion::constantinople())
+ forks += "\"constantinopleForkBlock\": \"0x00\",\n";
static string const c_configString = R"(
{
"sealEngine": "NoProof",
@@ -337,7 +339,9 @@ Json::Value RPCSession::rpcCall(string const& _methodName, vector<string> const&
BOOST_TEST_MESSAGE("Reply: " + reply);
Json::Value result;
- BOOST_REQUIRE(jsonParseStrict(reply, result));
+ string errorMsg;
+ if (!jsonParseStrict(reply, result, &errorMsg))
+ BOOST_REQUIRE_MESSAGE(false, errorMsg);
if (result.isMember("error"))
{
diff --git a/test/libevmasm/Optimiser.cpp b/test/libevmasm/Optimiser.cpp
index f630b304..089be45d 100644
--- a/test/libevmasm/Optimiser.cpp
+++ b/test/libevmasm/Optimiser.cpp
@@ -69,8 +69,9 @@ namespace
{
AssemblyItems input = addDummyLocations(_input);
+ bool usesMsize = (find(_input.begin(), _input.end(), AssemblyItem{Instruction::MSIZE}) != _input.end());
eth::CommonSubexpressionEliminator cse(_state);
- BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), usesMsize) == input.end());
AssemblyItems output = cse.getOptimizedItems();
for (AssemblyItem const& item: output)
@@ -124,7 +125,7 @@ BOOST_AUTO_TEST_CASE(cse_intermediate_swap)
Instruction::SLOAD, Instruction::SWAP1, u256(100), Instruction::EXP, Instruction::SWAP1,
Instruction::DIV, u256(0xff), Instruction::AND
};
- BOOST_REQUIRE(cse.feedItems(input.begin(), input.end()) == input.end());
+ BOOST_REQUIRE(cse.feedItems(input.begin(), input.end(), false) == input.end());
AssemblyItems output = cse.getOptimizedItems();
BOOST_CHECK(!output.empty());
}
@@ -857,6 +858,115 @@ BOOST_AUTO_TEST_CASE(peephole_pop_calldatasize)
BOOST_CHECK(items.empty());
}
+BOOST_AUTO_TEST_CASE(peephole_commutative_swap1)
+{
+ vector<Instruction> ops{
+ Instruction::ADD,
+ Instruction::MUL,
+ Instruction::EQ,
+ Instruction::AND,
+ Instruction::OR,
+ Instruction::XOR
+ };
+ for (Instruction const op: ops)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ op,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(peephole_noncommutative_swap1)
+{
+ // NOTE: not comprehensive
+ vector<Instruction> ops{
+ Instruction::SUB,
+ Instruction::DIV,
+ Instruction::SDIV,
+ Instruction::MOD,
+ Instruction::SMOD,
+ Instruction::EXP
+ };
+ for (Instruction const op: ops)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(!peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
+BOOST_AUTO_TEST_CASE(peephole_swap_comparison)
+{
+ map<Instruction, Instruction> swappableOps{
+ { Instruction::LT, Instruction::GT },
+ { Instruction::GT, Instruction::LT },
+ { Instruction::SLT, Instruction::SGT },
+ { Instruction::SGT, Instruction::SLT }
+ };
+
+ for (auto const& op: swappableOps)
+ {
+ AssemblyItems items{
+ u256(1),
+ u256(2),
+ Instruction::SWAP1,
+ op.first,
+ u256(4),
+ u256(5)
+ };
+ AssemblyItems expectation{
+ u256(1),
+ u256(2),
+ op.second,
+ u256(4),
+ u256(5)
+ };
+ PeepholeOptimiser peepOpt(items);
+ BOOST_REQUIRE(peepOpt.optimise());
+ BOOST_CHECK_EQUAL_COLLECTIONS(
+ items.begin(), items.end(),
+ expectation.begin(), expectation.end()
+ );
+ }
+}
+
BOOST_AUTO_TEST_CASE(jumpdest_removal)
{
AssemblyItems items{
diff --git a/test/libsolidity/AnalysisFramework.cpp b/test/libsolidity/AnalysisFramework.cpp
index 4538757d..72b86767 100644
--- a/test/libsolidity/AnalysisFramework.cpp
+++ b/test/libsolidity/AnalysisFramework.cpp
@@ -56,12 +56,23 @@ AnalysisFramework::parseAnalyseAndReturnError(
m_compiler.analyze();
+ ErrorList errors = filterErrors(m_compiler.errors(), _reportWarnings);
+ if (errors.size() > 1 && !_allowMultipleErrors)
+ BOOST_FAIL("Multiple errors found: " + formatErrors());
+
+ return make_pair(&m_compiler.ast(""), std::move(errors));
+}
+
+ErrorList AnalysisFramework::filterErrors(ErrorList const& _errorList, bool _includeWarnings) const
+{
ErrorList errors;
- for (auto const& currentError: m_compiler.errors())
+ for (auto const& currentError: _errorList)
{
solAssert(currentError->comment(), "");
if (currentError->type() == Error::Type::Warning)
{
+ if (!_includeWarnings)
+ continue;
bool ignoreWarning = false;
for (auto const& filter: m_warningsToFilter)
if (currentError->comment()->find(filter) == 0)
@@ -73,17 +84,10 @@ AnalysisFramework::parseAnalyseAndReturnError(
continue;
}
- if (_reportWarnings || (currentError->type() != Error::Type::Warning))
- {
- if (!_allowMultipleErrors && !errors.empty())
- {
- BOOST_FAIL("Multiple errors found: " + formatErrors());
- }
- errors.emplace_back(std::move(currentError));
- }
+ errors.emplace_back(currentError);
}
- return make_pair(&m_compiler.ast(""), errors);
+ return errors;
}
SourceUnit const* AnalysisFramework::parseAndAnalyse(string const& _source)
@@ -110,7 +114,7 @@ ErrorList AnalysisFramework::expectError(std::string const& _source, bool _warni
return sourceAndErrors.second;
}
-string AnalysisFramework::formatErrors()
+string AnalysisFramework::formatErrors() const
{
string message;
for (auto const& error: m_compiler.errors())
@@ -118,7 +122,7 @@ string AnalysisFramework::formatErrors()
return message;
}
-string AnalysisFramework::formatError(Error const& _error)
+string AnalysisFramework::formatError(Error const& _error) const
{
return SourceReferenceFormatter::formatExceptionInformation(
_error,
diff --git a/test/libsolidity/AnalysisFramework.h b/test/libsolidity/AnalysisFramework.h
index 6ecf4a5a..05490a42 100644
--- a/test/libsolidity/AnalysisFramework.h
+++ b/test/libsolidity/AnalysisFramework.h
@@ -57,8 +57,8 @@ protected:
bool success(std::string const& _source);
ErrorList expectError(std::string const& _source, bool _warning = false, bool _allowMultiple = false);
- std::string formatErrors();
- std::string formatError(Error const& _error);
+ std::string formatErrors() const;
+ std::string formatError(Error const& _error) const;
static ContractDefinition const* retrieveContractByName(SourceUnit const& _source, std::string const& _name);
static FunctionTypePointer retrieveFunctionBySignature(
@@ -66,6 +66,9 @@ protected:
std::string const& _signature
);
+ // filter out the warnings in m_warningsToFilter or all warnings if _includeWarnings is false
+ ErrorList filterErrors(ErrorList const& _errorList, bool _includeWarnings) const;
+
std::vector<std::string> m_warningsToFilter = {"This is a pre-release compiler version"};
dev::solidity::CompilerStack m_compiler;
};
diff --git a/test/libsolidity/JSONCompiler.cpp b/test/libsolidity/JSONCompiler.cpp
index aed0a370..cdcc22a6 100644
--- a/test/libsolidity/JSONCompiler.cpp
+++ b/test/libsolidity/JSONCompiler.cpp
@@ -111,12 +111,12 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
- "6060604052600080fd00"
+ "6080604052600080fd00"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
@@ -153,12 +153,12 @@ BOOST_AUTO_TEST_CASE(single_compilation)
BOOST_CHECK(contract["bytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["bytecode"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["runtimeBytecode"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["runtimeBytecode"].asString()),
- "6060604052600080fd00"
+ "6080604052600080fd00"
);
BOOST_CHECK(contract["functionHashes"].isObject());
BOOST_CHECK(contract["gasEstimates"].isObject());
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index f5813aed..beeae786 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -4884,6 +4884,48 @@ BOOST_AUTO_TEST_CASE(array_push)
ABI_CHECK(callContractFunction("test()"), encodeArgs(5, 4, 3, 3));
}
+BOOST_AUTO_TEST_CASE(array_push_struct)
+{
+ char const* sourceCode = R"(
+ contract c {
+ struct S { uint16 a; uint16 b; uint16[3] c; uint16[] d; }
+ S[] data;
+ function test() returns (uint16, uint16, uint16, uint16) {
+ S memory s;
+ s.a = 2;
+ s.b = 3;
+ s.c[2] = 4;
+ s.d = new uint16[](4);
+ s.d[2] = 5;
+ data.push(s);
+ return (data[0].a, data[0].b, data[0].c[2], data[0].d[2]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(2, 3, 4, 5));
+}
+
+BOOST_AUTO_TEST_CASE(array_push_packed_array)
+{
+ char const* sourceCode = R"(
+ contract c {
+ uint80[] x;
+ function test() returns (uint80, uint80, uint80, uint80) {
+ x.push(1);
+ x.push(2);
+ x.push(3);
+ x.push(4);
+ x.push(5);
+ x.length = 4;
+ return (x[0], x[1], x[2], x[3]);
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(1, 2, 3, 4));
+}
+
BOOST_AUTO_TEST_CASE(byte_array_push)
{
char const* sourceCode = R"(
@@ -4904,6 +4946,29 @@ BOOST_AUTO_TEST_CASE(byte_array_push)
ABI_CHECK(callContractFunction("test()"), encodeArgs(false));
}
+BOOST_AUTO_TEST_CASE(byte_array_push_transition)
+{
+ // Tests transition between short and long encoding
+ char const* sourceCode = R"(
+ contract c {
+ bytes data;
+ function test() returns (uint) {
+ for (uint i = 1; i < 40; i++)
+ {
+ data.push(byte(i));
+ if (data.length != i) return 0x1000 + i;
+ if (data[data.length - 1] != byte(i)) return i;
+ }
+ for (i = 1; i < 40; i++)
+ if (data[i - 1] != byte(i)) return 0x1000000 + i;
+ return 0;
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("test()"), encodeArgs(0));
+}
+
BOOST_AUTO_TEST_CASE(external_array_args)
{
char const* sourceCode = R"(
@@ -7687,7 +7752,6 @@ BOOST_AUTO_TEST_CASE(create_memory_array_allocation_size)
ABI_CHECK(callContractFunction("f()"), encodeArgs(0x40, 0x40, 0x20 + 256));
}
-
BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes)
{
// Computes binomial coefficients the chinese way
@@ -7710,6 +7774,41 @@ BOOST_AUTO_TEST_CASE(memory_arrays_of_various_sizes)
ABI_CHECK(callContractFunction("f(uint256,uint256)", encodeArgs(u256(9), u256(5))), encodeArgs(u256(70)));
}
+BOOST_AUTO_TEST_CASE(create_multiple_dynamic_arrays)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function f() returns (uint) {
+ uint[][] memory x = new uint[][](42);
+ assert(x[0].length == 0);
+ x[0] = new uint[](1);
+ x[0][0] = 1;
+ assert(x[4].length == 0);
+ x[4] = new uint[](1);
+ x[4][0] = 2;
+ assert(x[10].length == 0);
+ x[10] = new uint[](1);
+ x[10][0] = 44;
+ uint[][] memory y = new uint[][](24);
+ assert(y[0].length == 0);
+ y[0] = new uint[](1);
+ y[0][0] = 1;
+ assert(y[4].length == 0);
+ y[4] = new uint[](1);
+ y[4][0] = 2;
+ assert(y[10].length == 0);
+ y[10] = new uint[](1);
+ y[10][0] = 88;
+ if ((x[0][0] == y[0][0]) && (x[4][0] == y[4][0]) && (x[10][0] == 44) && (y[10][0] == 88))
+ return 7;
+ return 0;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
+}
+
BOOST_AUTO_TEST_CASE(memory_overwrite)
{
char const* sourceCode = R"(
@@ -8756,6 +8855,32 @@ BOOST_AUTO_TEST_CASE(create_dynamic_array_with_zero_length)
ABI_CHECK(callContractFunction("f()"), encodeArgs(u256(7)));
}
+BOOST_AUTO_TEST_CASE(correctly_initialize_memory_array_in_constructor)
+{
+ // Memory arrays are initialized using codecopy past the size of the code.
+ // This test checks that it also works in the constructor context.
+ char const* sourceCode = R"(
+ contract C {
+ bool public success;
+ function C() public {
+ // Make memory dirty.
+ assembly {
+ for { let i := 0 } lt(i, 64) { i := add(i, 1) } {
+ mstore(msize, not(0))
+ }
+ }
+ uint16[3] memory c;
+ require(c[0] == 0 && c[1] == 0 && c[2] == 0);
+ uint16[] memory x = new uint16[](3);
+ require(x[0] == 0 && x[1] == 0 && x[2] == 0);
+ success = true;
+ }
+ }
+ )";
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("success()"), encodeArgs(u256(1)));
+}
+
BOOST_AUTO_TEST_CASE(return_does_not_skip_modifier)
{
char const* sourceCode = R"(
@@ -9119,7 +9244,7 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail)
int mutex;
function t() returns (uint) {
if (mutex > 0)
- return 7;
+ { assembly { mstore(0, 7) return(0, 0x20) } }
mutex = 1;
// Avoid re-executing this function if we jump somewhere.
x();
@@ -9132,6 +9257,27 @@ BOOST_AUTO_TEST_CASE(calling_uninitialized_function_in_detail)
ABI_CHECK(callContractFunction("t()"), encodeArgs());
}
+BOOST_AUTO_TEST_CASE(calling_uninitialized_function_through_array)
+{
+ char const* sourceCode = R"(
+ contract C {
+ int mutex;
+ function t() returns (uint) {
+ if (mutex > 0)
+ { assembly { mstore(0, 7) return(0, 0x20) } }
+ mutex = 1;
+ // Avoid re-executing this function if we jump somewhere.
+ function() internal returns (uint)[200] x;
+ x[0]();
+ return 2;
+ }
+ }
+ )";
+
+ compileAndRun(sourceCode, 0, "C");
+ ABI_CHECK(callContractFunction("t()"), encodeArgs());
+}
+
BOOST_AUTO_TEST_CASE(pass_function_types_internally)
{
char const* sourceCode = R"(
@@ -10951,6 +11097,50 @@ BOOST_AUTO_TEST_CASE(staticcall_for_view_and_pure)
}
}
+BOOST_AUTO_TEST_CASE(swap_peephole_optimisation)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function lt(uint a, uint b) returns (bool c) {
+ assembly {
+ a
+ b
+ swap1
+ lt
+ =: c
+ }
+ }
+ function add(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ swap1
+ add
+ =: c
+ }
+ }
+ function div(uint a, uint b) returns (uint c) {
+ assembly {
+ a
+ b
+ swap1
+ div
+ =: c
+ }
+ }
+ }
+ )";
+ compileAndRun(sourceCode);
+ BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(1)));
+ BOOST_CHECK(callContractFunction("lt(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(1), u256(2)) == encodeArgs(u256(3)));
+ BOOST_CHECK(callContractFunction("add(uint256,uint256)", u256(100), u256(200)) == encodeArgs(u256(300)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(2), u256(1)) == encodeArgs(u256(2)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(200), u256(10)) == encodeArgs(u256(20)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(1), u256(0)) == encodeArgs(u256(0)));
+ BOOST_CHECK(callContractFunction("div(uint256,uint256)", u256(0), u256(1)) == encodeArgs(u256(0)));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityNameAndTypeResolution.cpp b/test/libsolidity/SolidityNameAndTypeResolution.cpp
index dd29bf48..fcee0df3 100644
--- a/test/libsolidity/SolidityNameAndTypeResolution.cpp
+++ b/test/libsolidity/SolidityNameAndTypeResolution.cpp
@@ -92,61 +92,6 @@ BOOST_AUTO_TEST_CASE(reference_to_later_declaration)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(struct_definition_directly_recursive)
-{
- char const* text = R"(
- contract test {
- struct MyStructName {
- address addr;
- MyStructName x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Recursive struct definition.");
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_indirectly_recursive)
-{
- char const* text = R"(
- contract test {
- struct MyStructName1 {
- address addr;
- uint256 count;
- MyStructName2 x;
- }
- struct MyStructName2 {
- MyStructName1 x;
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Recursive struct definition.");
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_not_really_recursive)
-{
- char const* text = R"(
- contract test {
- struct s1 { uint a; }
- struct s2 { s1 x; s1 y; }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(struct_definition_recursion_via_mapping)
-{
- char const* text = R"(
- contract test {
- struct MyStructName1 {
- address addr;
- uint256 count;
- mapping(uint => MyStructName1) x;
- }
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(type_inference_smoke_test)
{
char const* text = R"(
@@ -962,6 +907,35 @@ BOOST_AUTO_TEST_CASE(base_constructor_arguments_override)
CHECK_SUCCESS(text);
}
+BOOST_AUTO_TEST_CASE(new_constructor_syntax)
+{
+ char const* text = R"(
+ contract A { constructor() public {} }
+ )";
+ CHECK_SUCCESS_NO_WARNINGS(text);
+}
+
+BOOST_AUTO_TEST_CASE(old_constructor_syntax)
+{
+ char const* text = R"(
+ contract A { function A() public {} }
+ )";
+ CHECK_WARNING(
+ text,
+ "Defining constructors as functions with the same name as the contract is deprecated."
+ );
+
+ text = R"(
+ pragma experimental "v0.5.0";
+ contract A { function A() public {} }
+ )";
+ CHECK_ERROR(
+ text,
+ SyntaxError,
+ "Functions are not allowed to have the same name as the contract."
+ );
+}
+
BOOST_AUTO_TEST_CASE(implicit_derived_to_base_conversion)
{
char const* text = R"(
@@ -2452,8 +2426,8 @@ BOOST_AUTO_TEST_CASE(test_fromElementaryTypeName)
BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 31, 0)) == *make_shared<FixedBytesType>(31));
BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::BytesM, 32, 0)) == *make_shared<FixedBytesType>(32));
- BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Signed));
- BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 19, FixedPointType::Modifier::Unsigned));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::Fixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Signed));
+ BOOST_CHECK(*Type::fromElementaryTypeName(ElementaryTypeNameToken(Token::UFixed, 0, 0)) == *make_shared<FixedPointType>(128, 18, FixedPointType::Modifier::Unsigned));
}
BOOST_AUTO_TEST_CASE(test_byte_is_alias_of_byte1)
@@ -4471,7 +4445,7 @@ BOOST_AUTO_TEST_CASE(invalid_int_implicit_conversion_from_fixed)
}
}
)";
- CHECK_ERROR(text, TypeError, "Type fixed128x19 is not implicitly convertible to expected type int256");
+ CHECK_ERROR(text, TypeError, "Type fixed128x18 is not implicitly convertible to expected type int256");
}
BOOST_AUTO_TEST_CASE(rational_unary_operation)
@@ -4589,7 +4563,7 @@ BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_size)
}
}
)";
- CHECK_ERROR(text, TypeError, "Type ufixed128x19 is not implicitly convertible to expected type ufixed248x8");
+ CHECK_ERROR(text, TypeError, "Type ufixed128x18 is not implicitly convertible to expected type ufixed248x8");
}
BOOST_AUTO_TEST_CASE(fixed_type_invalid_implicit_conversion_lost_data)
@@ -4676,7 +4650,7 @@ BOOST_AUTO_TEST_CASE(fixed_to_bytes_implicit_conversion)
}
}
)";
- CHECK_ERROR(text, TypeError, "fixed128x19 is not implicitly convertible to expected type bytes32");
+ CHECK_ERROR(text, TypeError, "fixed128x18 is not implicitly convertible to expected type bytes32");
}
BOOST_AUTO_TEST_CASE(mapping_with_fixed_literal)
@@ -6193,44 +6167,6 @@ BOOST_AUTO_TEST_CASE(read_returned_struct)
)";
CHECK_WARNING(text, "Experimental features");
}
-
-BOOST_AUTO_TEST_CASE(return_recursive_structs)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; S[] sub; }
- function f() returns (uint, S) {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(return_recursive_structs2)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; S[2][] sub; }
- function f() returns (uint, S) {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
-BOOST_AUTO_TEST_CASE(return_recursive_structs3)
-{
- char const* text = R"(
- contract C {
- struct S { uint a; S[][][] sub; }
- struct T { S s; }
- function f() returns (uint x, T t) {
- }
- }
- )";
- CHECK_ERROR(text, TypeError, "Internal or recursive type is not allowed for public or external functions.");
-}
-
BOOST_AUTO_TEST_CASE(address_checksum_type_deduction)
{
char const* text = R"(
@@ -6353,38 +6289,6 @@ BOOST_AUTO_TEST_CASE(address_methods)
CHECK_SUCCESS(text);
}
-BOOST_AUTO_TEST_CASE(cyclic_dependency_for_constants)
-{
- char const* text = R"(
- contract C {
- uint constant a = a;
- }
- )";
- CHECK_ERROR(text, TypeError, "cyclic dependency via a");
- text = R"(
- contract C {
- uint constant a = b * c;
- uint constant b = 7;
- uint constant c = b + uint(keccak256(d));
- uint constant d = 2 + a;
- }
- )";
- CHECK_ERROR_ALLOW_MULTI(text, TypeError, (std::vector<std::string>{
- "a has a cyclic dependency via c",
- "c has a cyclic dependency via d",
- "d has a cyclic dependency via a"
- }));
- text = R"(
- contract C {
- uint constant a = b * c;
- uint constant b = 7;
- uint constant c = 4 + uint(keccak256(d));
- uint constant d = 2 + b;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
BOOST_AUTO_TEST_CASE(interface)
{
char const* text = R"(
@@ -6429,54 +6333,6 @@ BOOST_AUTO_TEST_CASE(interface_function_bodies)
CHECK_ERROR(text, TypeError, "Functions in interfaces cannot have an implementation");
}
-BOOST_AUTO_TEST_CASE(interface_function_external)
-{
- char const* text = R"(
- pragma experimental "v0.5.0";
- interface I {
- function f() external;
- }
- )";
- CHECK_SUCCESS(text);
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_public)
-{
- char const* text = R"(
- interface I {
- function f() public;
- }
- )";
- CHECK_WARNING(text, "Functions in interfaces should be declared external.");
- text = R"(
- pragma experimental "v0.5.0";
- interface I {
- function f() public;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces must be declared external.");
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_internal)
-{
- char const* text = R"(
- interface I {
- function f() internal;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private.");
-}
-
-BOOST_AUTO_TEST_CASE(interface_function_private)
-{
- char const* text = R"(
- interface I {
- function f() private;
- }
- )";
- CHECK_ERROR(text, TypeError, "Functions in interfaces cannot be internal or private.");
-}
-
BOOST_AUTO_TEST_CASE(interface_events)
{
char const* text = R"(
@@ -6964,7 +6820,7 @@ BOOST_AUTO_TEST_CASE(shadowing_builtins_ignores_constructor)
{
char const* text = R"(
contract C {
- function C() public {}
+ constructor() public {}
}
)";
CHECK_SUCCESS_NO_WARNINGS(text);
@@ -7376,7 +7232,7 @@ BOOST_AUTO_TEST_CASE(using_this_in_constructor)
{
char const* text = R"(
contract C {
- function C() public {
+ constructor() public {
this.f();
}
function f() pure public {
diff --git a/test/libsolidity/SolidityOptimizer.cpp b/test/libsolidity/SolidityOptimizer.cpp
index 33039ca9..5326feaf 100644
--- a/test/libsolidity/SolidityOptimizer.cpp
+++ b/test/libsolidity/SolidityOptimizer.cpp
@@ -74,11 +74,11 @@ public:
unsigned const _optimizeRuns = 200
)
{
- bytes nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
+ m_nonOptimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, false, _optimizeRuns);
m_nonOptimizedContract = m_contractAddress;
- bytes optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
- size_t nonOptimizedSize = numInstructions(nonOptimizedBytecode);
- size_t optimizedSize = numInstructions(optimizedBytecode);
+ m_optimizedBytecode = compileAndRunWithOptimizer(_sourceCode, _value, _contractName, true, _optimizeRuns);
+ size_t nonOptimizedSize = numInstructions(m_nonOptimizedBytecode);
+ size_t optimizedSize = numInstructions(m_optimizedBytecode);
BOOST_CHECK_MESSAGE(
_optimizeRuns < 50 || optimizedSize < nonOptimizedSize,
string("Optimizer did not reduce bytecode size. Non-optimized size: ") +
@@ -93,8 +93,10 @@ public:
{
m_contractAddress = m_nonOptimizedContract;
bytes nonOptimizedOutput = callContractFunction(_sig, _arguments...);
+ m_gasUsedNonOptimized = m_gasUsed;
m_contractAddress = m_optimizedContract;
bytes optimizedOutput = callContractFunction(_sig, _arguments...);
+ m_gasUsedOptimized = m_gasUsed;
BOOST_CHECK_MESSAGE(!optimizedOutput.empty(), "No optimized output for " + _sig);
BOOST_CHECK_MESSAGE(!nonOptimizedOutput.empty(), "No un-optimized output for " + _sig);
BOOST_CHECK_MESSAGE(nonOptimizedOutput == optimizedOutput, "Computed values do not match."
@@ -104,7 +106,7 @@ public:
/// @returns the number of intructions in the given bytecode, not taking the metadata hash
/// into account.
- size_t numInstructions(bytes const& _bytecode)
+ size_t numInstructions(bytes const& _bytecode, boost::optional<Instruction> _which = boost::optional<Instruction>{})
{
BOOST_REQUIRE(_bytecode.size() > 5);
size_t metadataSize = (_bytecode[_bytecode.size() - 2] << 8) + _bytecode[_bytecode.size() - 1];
@@ -112,13 +114,18 @@ public:
BOOST_REQUIRE(_bytecode.size() >= metadataSize + 2);
bytes realCode = bytes(_bytecode.begin(), _bytecode.end() - metadataSize - 2);
size_t instructions = 0;
- solidity::eachInstruction(realCode, [&](Instruction, u256 const&) {
- instructions++;
+ solidity::eachInstruction(realCode, [&](Instruction _instr, u256 const&) {
+ if (!_which || *_which == _instr)
+ instructions++;
});
return instructions;
}
protected:
+ u256 m_gasUsedOptimized;
+ u256 m_gasUsedNonOptimized;
+ bytes m_nonOptimizedBytecode;
+ bytes m_optimizedBytecode;
Address m_optimizedContract;
Address m_nonOptimizedContract;
};
@@ -581,6 +588,49 @@ BOOST_AUTO_TEST_CASE(invalid_state_at_control_flow_join)
compareVersions("test()");
}
+BOOST_AUTO_TEST_CASE(init_empty_dynamic_arrays)
+{
+ // This is not so much an optimizer test, but rather a test
+ // that allocating empty arrays is implemented efficiently.
+ // In particular, initializing a dynamic memory array does
+ // not use any memory.
+ char const* sourceCode = R"(
+ contract Test {
+ function f() pure returns (uint r) {
+ uint[][] memory x = new uint[][](20000);
+ return x.length;
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+ BOOST_CHECK_LE(m_gasUsedNonOptimized, 1900000);
+ BOOST_CHECK_LE(1600000, m_gasUsedNonOptimized);
+}
+
+BOOST_AUTO_TEST_CASE(optimise_multi_stores)
+{
+ char const* sourceCode = R"(
+ contract Test {
+ struct S { uint16 a; uint16 b; uint16[3] c; uint[] dyn; }
+ uint padding;
+ S[] s;
+ function f() public returns (uint16, uint16, uint16[3], uint) {
+ uint16[3] memory c;
+ c[0] = 7;
+ c[1] = 8;
+ c[2] = 9;
+ s.push(S(1, 2, c, new uint[](4)));
+ return (s[0].a, s[0].b, s[0].c, s[0].dyn[2]);
+ }
+ }
+ )";
+ compileBothVersions(sourceCode);
+ compareVersions("f()");
+ BOOST_CHECK_EQUAL(numInstructions(m_nonOptimizedBytecode, Instruction::SSTORE), 9);
+ BOOST_CHECK_EQUAL(numInstructions(m_optimizedBytecode, Instruction::SSTORE), 8);
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/SolidityParser.cpp b/test/libsolidity/SolidityParser.cpp
index 4e862f60..93e6bcaa 100644
--- a/test/libsolidity/SolidityParser.cpp
+++ b/test/libsolidity/SolidityParser.cpp
@@ -112,26 +112,6 @@ while(0)
BOOST_AUTO_TEST_SUITE(SolidityParser)
-BOOST_AUTO_TEST_CASE(smoke_test)
-{
- char const* text = R"(
- contract test {
- uint256 stateVariable1;
- }
- )";
- BOOST_CHECK(successParse(text));
-}
-
-BOOST_AUTO_TEST_CASE(missing_variable_name_in_declaration)
-{
- char const* text = R"(
- contract test {
- uint256 ;
- }
- )";
- CHECK_PARSE_ERROR(text, "Expected identifier");
-}
-
BOOST_AUTO_TEST_CASE(empty_function)
{
char const* text = R"(
diff --git a/test/libsolidity/StandardCompiler.cpp b/test/libsolidity/StandardCompiler.cpp
index dd6eb7c4..b285a2a0 100644
--- a/test/libsolidity/StandardCompiler.cpp
+++ b/test/libsolidity/StandardCompiler.cpp
@@ -261,14 +261,14 @@ BOOST_AUTO_TEST_CASE(basic_compilation)
BOOST_CHECK(contract["evm"]["bytecode"]["object"].isString());
BOOST_CHECK_EQUAL(
dev::test::bytecodeSansMetadata(contract["evm"]["bytecode"]["object"].asString()),
- "60606040523415600e57600080fd5b603580601b6000396000f3006060604052600080fd00"
+ "60806040523415600e57600080fd5b603580601b6000396000f3006080604052600080fd00"
);
BOOST_CHECK(contract["evm"]["assembly"].isString());
BOOST_CHECK(contract["evm"]["assembly"].asString().find(
- " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x60)\n jumpi(tag_1, iszero(callvalue))\n"
+ " /* \"fileA\":0:14 contract A { } */\n mstore(0x40, 0x80)\n jumpi(tag_1, iszero(callvalue))\n"
" 0x0\n dup1\n revert\ntag_1:\n dataSize(sub_0)\n dup1\n dataOffset(sub_0)\n 0x0\n codecopy\n 0x0\n"
" return\nstop\n\nsub_0: assembly {\n /* \"fileA\":0:14 contract A { } */\n"
- " mstore(0x40, 0x60)\n 0x0\n dup1\n revert\n\n"
+ " mstore(0x40, 0x80)\n 0x0\n dup1\n revert\n\n"
" auxdata: 0xa165627a7a7230582") == 0);
BOOST_CHECK(contract["evm"]["gasEstimates"].isObject());
BOOST_CHECK_EQUAL(
diff --git a/test/libsolidity/SyntaxTest.cpp b/test/libsolidity/SyntaxTest.cpp
index ca051138..329543bf 100644
--- a/test/libsolidity/SyntaxTest.cpp
+++ b/test/libsolidity/SyntaxTest.cpp
@@ -16,6 +16,7 @@
*/
#include <test/libsolidity/SyntaxTest.h>
+#include <test/Options.h>
#include <boost/algorithm/string.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/throw_exception.hpp>
@@ -59,93 +60,52 @@ SyntaxTest::SyntaxTest(string const& _filename)
bool SyntaxTest::run(ostream& _stream, string const& _linePrefix, bool const _formatted)
{
- m_errorList = parseAnalyseAndReturnError(m_source, true, true, true).second;
- if (!matchesExpectations(m_errorList))
+ m_compiler.reset();
+ m_compiler.addSource("", "pragma solidity >=0.0;\n" + m_source);
+ m_compiler.setEVMVersion(dev::test::Options::get().evmVersion());
+
+ if (m_compiler.parse())
+ m_compiler.analyze();
+
+ for (auto const& currentError: filterErrors(m_compiler.errors(), true))
+ m_errorList.emplace_back(SyntaxTestError{currentError->typeName(), errorMessage(*currentError)});
+
+ if (m_expectations != m_errorList)
{
- std::string nextIndentLevel = _linePrefix + " ";
+ string nextIndentLevel = _linePrefix + " ";
FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Expected result:" << endl;
- printExpected(_stream, nextIndentLevel, _formatted);
+ printErrorList(_stream, m_expectations, nextIndentLevel, _formatted);
FormattedScope(_stream, _formatted, {BOLD, CYAN}) << _linePrefix << "Obtained result:\n";
- printErrorList(_stream, m_errorList, nextIndentLevel, false, false, _formatted);
+ printErrorList(_stream, m_errorList, nextIndentLevel, _formatted);
return false;
}
return true;
}
-void SyntaxTest::printExpected(ostream& _stream, string const& _linePrefix, bool const _formatted) const
-{
- if (m_expectations.empty())
- FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl;
- else
- for (auto const& expectation: m_expectations)
- {
- FormattedScope(_stream, _formatted, {BOLD, expectation.type == "Warning" ? YELLOW : RED}) <<
- _linePrefix << expectation.type << ": ";
- _stream << expectation.message << endl;
- }
-}
-
void SyntaxTest::printErrorList(
ostream& _stream,
- ErrorList const& _errorList,
+ vector<SyntaxTestError> const& _errorList,
string const& _linePrefix,
- bool const _ignoreWarnings,
- bool const _lineNumbers,
bool const _formatted
-) const
+)
{
if (_errorList.empty())
FormattedScope(_stream, _formatted, {BOLD, GREEN}) << _linePrefix << "Success" << endl;
else
for (auto const& error: _errorList)
{
- bool isWarning = (error->type() == Error::Type::Warning);
- if (isWarning && _ignoreWarnings) continue;
-
{
- FormattedScope scope(_stream, _formatted, {BOLD, isWarning ? YELLOW : RED});
+ FormattedScope scope(_stream, _formatted, {BOLD, (error.type == "Warning") ? YELLOW : RED});
_stream << _linePrefix;
- if (_lineNumbers)
- {
- int line = offsetToLineNumber(
- boost::get_error_info<errinfo_sourceLocation>(*error)->start
- );
- if (line >= 0)
- _stream << "(" << line << "): ";
- }
- _stream << error->typeName() << ": ";
+ _stream << error.type << ": ";
}
- _stream << errorMessage(*error) << endl;
+ _stream << error.message << endl;
}
}
-int SyntaxTest::offsetToLineNumber(int _location) const
-{
- // parseAnalyseAndReturnError(...) prepends a version pragma
- _location -= strlen("pragma solidity >=0.0;\n");
- if (_location < 0 || static_cast<size_t>(_location) >= m_source.size())
- return -1;
- else
- return 1 + std::count(m_source.begin(), m_source.begin() + _location, '\n');
-}
-
-bool SyntaxTest::matchesExpectations(ErrorList const& _errorList) const
-{
- if (_errorList.size() != m_expectations.size())
- return false;
- else
- for (size_t i = 0; i < _errorList.size(); i++)
- if (
- (_errorList[i]->typeName() != m_expectations[i].type) ||
- (errorMessage(*_errorList[i]) != m_expectations[i].message)
- )
- return false;
- return true;
-}
-
-string SyntaxTest::errorMessage(Error const& _e)
+string SyntaxTest::errorMessage(Exception const& _e)
{
- if (_e.comment())
+ if (_e.comment() && !_e.comment()->empty())
return boost::replace_all_copy(*_e.comment(), "\n", "\\n");
else
return "NONE";
@@ -164,9 +124,9 @@ string SyntaxTest::parseSource(istream& _stream)
return source;
}
-vector<SyntaxTestExpectation> SyntaxTest::parseExpectations(istream& _stream)
+vector<SyntaxTestError> SyntaxTest::parseExpectations(istream& _stream)
{
- vector<SyntaxTestExpectation> expectations;
+ vector<SyntaxTestError> expectations;
string line;
while (getline(_stream, line))
{
@@ -188,7 +148,7 @@ vector<SyntaxTestExpectation> SyntaxTest::parseExpectations(istream& _stream)
skipWhitespace(it, line.end());
string errorMessage(it, line.end());
- expectations.emplace_back(SyntaxTestExpectation{move(errorType), move(errorMessage)});
+ expectations.emplace_back(SyntaxTestError{move(errorType), move(errorMessage)});
}
return expectations;
}
@@ -239,9 +199,11 @@ int SyntaxTest::registerTests(
_suite.add(make_test_case(
[fullpath]
{
- std::stringstream errorStream;
- if (!SyntaxTest(fullpath.string()).run(errorStream))
- BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str());
+ BOOST_REQUIRE_NO_THROW({
+ stringstream errorStream;
+ if (!SyntaxTest(fullpath.string()).run(errorStream))
+ BOOST_ERROR("Test expectation mismatch.\n" + errorStream.str());
+ });
},
_path.stem().string(),
*filenames.back(),
diff --git a/test/libsolidity/SyntaxTest.h b/test/libsolidity/SyntaxTest.h
index cb6ee05c..dddd86ef 100644
--- a/test/libsolidity/SyntaxTest.h
+++ b/test/libsolidity/SyntaxTest.h
@@ -36,10 +36,14 @@ namespace solidity
namespace test
{
-struct SyntaxTestExpectation
+struct SyntaxTestError
{
std::string type;
std::string message;
+ bool operator==(SyntaxTestError const& _rhs) const
+ {
+ return type == _rhs.type && message == _rhs.message;
+ }
};
@@ -50,21 +54,16 @@ public:
bool run(std::ostream& _stream, std::string const& _linePrefix = "", bool const _formatted = false);
- std::vector<SyntaxTestExpectation> const& expectations() const { return m_expectations; }
+ std::vector<SyntaxTestError> const& expectations() const { return m_expectations; }
std::string const& source() const { return m_source; }
- ErrorList const& errorList() const { return m_errorList; }
- ErrorList const& compilerErrors() const { return m_compiler.errors(); }
+ std::vector<SyntaxTestError> const& errorList() const { return m_errorList; }
- void printExpected(std::ostream& _stream, std::string const& _linePrefix, bool const _formatted = false) const;
-
- void printErrorList(
+ static void printErrorList(
std::ostream& _stream,
- ErrorList const& _errors,
+ std::vector<SyntaxTestError> const& _errors,
std::string const& _linePrefix,
- bool const _ignoreWarnings,
- bool const _lineNumbers,
bool const _formatted = false
- ) const;
+ );
static int registerTests(
boost::unit_test::test_suite& _suite,
@@ -72,16 +71,14 @@ public:
boost::filesystem::path const& _path
);
static bool isTestFilename(boost::filesystem::path const& _filename);
+ static std::string errorMessage(Exception const& _e);
private:
- bool matchesExpectations(ErrorList const& _errors) const;
- static std::string errorMessage(Error const& _e);
static std::string parseSource(std::istream& _stream);
- static std::vector<SyntaxTestExpectation> parseExpectations(std::istream& _stream);
- int offsetToLineNumber(int _location) const;
+ static std::vector<SyntaxTestError> parseExpectations(std::istream& _stream);
std::string m_source;
- std::vector<SyntaxTestExpectation> m_expectations;
- ErrorList m_errorList;
+ std::vector<SyntaxTestError> m_expectations;
+ std::vector<SyntaxTestError> m_errorList;
};
}
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol
new file mode 100644
index 00000000..2b6aa088
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_1.sol
@@ -0,0 +1,5 @@
+contract C {
+ uint constant a = a;
+}
+// ----
+// TypeError: The value of the constant a has a cyclic dependency via a.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
new file mode 100644
index 00000000..461979f8
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_2.sol
@@ -0,0 +1,10 @@
+contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = b + uint(keccak256(d));
+ uint constant d = 2 + a;
+}
+// ----
+// TypeError: The value of the constant a has a cyclic dependency via c.
+// TypeError: The value of the constant c has a cyclic dependency via d.
+// TypeError: The value of the constant d has a cyclic dependency via a.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol
new file mode 100644
index 00000000..f63be05e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_3.sol
@@ -0,0 +1,11 @@
+contract C {
+ uint constant x = a;
+ uint constant a = b * c;
+ uint constant b = c;
+ uint constant c = b;
+}
+// ----
+// TypeError: The value of the constant x has a cyclic dependency via a.
+// TypeError: The value of the constant a has a cyclic dependency via b.
+// TypeError: The value of the constant b has a cyclic dependency via c.
+// TypeError: The value of the constant c has a cyclic dependency via b.
diff --git a/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
new file mode 100644
index 00000000..f01cb98e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constants/cyclic_dependency_4.sol
@@ -0,0 +1,6 @@
+contract C {
+ uint constant a = b * c;
+ uint constant b = 7;
+ uint constant c = 4 + uint(keccak256(d));
+ uint constant d = 2 + b;
+} \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol
new file mode 100644
index 00000000..b3fbd04a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses.sol
@@ -0,0 +1,7 @@
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base(), Derived() { }
+// ----
+// Warning: Wrong argument count for constructor call: 0 arguments given but expected 1.
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol
new file mode 100644
index 00000000..b3728634
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_empty_parentheses_V050.sol
@@ -0,0 +1,9 @@
+pragma experimental "v0.5.0";
+
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base(), Derived() { }
+// ----
+// TypeError: Wrong argument count for constructor call: 0 arguments given but expected 1.
diff --git a/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol
new file mode 100644
index 00000000..24cca8f0
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/base_arguments_no_parentheses.sol
@@ -0,0 +1,5 @@
+contract Base {
+ constructor(uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base, Derived {}
diff --git a/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol
new file mode 100644
index 00000000..45a0770f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inheritance/too_few_base_arguments.sol
@@ -0,0 +1,10 @@
+contract Base {
+ constructor(uint, uint) public {}
+}
+contract Derived is Base(2) { }
+contract Derived2 is Base {
+ constructor() Base(2) public { }
+}
+// ----
+// TypeError: Wrong argument count for constructor call: 1 arguments given but expected 2.
+// TypeError: Wrong argument count for modifier invocation: 1 arguments given but expected 2.
diff --git a/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
new file mode 100644
index 00000000..c03fd97d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/missing_variable_name_in_declaration.sol
@@ -0,0 +1,5 @@
+contract test {
+ uint256 ;
+}
+// ----
+// ParserError: Expected identifier, got 'Semicolon'
diff --git a/test/libsolidity/syntaxTests/parsing/smoke_test.sol b/test/libsolidity/syntaxTests/parsing/smoke_test.sol
new file mode 100644
index 00000000..d328b167
--- /dev/null
+++ b/test/libsolidity/syntaxTests/parsing/smoke_test.sol
@@ -0,0 +1,4 @@
+contract test {
+ uint256 stateVariable1;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol
new file mode 100644
index 00000000..9a1c22f1
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/multi_struct_composition.sol
@@ -0,0 +1,15 @@
+pragma experimental ABIEncoderV2;
+
+contract C {
+ struct T { U u; V v; }
+
+ struct U { W w; }
+
+ struct V { W w; }
+
+ struct W { uint x; }
+
+ function f(T) public pure { }
+}
+// ----
+// Warning: Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol
new file mode 100644
index 00000000..d4ad088d
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/parallel_structs.sol
@@ -0,0 +1,15 @@
+pragma experimental ABIEncoderV2;
+
+contract TestContract
+{
+ struct SubStruct {
+ uint256 id;
+ }
+ struct TestStruct {
+ SubStruct subStruct1;
+ SubStruct subStruct2;
+ }
+ function addTestStruct(TestStruct) public pure {}
+}
+// ----
+// Warning: Experimental features are turned on. Do not use experimental features on live deployments.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol
new file mode 100644
index 00000000..c02a8aa4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs.sol
@@ -0,0 +1,7 @@
+contract C {
+ struct S { uint a; S[] sub; }
+ function f() public pure returns (uint, S) {
+ }
+}
+// ----
+// TypeError: Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol
new file mode 100644
index 00000000..e9488cf4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs2.sol
@@ -0,0 +1,7 @@
+contract C {
+ struct S { uint a; S[2][] sub; }
+ function f() public pure returns (uint, S) {
+ }
+}
+// ----
+// TypeError: Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol
new file mode 100644
index 00000000..6728baec
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/return_recursive_structs3.sol
@@ -0,0 +1,8 @@
+contract C {
+ struct S { uint a; S[][][] sub; }
+ struct T { S s; }
+ function f() public pure returns (uint x, T t) {
+ }
+}
+// ----
+// TypeError: Internal or recursive type is not allowed for public or external functions.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol
new file mode 100644
index 00000000..cac2e23f
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_directly_recursive.sol
@@ -0,0 +1,8 @@
+contract Test {
+ struct MyStructName {
+ address addr;
+ MyStructName x;
+ }
+}
+// ----
+// TypeError: Recursive struct definition.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol
new file mode 100644
index 00000000..11fc6307
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_indirectly_recursive.sol
@@ -0,0 +1,12 @@
+contract Test {
+ struct MyStructName1 {
+ address addr;
+ uint256 count;
+ MyStructName2 x;
+ }
+ struct MyStructName2 {
+ MyStructName1 x;
+ }
+}
+// ----
+// TypeError: Recursive struct definition.
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol
new file mode 100644
index 00000000..6ec4ee01
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_not_really_recursive.sol
@@ -0,0 +1,4 @@
+contract Test {
+ struct S1 { uint a; }
+ struct S2 { S1 x; S1 y; }
+}
diff --git a/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol
new file mode 100644
index 00000000..926981b3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/struct_definition_recursion_via_mapping.sol
@@ -0,0 +1,7 @@
+contract Test {
+ struct MyStructName1 {
+ address addr;
+ uint256 count;
+ mapping(uint => MyStructName1) x;
+ }
+}
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol
new file mode 100644
index 00000000..7b9044dd
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_default.sol
@@ -0,0 +1,6 @@
+interface I {
+ function f();
+}
+// ----
+// Warning: Functions in interfaces should be declared external.
+// Warning: No visibility specified. Defaulting to "public". In interfaces it defaults to external.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol
new file mode 100644
index 00000000..127d4e92
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_default050.sol
@@ -0,0 +1,7 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f();
+}
+// ----
+// SyntaxError: No visibility specified.
+// TypeError: Functions in interfaces must be declared external.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol
new file mode 100644
index 00000000..3f0a9aca
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_external050.sol
@@ -0,0 +1,5 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f() external;
+}
+// ----
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol
new file mode 100644
index 00000000..a1cf5246
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_internal.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() internal;
+}
+// ----
+// TypeError: Functions in interfaces cannot be internal or private.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_private.sol b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol
new file mode 100644
index 00000000..887ebd4b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_private.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() private;
+}
+// ----
+// TypeError: Functions in interfaces cannot be internal or private.
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol
new file mode 100644
index 00000000..146d4f5b
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_public.sol
@@ -0,0 +1,5 @@
+interface I {
+ function f() public;
+}
+// ----
+// Warning: Functions in interfaces should be declared external. \ No newline at end of file
diff --git a/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol
new file mode 100644
index 00000000..f957f0b4
--- /dev/null
+++ b/test/libsolidity/syntaxTests/visibility/interface/function_public050.sol
@@ -0,0 +1,6 @@
+pragma experimental "v0.5.0";
+interface I {
+ function f() public;
+}
+// ----
+// TypeError: Functions in interfaces must be declared external. \ No newline at end of file
diff --git a/test/tools/isoltest.cpp b/test/tools/isoltest.cpp
index 5efec421..07df8f60 100644
--- a/test/tools/isoltest.cpp
+++ b/test/tools/isoltest.cpp
@@ -55,8 +55,8 @@ public:
{
Success,
Failure,
- ParserError,
- InputOutputError
+ InputOutputError,
+ Exception
};
Result process();
@@ -76,7 +76,7 @@ private:
Quit
};
- Request handleResponse(bool const _parserError);
+ Request handleResponse(bool const _exception);
void printContract() const;
@@ -100,7 +100,6 @@ void SyntaxTestTool::printContract() const
SyntaxTestTool::Result SyntaxTestTool::process()
{
bool success;
- bool parserError = false;
std::stringstream outputMessages;
(FormattedScope(cout, m_formatted, {BOLD}) << m_name << ": ").flush();
@@ -119,10 +118,35 @@ SyntaxTestTool::Result SyntaxTestTool::process()
{
success = m_test->run(outputMessages, " ", m_formatted);
}
- catch (...)
+ catch(CompilerError const& _e)
{
- success = false;
- parserError = true;
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "Exception: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(InternalCompilerError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "InternalCompilerError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(FatalError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "FatalError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(UnimplementedFeatureError const& _e)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "UnimplementedFeatureError: " << SyntaxTest::errorMessage(_e) << endl;
+ return Result::Exception;
+ }
+ catch(...)
+ {
+ FormattedScope(cout, m_formatted, {BOLD, RED}) <<
+ "Unknown Exception" << endl;
+ return Result::Exception;
}
if (success)
@@ -137,25 +161,14 @@ SyntaxTestTool::Result SyntaxTestTool::process()
FormattedScope(cout, m_formatted, {BOLD, CYAN}) << " Contract:" << endl;
printContract();
- if (parserError)
- {
- cout << " ";
- FormattedScope(cout, m_formatted, {INVERSE, RED}) << "Parsing failed:" << endl;
- m_test->printErrorList(cout, m_test->compilerErrors(), " ", true, true, m_formatted);
- cout << endl;
- return Result::ParserError;
- }
- else
- {
- cout << outputMessages.str() << endl;
- return Result::Failure;
- }
+ cout << outputMessages.str() << endl;
+ return Result::Failure;
}
}
-SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError)
+SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _exception)
{
- if (_parserError)
+ if (_exception)
cout << "(e)dit/(s)kip/(q)uit? ";
else
cout << "(e)dit/(u)pdate expectations/(s)kip/(q)uit? ";
@@ -169,7 +182,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError)
cout << endl;
return Request::Skip;
case 'u':
- if (_parserError)
+ if (_exception)
break;
else
{
@@ -178,7 +191,7 @@ SyntaxTestTool::Request SyntaxTestTool::handleResponse(bool const _parserError)
file << m_test->source();
file << "// ----" << endl;
if (!m_test->errorList().empty())
- m_test->printErrorList(file, m_test->errorList(), "// ", false, false, false);
+ m_test->printErrorList(file, m_test->errorList(), "// ", false);
return Request::Rerun;
}
case 'e':
@@ -231,8 +244,8 @@ SyntaxTestStats SyntaxTestTool::processPath(
switch(result)
{
case Result::Failure:
- case Result::ParserError:
- switch(testTool.handleResponse(result == Result::ParserError))
+ case Result::Exception:
+ switch(testTool.handleResponse(result == Result::Exception))
{
case Request::Quit:
return { successCount, runCount };