Merge remote-tracking branch 'origin/develop' into breaking

This commit is contained in:
chriseth 2020-04-22 17:21:32 +02:00
commit 6728e3ef94
270 changed files with 5249 additions and 1817 deletions

View File

@ -338,7 +338,7 @@ jobs:
chk_proofs: chk_proofs:
docker: docker:
- image: buildpack-deps:disco - image: buildpack-deps:latest
environment: environment:
TERM: xterm TERM: xterm
steps: steps:
@ -347,8 +347,8 @@ jobs:
name: Z3 python deps name: Z3 python deps
command: | command: |
apt-get -qq update apt-get -qq update
apt-get -qy install python-pip apt-get -qy install python3-pip
pip install --user z3-solver pip3 install --user z3-solver
- run: *run_proofs - run: *run_proofs
chk_docs_pragma_min_version: chk_docs_pragma_min_version:
@ -661,7 +661,7 @@ jobs:
t_ems_solcjs: t_ems_solcjs:
docker: docker:
- image: ethereum/solidity-buildpack-deps:ubuntu1904 - image: buildpack-deps:latest
environment: environment:
TERM: xterm TERM: xterm
steps: steps:

View File

@ -52,8 +52,8 @@ then
rm -rf z3-4.8.7-x64-osx-10.14.6 rm -rf z3-4.8.7-x64-osx-10.14.6
# evmone # evmone
wget https://github.com/ethereum/evmone/releases/download/v0.3.0/evmone-0.3.0-darwin-x86_64.tar.gz wget https://github.com/ethereum/evmone/releases/download/v0.4.0/evmone-0.4.0-darwin-x86_64.tar.gz
tar xzpf evmone-0.3.0-darwin-x86_64.tar.gz -C /usr/local tar xzpf evmone-0.4.0-darwin-x86_64.tar.gz -C /usr/local
rm -f evmone-0.3.0-darwin-x86_64.tar.gz rm -f evmone-0.4.0-darwin-x86_64.tar.gz
fi fi

View File

@ -12,7 +12,7 @@ Compiler Features:
Bugfixes: Bugfixes:
### 0.6.6 (unreleased) ### 0.6.7 (unreleased)
Language Features: Language Features:
@ -21,7 +21,25 @@ Compiler Features:
Bugfixes: Bugfixes:
* Type Checker: Disallow ``virtual`` and ``override`` for constructors.
* Type Checker: Fix several internal errors by performing size and recursiveness checks of types before the full type checking.
* Type Checker: Perform recursiveness check on structs declared at the file level.
Build System:
* soltest.sh: ``SOLIDITY_BUILD_DIR`` is no longer relative to ``REPO_ROOT`` to allow for build directories outside of the source tree.
### 0.6.6 (2020-04-09)
Important Bugfixes:
* Fix tuple assignments with components occupying multiple stack slots and different stack size on left- and right-hand-side.
Bugfixes:
* AST export: Export `immutable` property in the field `mutability`.
* SMTChecker: Fix internal error in the CHC engine when calling inherited functions internally. * SMTChecker: Fix internal error in the CHC engine when calling inherited functions internally.
* Type Checker: Error when trying to encode functions with call options gas and value set.

622
docs/_static/css/dark.css vendored Normal file
View File

@ -0,0 +1,622 @@
/* links */
a,
a:visited {
color: #aaddff;
}
/* code directives */
.method dt,
.class dt,
.data dt,
.attribute dt,
.function dt,
.classmethod dt,
.exception dt,
.descclassname,
.descname {
background-color: #2d2d2d !important;
}
.rst-content dl:not(.docutils) dt {
color: #aaddff;
background-color: #2d2d2d;
border-top: solid 3px #525252;
border-left: solid 3px #525252;
}
em.property {
color: #888888;
}
/* tables */
.rst-content table.docutils thead {
color: #ddd;
}
.rst-content table.docutils td {
border: 0px;
}
.rst-content table.docutils:not(.field-list) tr:nth-child(2n-1) td {
background-color: #5a5a5a;
}
/* inlined code highlights */
.xref,
.py-meth,
.rst-content a code {
color: #aaddff !important;
font-weight: normal !important;
}
.rst-content code {
color: #eee !important;
font-weight: normal !important;
}
code.literal {
background-color: #2d2d2d !important;
border: 1px solid #6d6d6d !important;
}
code.docutils.literal.notranslate {
color: #ddd;
}
/* notes, warnings, hints */
.hint .admonition-title {
background: #2aa87c !important;
}
.warning .admonition-title {
background: #cc4444 !important;
}
.admonition-title {
background: #3a7ca8 !important;
}
.admonition,
.note {
background-color: #2d2d2d !important;
}
/* table of contents */
.wy-nav-content-wrap {
background-color: rgba(0, 0, 0, 0.6) !important;
}
.sidebar {
background-color: #191919 !important;
}
.sidebar-title {
background-color: #2b2b2b !important;
}
.wy-menu-vertical a {
color: #ddd;
}
.wy-menu-vertical code.docutils.literal.notranslate {
color: #404040;
background: none !important;
border: none !important;
}
.wy-nav-content {
background: #3c3c3c;
color: #dddddd;
}
.wy-menu-vertical li.on a,
.wy-menu-vertical li.current>a {
background: #a3a3a3;
border-bottom: 0px !important;
border-top: 0px !important;
}
.wy-menu-vertical li.current {
background: #b3b3b3;
}
.toc-backref {
color: grey !important;
}
.highlight .hll {
background-color: #49483e
}
.highlight {
background: #222;
color: #f8f8f2
}
.highlight .c {
color: #888
}
/* Comment */
.highlight .err {
color: #960050;
background-color: #1e0010
}
/* Error */
.highlight .k {
color: #66d9ef
}
/* Keyword */
.highlight .l {
color: #ae81ff
}
/* Literal */
.highlight .n {
color: #f8f8f2
}
/* Name */
.highlight .o {
color: #f92672
}
/* Operator */
.highlight .p {
color: #f8f8f2
}
/* Punctuation */
.highlight .ch {
color: #888
}
/* Comment.Hashbang */
.highlight .cm {
color: #888
}
/* Comment.Multiline */
.highlight .cp {
color: #888
}
/* Comment.Preproc */
.highlight .cpf {
color: #888
}
/* Comment.PreprocFile */
.highlight .c1 {
color: #888
}
/* Comment.Single */
.highlight .cs {
color: #888
}
/* Comment.Special */
.highlight .gd {
color: #f92672
}
/* Generic.Deleted */
.highlight .ge {
font-style: italic
}
/* Generic.Emph */
.highlight .gi {
color: #a6e22e
}
/* Generic.Inserted */
.highlight .gs {
font-weight: bold
}
/* Generic.Strong */
.highlight .gu {
color: #888
}
/* Generic.Subheading */
.highlight .kc {
color: #66d9ef
}
/* Keyword.Constant */
.highlight .kd {
color: #66d9ef
}
/* Keyword.Declaration */
.highlight .kn {
color: #f92672
}
/* Keyword.Namespace */
.highlight .kp {
color: #66d9ef
}
/* Keyword.Pseudo */
.highlight .kr {
color: #66d9ef
}
/* Keyword.Reserved */
.highlight .kt {
color: #66d9ef
}
/* Keyword.Type */
.highlight .ld {
color: #e6db74
}
/* Literal.Date */
.highlight .m {
color: #ae81ff
}
/* Literal.Number */
.highlight .s {
color: #e6db74
}
/* Literal.String */
.highlight .na {
color: #a6e22e
}
/* Name.Attribute */
.highlight .nb {
color: #f8f8f2
}
/* Name.Builtin */
.highlight .nc {
color: #a6e22e
}
/* Name.Class */
.highlight .no {
color: #66d9ef
}
/* Name.Constant */
.highlight .nd {
color: #a6e22e
}
/* Name.Decorator */
.highlight .ni {
color: #f8f8f2
}
/* Name.Entity */
.highlight .ne {
color: #a6e22e
}
/* Name.Exception */
.highlight .nf {
color: #a6e22e
}
/* Name.Function */
.highlight .nl {
color: #f8f8f2
}
/* Name.Label */
.highlight .nn {
color: #f8f8f2
}
/* Name.Namespace */
.highlight .nx {
color: #a6e22e
}
/* Name.Other */
.highlight .py {
color: #f8f8f2
}
/* Name.Property */
.highlight .nt {
color: #f92672
}
/* Name.Tag */
.highlight .nv {
color: #f8f8f2
}
/* Name.Variable */
.highlight .ow {
color: #f92672
}
/* Operator.Word */
.highlight .w {
color: #f8f8f2
}
/* Text.Whitespace */
.highlight .mb {
color: #ae81ff
}
/* Literal.Number.Bin */
.highlight .mf {
color: #ae81ff
}
/* Literal.Number.Float */
.highlight .mh {
color: #ae81ff
}
/* Literal.Number.Hex */
.highlight .mi {
color: #ae81ff
}
/* Literal.Number.Integer */
.highlight .mo {
color: #ae81ff
}
/* Literal.Number.Oct */
.highlight .sa {
color: #e6db74
}
/* Literal.String.Affix */
.highlight .sb {
color: #e6db74
}
/* Literal.String.Backtick */
.highlight .sc {
color: #e6db74
}
/* Literal.String.Char */
.highlight .dl {
color: #e6db74
}
/* Literal.String.Delimiter */
.highlight .sd {
color: #e6db74
}
/* Literal.String.Doc */
.highlight .s2 {
color: #e6db74
}
/* Literal.String.Double */
.highlight .se {
color: #ae81ff
}
/* Literal.String.Escape */
.highlight .sh {
color: #e6db74
}
/* Literal.String.Heredoc */
.highlight .si {
color: #e6db74
}
/* Literal.String.Interpol */
.highlight .sx {
color: #e6db74
}
/* Literal.String.Other */
.highlight .sr {
color: #e6db74
}
/* Literal.String.Regex */
.highlight .s1 {
color: #e6db74
}
/* Literal.String.Single */
.highlight .ss {
color: #e6db74
}
/* Literal.String.Symbol */
.highlight .bp {
color: #f8f8f2
}
/* Name.Builtin.Pseudo */
.highlight .fm {
color: #a6e22e
}
/* Name.Function.Magic */
.highlight .vc {
color: #f8f8f2
}
/* Name.Variable.Class */
.highlight .vg {
color: #f8f8f2
}
/* Name.Variable.Global */
.highlight .vi {
color: #f8f8f2
}
/* Name.Variable.Instance */
.highlight .vm {
color: #f8f8f2
}
/* Name.Variable.Magic */
.highlight .il {
color: #ae81ff
}
/* Literal.Number.Integer.Long */

77
docs/_static/css/toggle.css vendored Normal file
View File

@ -0,0 +1,77 @@
input[type=checkbox] {
visibility: hidden;
height: 0;
width: 0;
margin: 0;
}
.rst-versions .rst-current-version {
padding: 10px;
display: flex;
justify-content: space-between;
}
.rst-versions .rst-current-version .fa-book,
.rst-versions .rst-current-version .fa-v,
.rst-versions .rst-current-version .fa-caret-down {
height: 24px;
line-height: 24px;
vertical-align: middle;
}
.rst-versions .rst-current-version .fa-element {
width: 80px;
text-align: center;
}
.rst-versions .rst-current-version .fa-book {
text-align: left;
}
.rst-versions .rst-current-version .fa-v {
color: #27AE60;
text-align: right;
}
label {
margin: 0 auto;
display: inline-block;
justify-content: center;
align-items: right;
border-radius: 100px;
position: relative;
cursor: pointer;
text-indent: -9999px;
width: 50px;
height: 21px;
background: #000;
}
label:after {
border-radius: 50%;
position: absolute;
content: '';
background: #fff;
width: 15px;
height: 15px;
top: 3px;
left: 3px;
transition: ease-in-out 200ms;
}
input:checked+label {
background: #3a7ca8;
}
input:checked+label:after {
left: calc(100% - 5px);
transform: translateX(-100%);
}
html.transition,
html.transition *,
html.transition *:before,
html.transition *:after {
transition: ease-in-out 200ms !important;
transition-delay: 0 !important;
}

38
docs/_static/js/toggle.js vendored Normal file
View File

@ -0,0 +1,38 @@
document.addEventListener('DOMContentLoaded', function() {
function toggleCssMode(isDay) {
var mode = (isDay ? "Day" : "Night");
localStorage.setItem("css-mode", mode);
var daysheet = $('link[href="_static/pygments.css"]')[0].sheet;
daysheet.disabled = !isDay;
var nightsheet = $('link[href="_static/css/dark.css"]')[0];
if (!isDay && nightsheet === undefined) {
var element = document.createElement("link");
element.setAttribute("rel", "stylesheet");
element.setAttribute("type", "text/css");
element.setAttribute("href", "_static/css/dark.css");
document.getElementsByTagName("head")[0].appendChild(element);
return;
}
if (nightsheet !== undefined) {
nightsheet.sheet.disabled = isDay;
}
}
var initial = localStorage.getItem("css-mode") != "Night";
var checkbox = document.querySelector('input[name=mode]');
toggleCssMode(initial);
checkbox.checked = initial;
checkbox.addEventListener('change', function() {
document.documentElement.classList.add('transition');
window.setTimeout(() => {
document.documentElement.classList.remove('transition');
}, 1000)
toggleCssMode(this.checked);
})
});

36
docs/_templates/versions.html vendored Normal file
View File

@ -0,0 +1,36 @@
{# Add rst-badge after rst-versions for small badge style. #}
<div class="rst-versions" data-toggle="rst-versions" role="note" aria-label="versions">
<span class="rst-current-version" data-toggle="rst-current-version">
<span class="fa fa-book fa-element"> RTD </span>
<span class="fa fa-element">
<input class="container_toggle" type="checkbox" id="switch" name="mode">
<label for="switch"></label>
</span>
<span class="fa fa-v fa-element"> v: {{ current_version }} <span class="fa fa-caret-down"></span></span>
</span>
<div class="rst-other-versions">
<dl>
<dt>{{ _('Versions') }}</dt> {% for slug, url in versions %}
<dd><a href="{{ url }}">{{ slug }}</a></dd>
{% endfor %}
</dl>
<dl>
<dt>{{ _('Downloads') }}</dt> {% for type, url in downloads %}
<dd><a href="{{ url }}">{{ type }}</a></dd>
{% endfor %}
</dl>
<dl>
{# Translators: The phrase "Read the Docs" is not translated #}
<dt>{{ _('On Read the Docs') }}</dt>
<dd>
<a href="//{{ PRODUCTION_DOMAIN }}/projects/{{ slug }}/?fromdocs={{ slug }}">{{ _('Project Home') }}</a>
</dd>
<dd>
<a href="//{{ PRODUCTION_DOMAIN }}/builds/{{ slug }}/?fromdocs={{ slug }}">{{ _('Builds') }}</a>
</dd>
</dl>
</div>
</div>

View File

@ -202,7 +202,7 @@ on the type of ``X`` being
- ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order - ``uint<M>``: ``enc(X)`` is the big-endian encoding of ``X``, padded on the higher-order
(left) side with zero-bytes such that the length is 32 bytes. (left) side with zero-bytes such that the length is 32 bytes.
- ``address``: as in the ``uint160`` case - ``address``: as in the ``uint160`` case
- ``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. - ``int<M>``: ``enc(X)`` is the big-endian two's complement encoding of ``X``, padded on the higher-order (left) side with ``0xff`` bytes for negative ``X`` and with zero-bytes for non-negative ``X`` such that the length is 32 bytes.
- ``bool``: as in the ``uint8`` case, where ``1`` is used for ``true`` and ``0`` for ``false`` - ``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<M>x<N>``: ``enc(X)`` is ``enc(X * 10**N)`` where ``X * 10**N`` is interpreted as a ``int256``.
- ``fixed``: as in the ``fixed128x18`` case - ``fixed``: as in the ``fixed128x18`` case

View File

@ -1,4 +1,12 @@
[ [
{
"name": "TupleAssignmentMultiStackSlotComponents",
"summary": "Tuple assignments with components that occupy several stack slots, i.e. nested tuples, pointers to external functions or references to dynamically sized calldata arrays, can result in invalid values.",
"description": "Tuple assignments did not correctly account for tuple components that occupy multiple stack slots in case the number of stack slots differs between left-hand-side and right-hand-side. This can either happen in the presence of nested tuples or if the right-hand-side contains external function pointers or references to dynamic calldata arrays, while the left-hand-side contains an omission.",
"introduced": "0.1.6",
"fixed": "0.6.6",
"severity": "very low"
},
{ {
"name": "MemoryArrayCreationOverflow", "name": "MemoryArrayCreationOverflow",
"summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.", "summary": "The creation of very large memory arrays can result in overlapping memory regions and thus memory corruption.",

View File

@ -111,6 +111,7 @@
}, },
"0.1.6": { "0.1.6": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"ExpExponentCleanup", "ExpExponentCleanup",
"NestedArrayFunctionCallDecoder", "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
@ -131,6 +132,7 @@
}, },
"0.1.7": { "0.1.7": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"ExpExponentCleanup", "ExpExponentCleanup",
"NestedArrayFunctionCallDecoder", "NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector", "ZeroFunctionSelector",
@ -151,6 +153,7 @@
}, },
"0.2.0": { "0.2.0": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"ExpExponentCleanup", "ExpExponentCleanup",
"NestedArrayFunctionCallDecoder", "NestedArrayFunctionCallDecoder",
@ -172,6 +175,7 @@
}, },
"0.2.1": { "0.2.1": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"ExpExponentCleanup", "ExpExponentCleanup",
"NestedArrayFunctionCallDecoder", "NestedArrayFunctionCallDecoder",
@ -193,6 +197,7 @@
}, },
"0.2.2": { "0.2.2": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"ExpExponentCleanup", "ExpExponentCleanup",
"NestedArrayFunctionCallDecoder", "NestedArrayFunctionCallDecoder",
@ -214,6 +219,7 @@
}, },
"0.3.0": { "0.3.0": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -237,6 +243,7 @@
}, },
"0.3.1": { "0.3.1": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -259,6 +266,7 @@
}, },
"0.3.2": { "0.3.2": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -281,6 +289,7 @@
}, },
"0.3.3": { "0.3.3": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -302,6 +311,7 @@
}, },
"0.3.4": { "0.3.4": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -323,6 +333,7 @@
}, },
"0.3.5": { "0.3.5": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -344,6 +355,7 @@
}, },
"0.3.6": { "0.3.6": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -363,6 +375,7 @@
}, },
"0.4.0": { "0.4.0": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -382,6 +395,7 @@
}, },
"0.4.1": { "0.4.1": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -401,6 +415,7 @@
}, },
"0.4.10": { "0.4.10": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -418,6 +433,7 @@
}, },
"0.4.11": { "0.4.11": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -434,6 +450,7 @@
}, },
"0.4.12": { "0.4.12": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -449,6 +466,7 @@
}, },
"0.4.13": { "0.4.13": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -464,6 +482,7 @@
}, },
"0.4.14": { "0.4.14": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -478,6 +497,7 @@
}, },
"0.4.15": { "0.4.15": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -491,6 +511,7 @@
}, },
"0.4.16": { "0.4.16": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -506,6 +527,7 @@
}, },
"0.4.17": { "0.4.17": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -522,6 +544,7 @@
}, },
"0.4.18": { "0.4.18": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -537,6 +560,7 @@
}, },
"0.4.19": { "0.4.19": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -553,6 +577,7 @@
}, },
"0.4.2": { "0.4.2": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -571,6 +596,7 @@
}, },
"0.4.20": { "0.4.20": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -587,6 +613,7 @@
}, },
"0.4.21": { "0.4.21": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -603,6 +630,7 @@
}, },
"0.4.22": { "0.4.22": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -619,6 +647,7 @@
}, },
"0.4.23": { "0.4.23": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -634,6 +663,7 @@
}, },
"0.4.24": { "0.4.24": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -649,6 +679,7 @@
}, },
"0.4.25": { "0.4.25": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -662,6 +693,7 @@
}, },
"0.4.26": { "0.4.26": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -672,6 +704,7 @@
}, },
"0.4.3": { "0.4.3": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -689,6 +722,7 @@
}, },
"0.4.4": { "0.4.4": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x", "IncorrectEventSignatureInLibraries_0.4.x",
@ -705,6 +739,7 @@
}, },
"0.4.5": { "0.4.5": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x", "UninitializedFunctionPointerInConstructor_0.4.x",
@ -723,6 +758,7 @@
}, },
"0.4.6": { "0.4.6": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x", "UninitializedFunctionPointerInConstructor_0.4.x",
@ -740,6 +776,7 @@
}, },
"0.4.7": { "0.4.7": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -757,6 +794,7 @@
}, },
"0.4.8": { "0.4.8": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -774,6 +812,7 @@
}, },
"0.4.9": { "0.4.9": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -791,6 +830,7 @@
}, },
"0.5.0": { "0.5.0": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -804,6 +844,7 @@
}, },
"0.5.1": { "0.5.1": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -817,6 +858,7 @@
}, },
"0.5.10": { "0.5.10": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5", "YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -826,6 +868,7 @@
}, },
"0.5.11": { "0.5.11": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5" "YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -834,6 +877,7 @@
}, },
"0.5.12": { "0.5.12": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5" "YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -842,6 +886,7 @@
}, },
"0.5.13": { "0.5.13": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5" "YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -850,6 +895,7 @@
}, },
"0.5.14": { "0.5.14": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5", "YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -859,6 +905,7 @@
}, },
"0.5.15": { "0.5.15": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5" "YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -867,6 +914,7 @@
}, },
"0.5.16": { "0.5.16": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden" "privateCanBeOverridden"
], ],
@ -874,12 +922,14 @@
}, },
"0.5.17": { "0.5.17": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
"released": "2020-03-17" "released": "2020-03-17"
}, },
"0.5.2": { "0.5.2": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -893,6 +943,7 @@
}, },
"0.5.3": { "0.5.3": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -906,6 +957,7 @@
}, },
"0.5.4": { "0.5.4": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -919,6 +971,7 @@
}, },
"0.5.5": { "0.5.5": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"SignedArrayStorageCopy", "SignedArrayStorageCopy",
@ -934,6 +987,7 @@
}, },
"0.5.6": { "0.5.6": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
@ -949,6 +1003,7 @@
}, },
"0.5.7": { "0.5.7": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers", "ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
@ -962,6 +1017,7 @@
}, },
"0.5.8": { "0.5.8": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5", "YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -974,6 +1030,7 @@
}, },
"0.5.9": { "0.5.9": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"privateCanBeOverridden", "privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5", "YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -985,6 +1042,7 @@
}, },
"0.6.0": { "0.6.0": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow", "MemoryArrayCreationOverflow",
"YulOptimizerRedundantAssignmentBreakContinue" "YulOptimizerRedundantAssignmentBreakContinue"
], ],
@ -992,30 +1050,40 @@
}, },
"0.6.1": { "0.6.1": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
"released": "2020-01-02" "released": "2020-01-02"
}, },
"0.6.2": { "0.6.2": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
"released": "2020-01-27" "released": "2020-01-27"
}, },
"0.6.3": { "0.6.3": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
"released": "2020-02-18" "released": "2020-02-18"
}, },
"0.6.4": { "0.6.4": {
"bugs": [ "bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow" "MemoryArrayCreationOverflow"
], ],
"released": "2020-03-10" "released": "2020-03-10"
}, },
"0.6.5": { "0.6.5": {
"bugs": [], "bugs": [
"TupleAssignmentMultiStackSlotComponents"
],
"released": "2020-04-06" "released": "2020-04-06"
},
"0.6.6": {
"bugs": [],
"released": "2020-04-09"
} }
} }

View File

@ -146,10 +146,14 @@ html_theme = 'sphinx_rtd_theme'
# so a file named "default.css" will overwrite the builtin "default.css". # so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static'] html_static_path = ['_static']
html_css_files = ["css/toggle.css"]
html_js_files = ["js/toggle.js"]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
# directly to the root of the documentation. # directly to the root of the documentation.
#html_extra_path = [] html_extra_path = ["_static/css"]
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format. # using the given strftime format.

View File

@ -195,7 +195,7 @@ In addition to the list of state modifying statements explained above, the follo
} }
} }
Pure functions are able to use the `revert()` and `require()` functions to revert Pure functions are able to use the ``revert()`` and ``require()`` functions to revert
potential state changes when an :ref:`error occurs <assert-and-require>`. potential state changes when an :ref:`error occurs <assert-and-require>`.
Reverting a state change is not considered a "state modification", as only changes to the Reverting a state change is not considered a "state modification", as only changes to the
@ -235,9 +235,9 @@ A contract can have at most one ``receive`` function, declared using
``receive() external payable { ... }`` ``receive() external payable { ... }``
(without the ``function`` keyword). (without the ``function`` keyword).
This function cannot have arguments, cannot return anything and must have This function cannot have arguments, cannot return anything and must have
``external`` visibility and ``payable`` state mutability. It is executed on a ``external`` visibility and ``payable`` state mutability. It is executed on a
call to the contract with empty calldata. This is the function that is executed call to the contract with empty calldata. This is the function that is executed
on plain Ether transfers (e.g. via `.send()` or `.transfer()`). If no such on plain Ether transfers (e.g. via ``.send()`` or ``.transfer()``). If no such
function exists, but a payable :ref:`fallback function <fallback-function>` function exists, but a payable :ref:`fallback function <fallback-function>`
exists, the fallback function will be called on a plain Ether transfer. If exists, the fallback function will be called on a plain Ether transfer. If
neither a receive Ether nor a payable fallback function is present, the neither a receive Ether nor a payable fallback function is present, the
@ -245,7 +245,7 @@ contract cannot receive Ether through regular transactions and throws an
exception. exception.
In the worst case, the fallback function can only rely on 2300 gas being In the worst case, the fallback function can only rely on 2300 gas being
available (for example when `send` or `transfer` is used), leaving little available (for example when ``send`` or ``transfer`` is used), leaving little
room to perform other operations except basic logging. The following operations room to perform other operations except basic logging. The following operations
will consume more gas than the 2300 gas stipend: will consume more gas than the 2300 gas stipend:
@ -265,7 +265,7 @@ will consume more gas than the 2300 gas stipend:
.. warning:: .. warning::
A contract without a receive Ether function can receive Ether as a A contract without a receive Ether function can receive Ether as a
recipient of a `coinbase transaction` (aka `miner block reward`) recipient of a *coinbase transaction* (aka *miner block reward*)
or as a destination of a ``selfdestruct``. or as a destination of a ``selfdestruct``.
A contract cannot react to such Ether transfers and thus also A contract cannot react to such Ether transfers and thus also

View File

@ -16,7 +16,7 @@ Functions have to be specified as being ``external``,
``public``, ``internal`` or ``private``. ``public``, ``internal`` or ``private``.
For state variables, ``external`` is not possible. For state variables, ``external`` is not possible.
``external``: ``external``
External functions are part of the contract interface, External functions are part of the contract interface,
which means they can be called from other contracts and which means they can be called from other contracts and
via transactions. An external function ``f`` cannot be called via transactions. An external function ``f`` cannot be called
@ -25,18 +25,18 @@ For state variables, ``external`` is not possible.
they receive large arrays of data, because the data they receive large arrays of data, because the data
is not copied from calldata to memory. is not copied from calldata to memory.
``public``: ``public``
Public functions are part of the contract interface Public functions are part of the contract interface
and can be either called internally or via and can be either called internally or via
messages. For public state variables, an automatic getter messages. For public state variables, an automatic getter
function (see below) is generated. function (see below) is generated.
``internal``: ``internal``
Those functions and state variables can only be Those functions and state variables can only be
accessed internally (i.e. from within the current contract accessed internally (i.e. from within the current contract
or contracts deriving from it), without using ``this``. or contracts deriving from it), without using ``this``.
``private``: ``private``
Private functions and state variables are only Private functions and state variables are only
visible for the contract they are defined in and not in visible for the contract they are defined in and not in
derived contracts. derived contracts.

View File

@ -81,26 +81,55 @@ Thank you for your help!
Running the compiler tests Running the compiler tests
========================== ==========================
The ``./scripts/tests.sh`` script executes most Solidity tests automatically, Prerequisites
but for quicker feedback, you might want to run specific tests. -------------
Some tests require the `evmone <https://github.com/ethereum/evmone/releases>`_
library, others require `libz3 <https://github.com/Z3Prover/z3>`_. The test script
tries to discover the location of the ``evmone`` library, which can be located
in the current directory, installed on the system level, or the ``deps`` folder
in the project top level. The required file is called ``libevmone.so`` on Linux
systems, ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS.
Running the tests
-----------------
Solidity includes different types of tests, most of them bundled into the Solidity includes different types of tests, most of them bundled into the
`Boost C++ Test Framework <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html>`_ application ``soltest``. `Boost C++ Test Framework <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html>`_ application ``soltest``.
Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes. Running ``build/test/soltest`` or its wrapper ``scripts/soltest.sh`` is sufficient for most changes.
Some tests require the ``evmone`` library, others require ``libz3``. The ``./scripts/tests.sh`` script executes most Solidity tests automatically,
including those bundled into the `Boost C++ Test Framework <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/index.html>`_ application ``soltest`` (or its wrapper ``scripts/soltest.sh``),
as well as command line tests and compilation tests.
The test system will automatically try to discover the location of the ``evmone`` library The test system automatically tries try to discover the location of the ``evmone`` library
starting from the current directory. The required file is called ``libevmone.so`` on Linux systems, starting from the current directory. The required file is called ``libevmone.so`` on Linux systems,
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests ``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that
are skipped. To run all tests, download the library from use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``,
`Github <https://github.com/ethereum/evmone/releases/tag/v0.3.0>`_ ``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
and either place it in the project root path or inside the ``deps`` folder. `GitHub <https://github.com/ethereum/evmone/releases/tag/v0.4.1>`_
and place it in the project root path or inside the ``deps`` folder.
If you do not have libz3 installed on your system, you should disable the SMT tests: If the ``libz3`` library is not installed on your system, you should disable the
``./scripts/soltest.sh --no-smt``. SMT tests by exporting ``SMT_FLAGS=--no-smt`` before running ``./scripts/tests.sh`` or
running ``./scripts/soltest.sh --no-smt``.
These tests are ``libsolidity/smtCheckerTests`` and ``libsolidity/smtCheckerTestsJSON``.
.. note ::
To get a list of all unit tests run by Soltest, run ``./build/test/soltest --list_content=HRF``.
For quicker results you can run a subset of, or specific tests.
To run a subset of tests, you can use filters:
``./scripts/soltest.sh -t TestSuite/TestName``,
where ``TestName`` can be a wildcard ``*``.
Or, for example, to run all the tests for the yul disambiguator:
``./scripts/soltest.sh -t "yulOptimizerTests/disambiguator/*" --no-smt``.
``./build/test/soltest --help`` has extensive help on all of the options available. ``./build/test/soltest --help`` has extensive help on all of the options available.
See especially: See especially:
- `show_progress (-p) <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/show_progress.html>`_ to show test completion, - `show_progress (-p) <https://www.boost.org/doc/libs/1_69_0/libs/test/doc/html/boost_test/utf_reference/rt_param_reference/show_progress.html>`_ to show test completion,
@ -109,20 +138,10 @@ See especially:
.. note :: .. note ::
Those working in a Windows environment wanting to run the above basic sets without libz3 in Git Bash, you would have to do: ``./build/test/Release/soltest.exe -- --no-smt``. Those working in a Windows environment wanting to run the above basic sets
without libz3. Using Git Bash, you use: ``./build/test/Release/soltest.exe -- --no-smt``.
If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``. If you are running this in plain Command Prompt, use ``.\build\test\Release\soltest.exe -- --no-smt``.
To run a subset of tests, you can use filters:
``./scripts/soltest.sh -t TestSuite/TestName``,
where ``TestName`` can be a wildcard ``*``.
For example, here is an example test you might run;
``./scripts/soltest.sh -t "yulOptimizerTests/disambiguator/*" --no-smt``.
This will test all the tests for the disambiguator.
To get a list of all tests, use
``./build/test/soltest --list_content=HRF``.
If you want to debug using GDB, make sure you build differently than the "usual". If you want to debug using GDB, make sure you build differently than the "usual".
For example, you could run the following command in your ``build`` folder: For example, you could run the following command in your ``build`` folder:
:: ::
@ -130,14 +149,11 @@ For example, you could run the following command in your ``build`` folder:
cmake -DCMAKE_BUILD_TYPE=Debug .. cmake -DCMAKE_BUILD_TYPE=Debug ..
make make
This will create symbols such that when you debug a test using the ``--debug`` flag, you will have access to functions and variables in which you can break or print with. This creates symbols so that when you debug a test using the ``--debug`` flag,
you have access to functions and variables in which you can break or print with.
The script ``./scripts/tests.sh`` also runs commandline tests and compilation tests
in addition to those found in ``soltest``.
The CI runs additional tests (including ``solc-js`` and testing third party Solidity frameworks) that require compiling the Emscripten target.
The CI runs additional tests (including ``solc-js`` and testing third party Solidity
frameworks) that require compiling the Emscripten target.
Writing and running syntax tests Writing and running syntax tests
-------------------------------- --------------------------------

View File

@ -433,7 +433,7 @@ The full contract
.. note:: .. note::
The function ``splitSignature`` does not use all security The function ``splitSignature`` does not use all security
checks. A real implementation should use a more rigorously tested library, checks. A real implementation should use a more rigorously tested library,
such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code. such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol>`_ of this code.
Verifying Payments Verifying Payments
------------------ ------------------
@ -454,7 +454,7 @@ The recipient should verify each message using the following process:
We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_ We'll use the `ethereumjs-util <https://github.com/ethereumjs/ethereumjs-util>`_
library to write this verification. The final step can be done a number of ways, library to write this verification. The final step can be done a number of ways,
and we use JavaScript. The following code borrows the `constructMessage` function from the signing **JavaScript code** above: and we use JavaScript. The following code borrows the ``constructMessage`` function from the signing **JavaScript code** above:
:: ::

View File

@ -35,11 +35,11 @@ or if you require more compilation options.
npm / Node.js npm / Node.js
============= =============
Use `npm` for a convenient and portable way to install `solcjs`, a Solidity compiler. The Use ``npm`` for a convenient and portable way to install ``solcjs``, a Solidity compiler. The
`solcjs` program has fewer features than the ways to access the compiler described `solcjs` program has fewer features than the ways to access the compiler described
further down this page. The further down this page. The
:ref:`commandline-compiler` documentation assumes you are using :ref:`commandline-compiler` documentation assumes you are using
the full-featured compiler, `solc`. The usage of `solcjs` is documented inside its own the full-featured compiler, ``solc``. The usage of ``solcjs`` is documented inside its own
`repository <https://github.com/ethereum/solc-js>`_. `repository <https://github.com/ethereum/solc-js>`_.
Note: The solc-js project is derived from the C++ Note: The solc-js project is derived from the C++
@ -53,10 +53,10 @@ Please refer to the solc-js repository for instructions.
.. note:: .. note::
The commandline executable is named `solcjs`. The commandline executable is named ``solcjs``.
The comandline options of `solcjs` are not compatible with `solc` and tools (such as `geth`) The comandline options of ``solcjs`` are not compatible with ``solc`` and tools (such as ``geth``)
expecting the behaviour of `solc` will not work with `solcjs`. expecting the behaviour of ``solc`` will not work with ``solcjs``.
Docker Docker
====== ======

View File

@ -323,7 +323,7 @@ Every account has a persistent key-value store mapping 256-bit words to 256-bit
words called **storage**. words called **storage**.
Furthermore, every account has a **balance** in Furthermore, every account has a **balance** in
Ether (in "Wei" to be exact, `1 ether` is `10**18 wei`) which can be modified by sending transactions that Ether (in "Wei" to be exact, ``1 ether`` is ``10**18 wei``) which can be modified by sending transactions that
include Ether. include Ether.
.. index:: ! transaction .. index:: ! transaction
@ -520,9 +520,9 @@ idea, but it is potentially dangerous, as if someone sends Ether to removed
contracts, the Ether is forever lost. contracts, the Ether is forever lost.
.. warning:: .. warning::
Even if a contract is removed by "selfdestruct", it is still part of the Even if a contract is removed by ``selfdestruct``, it is still part of the
history of the blockchain and probably retained by most Ethereum nodes. history of the blockchain and probably retained by most Ethereum nodes.
So using "selfdestruct" is not the same as deleting data from a hard disk. So using ``selfdestruct`` is not the same as deleting data from a hard disk.
.. note:: .. note::
Even if a contract's code does not contain a call to ``selfdestruct``, Even if a contract's code does not contain a call to ``selfdestruct``,

View File

@ -73,8 +73,8 @@ Tags
All tags are optional. The following table explains the purpose of each All tags are optional. The following table explains the purpose of each
NatSpec tag and where it may be used. As a special case, if no tags are NatSpec tag and where it may be used. As a special case, if no tags are
used then the Solidity compiler will interpret a `///` or `/**` comment used then the Solidity compiler will interpret a ``///`` or ``/**`` comment
in the same way as if it were tagged with `@notice`. in the same way as if it were tagged with ``@notice``.
=========== =============================================================================== ============================= =========== =============================================================================== =============================
Tag Context Tag Context

View File

@ -24,8 +24,11 @@ solidity code. The goal of this guide is *consistency*. A quote from python's
`pep8 <https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds>`_ `pep8 <https://www.python.org/dev/peps/pep-0008/#a-foolish-consistency-is-the-hobgoblin-of-little-minds>`_
captures this concept well. captures this concept well.
.. note::
A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important. A style guide is about consistency. Consistency with this style guide is important. Consistency within a project is more important. Consistency within one module or function is most important.
But most importantly: know when to be inconsistent -- sometimes the style guide just doesn't apply. When in doubt, use your best judgement. Look at other examples and decide what looks best. And don't hesitate to ask!
But most importantly: **know when to be inconsistent** -- sometimes the style guide just doesn't apply. When in doubt, use your best judgement. Look at other examples and decide what looks best. And don't hesitate to ask!
*********** ***********
@ -383,8 +386,7 @@ No::
function spam(uint i , Coin coin) public ; function spam(uint i , Coin coin) public ;
More than one space around an assignment or other operator to align with More than one space around an assignment or other operator to align with another:
another:
Yes:: Yes::
@ -996,7 +998,7 @@ Contract and Library Names
* Contract and library names should also match their filenames. * Contract and library names should also match their filenames.
* If a contract file includes multiple contracts and/or libraries, then the filename should match the *core contract*. This is not recommended however if it can be avoided. * If a contract file includes multiple contracts and/or libraries, then the filename should match the *core contract*. This is not recommended however if it can be avoided.
As shown in the example below, if the contract name is `Congress` and the library name is `Owned`, then their associated filenames should be `Congress.sol` and `Owned.sol`. As shown in the example below, if the contract name is ``Congress`` and the library name is ``Owned``, then their associated filenames should be ``Congress.sol`` and ``Owned.sol``.
Yes:: Yes::
@ -1132,8 +1134,8 @@ Solidity contracts can have a form of comments that are the basis of the
Ethereum Natural Language Specification Format. Ethereum Natural Language Specification Format.
Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation
of one or multiple lines starting with `///` or a of one or multiple lines starting with ``///`` or a
multiline comment starting with `/**` and ending with `*/`. multiline comment starting with ``/**`` and ending with ``*/``.
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
added looks like the one below:: added looks like the one below::

View File

@ -16,7 +16,7 @@ operators. For a quick reference of the various operators, see :ref:`order`.
The concept of "undefined" or "null" values does not exist in Solidity, but newly The concept of "undefined" or "null" values does not exist in Solidity, but newly
declared variables always have a :ref:`default value<default-value>` dependent declared variables always have a :ref:`default value<default-value>` dependent
on its type. To handle any unexpected values, you should use the :ref:`revert function<assert-and-require>` to revert the whole transaction, or return a on its type. To handle any unexpected values, you should use the :ref:`revert function<assert-and-require>` to revert the whole transaction, or return a
tuple with a second `bool` value denoting success. tuple with a second ``bool`` value denoting success.
.. include:: types/value-types.rst .. include:: types/value-types.rst
@ -26,4 +26,4 @@ tuple with a second `bool` value denoting success.
.. include:: types/operators.rst .. include:: types/operators.rst
.. include:: types/conversion.rst .. include:: types/conversion.rst

View File

@ -332,7 +332,7 @@ the :ref:`address type<address>`.
Before version 0.5.0, contracts directly derived from the address type Before version 0.5.0, contracts directly derived from the address type
and there was no distinction between ``address`` and ``address payable``. and there was no distinction between ``address`` and ``address payable``.
If you declare a local variable of contract type (`MyContract c`), you can call If you declare a local variable of contract type (``MyContract c``), you can call
functions on that contract. Take care to assign it from somewhere that is the functions on that contract. Take care to assign it from somewhere that is the
same contract type. same contract type.

View File

@ -140,15 +140,19 @@ Error Handling
See the dedicated section on :ref:`assert and require<assert-and-require>` for See the dedicated section on :ref:`assert and require<assert-and-require>` for
more details on error handling and when to use which function. more details on error handling and when to use which function.
``assert(bool condition)``: ``assert(bool condition)``
causes an invalid opcode and thus state change reversion if the condition is not met - to be used for internal errors. causes an invalid opcode and thus state change reversion if the condition is not met - to be used for internal errors.
``require(bool condition)``:
``require(bool condition)``
reverts if the condition is not met - to be used for errors in inputs or external components. reverts if the condition is not met - to be used for errors in inputs or external components.
``require(bool condition, string memory message)``:
``require(bool condition, string memory message)``
reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message. reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.
``revert()``:
``revert()``
abort execution and revert state changes abort execution and revert state changes
``revert(string memory reason)``:
``revert(string memory reason)``
abort execution and revert state changes, providing an explanatory string abort execution and revert state changes, providing an explanatory string
.. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography, .. index:: keccak256, ripemd160, sha256, ecrecover, addmod, mulmod, cryptography,
@ -156,32 +160,32 @@ more details on error handling and when to use which function.
Mathematical and Cryptographic Functions Mathematical and Cryptographic Functions
---------------------------------------- ----------------------------------------
``addmod(uint x, uint y, uint k) returns (uint)``: ``addmod(uint x, uint y, uint k) returns (uint)``
compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. compute ``(x + y) % k`` where the addition is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
``mulmod(uint x, uint y, uint k) returns (uint)``: ``mulmod(uint x, uint y, uint k) returns (uint)``
compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0. compute ``(x * y) % k`` where the multiplication is performed with arbitrary precision and does not wrap around at ``2**256``. Assert that ``k != 0`` starting from version 0.5.0.
``keccak256(bytes memory) returns (bytes32)``: ``keccak256(bytes memory) returns (bytes32)``
compute the Keccak-256 hash of the input compute the Keccak-256 hash of the input
.. note:: .. note::
There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0. There used to be an alias for ``keccak256`` called ``sha3``, which was removed in version 0.5.0.
``sha256(bytes memory) returns (bytes32)``: ``sha256(bytes memory) returns (bytes32)``
compute the SHA-256 hash of the input compute the SHA-256 hash of the input
``ripemd160(bytes memory) returns (bytes20)``: ``ripemd160(bytes memory) returns (bytes20)``
compute RIPEMD-160 hash of the input compute RIPEMD-160 hash of the input
``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``: ``ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address)``
recover the address associated with the public key from elliptic curve signature or return zero on error. recover the address associated with the public key from elliptic curve signature or return zero on error.
The function parameters correspond to ECDSA values of the signature: The function parameters correspond to ECDSA values of the signature:
``r`` = first 32 bytes of signature * ``r`` = first 32 bytes of signature
``s`` = second 32 bytes of signature * ``s`` = second 32 bytes of signature
``v`` = final 1 byte of signature * ``v`` = final 1 byte of signature
``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable<address>` for ``ecrecover`` returns an ``address``, and not an ``address payable``. See :ref:`address payable<address>` for
conversion, in case you need to transfer funds to the recovered address. conversion, in case you need to transfer funds to the recovered address.
@ -209,17 +213,22 @@ Mathematical and Cryptographic Functions
Members of Address Types Members of Address Types
------------------------ ------------------------
``<address>.balance`` (``uint256``): ``<address>.balance`` (``uint256``)
balance of the :ref:`address` in Wei balance of the :ref:`address` in Wei
``<address payable>.transfer(uint256 amount)``:
``<address payable>.transfer(uint256 amount)``
send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable send given amount of Wei to :ref:`address`, reverts on failure, forwards 2300 gas stipend, not adjustable
``<address payable>.send(uint256 amount) returns (bool)``:
``<address payable>.send(uint256 amount) returns (bool)``
send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable send given amount of Wei to :ref:`address`, returns ``false`` on failure, forwards 2300 gas stipend, not adjustable
``<address>.call(bytes memory) returns (bool, bytes memory)``:
``<address>.call(bytes memory) returns (bool, bytes memory)``
issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable issue low-level ``CALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
``<address>.delegatecall(bytes memory) returns (bool, bytes memory)``:
``<address>.delegatecall(bytes memory) returns (bool, bytes memory)``
issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable issue low-level ``DELEGATECALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
``<address>.staticcall(bytes memory) returns (bool, bytes memory)``:
``<address>.staticcall(bytes memory) returns (bool, bytes memory)``
issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable issue low-level ``STATICCALL`` with the given payload, returns success condition and return data, forwards all available gas, adjustable
For more information, see the section on :ref:`address`. For more information, see the section on :ref:`address`.
@ -258,10 +267,10 @@ For more information, see the section on :ref:`address`.
Contract Related Contract Related
---------------- ----------------
``this`` (current contract's type): ``this`` (current contract's type)
the current contract, explicitly convertible to :ref:`address` the current contract, explicitly convertible to :ref:`address`
``selfdestruct(address payable recipient)``: ``selfdestruct(address payable recipient)``
Destroy the current contract, sending its funds to the given :ref:`address` Destroy the current contract, sending its funds to the given :ref:`address`
and end execution. and end execution.
Note that ``selfdestruct`` has some peculiarities inherited from the EVM: Note that ``selfdestruct`` has some peculiarities inherited from the EVM:
@ -290,10 +299,10 @@ type ``X``. Currently, there is limited support for this feature, but
it might be expanded in the future. The following properties are it might be expanded in the future. The following properties are
available for a contract type ``C``: available for a contract type ``C``:
``type(C).name``: ``type(C).name``
The name of the contract. The name of the contract.
``type(C).creationCode``: ``type(C).creationCode``
Memory byte array that contains the creation bytecode of the contract. Memory byte array that contains the creation bytecode of the contract.
This can be used in inline assembly to build custom creation routines, This can be used in inline assembly to build custom creation routines,
especially by using the ``create2`` opcode. especially by using the ``create2`` opcode.
@ -301,7 +310,7 @@ available for a contract type ``C``:
derived contract. It causes the bytecode to be included in the bytecode derived contract. It causes the bytecode to be included in the bytecode
of the call site and thus circular references like that are not possible. of the call site and thus circular references like that are not possible.
``type(C).runtimeCode``: ``type(C).runtimeCode``
Memory byte array that contains the runtime bytecode of the contract. Memory byte array that contains the runtime bytecode of the contract.
This is the code that is usually deployed by the constructor of ``C``. This is the code that is usually deployed by the constructor of ``C``.
If ``C`` has a constructor that uses inline assembly, this might be If ``C`` has a constructor that uses inline assembly, this might be

View File

@ -106,7 +106,8 @@ Target options
Below is a list of target EVM versions and the compiler-relevant changes introduced Below is a list of target EVM versions and the compiler-relevant changes introduced
at each version. Backward compatibility is not guaranteed between each version. at each version. Backward compatibility is not guaranteed between each version.
- ``homestead`` (oldest version) - ``homestead``
- (oldest version)
- ``tangerineWhistle`` - ``tangerineWhistle``
- Gas cost for access to other accounts increased, relevant for gas estimation and the optimizer. - Gas cost for access to other accounts increased, relevant for gas estimation and the optimizer.
- All gas sent by default for external calls, previously a certain amount had to be retained. - All gas sent by default for external calls, previously a certain amount had to be retained.
@ -692,7 +693,7 @@ Review changes
The command above applies all changes as shown below. Please review them carefully. The command above applies all changes as shown below. Please review them carefully.
.. code-block:: none .. code-block:: solidity
pragma solidity >=0.6.0 <0.8.0; pragma solidity >=0.6.0 <0.8.0;

View File

@ -22,15 +22,17 @@
*/ */
#include <libevmasm/ExpressionClasses.h> #include <libevmasm/ExpressionClasses.h>
#include <utility>
#include <tuple>
#include <functional>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <libevmasm/Assembly.h> #include <libevmasm/Assembly.h>
#include <libevmasm/CommonSubexpressionEliminator.h> #include <libevmasm/CommonSubexpressionEliminator.h>
#include <libevmasm/SimplificationRules.h> #include <libevmasm/SimplificationRules.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/noncopyable.hpp>
#include <functional>
#include <tuple>
#include <utility>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::evmasm; using namespace solidity::evmasm;

View File

@ -28,6 +28,7 @@
#include <ostream> #include <ostream>
#include <tuple> #include <tuple>
#include <utility>
namespace solidity::evmasm namespace solidity::evmasm
{ {
@ -119,7 +120,7 @@ public:
struct GasConsumption struct GasConsumption
{ {
GasConsumption(unsigned _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {} GasConsumption(unsigned _value = 0, bool _infinite = false): value(_value), isInfinite(_infinite) {}
GasConsumption(u256 _value, bool _infinite = false): value(_value), isInfinite(_infinite) {} GasConsumption(u256 _value, bool _infinite = false): value(std::move(_value)), isInfinite(_infinite) {}
static GasConsumption infinite() { return GasConsumption(0, true); } static GasConsumption infinite() { return GasConsumption(0, true); }
GasConsumption& operator+=(GasConsumption const& _other); GasConsumption& operator+=(GasConsumption const& _other);
@ -133,8 +134,8 @@ public:
}; };
/// Constructs a new gas meter given the current state. /// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state, langutil::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0): GasMeter(std::shared_ptr<KnownState> _state, langutil::EVMVersion _evmVersion, u256 _largestMemoryAccess = 0):
m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {} m_state(std::move(_state)), m_evmVersion(_evmVersion), m_largestMemoryAccess(std::move(_largestMemoryAccess)) {}
/// @returns an upper bound on the gas consumed by the given instruction and updates /// @returns an upper bound on the gas consumed by the given instruction and updates
/// the state. /// the state.

View File

@ -23,6 +23,7 @@
#pragma once #pragma once
#include <utility>
#include <vector> #include <vector>
#include <map> #include <map>
#include <set> #include <set>
@ -83,7 +84,7 @@ public:
explicit KnownState( explicit KnownState(
std::shared_ptr<ExpressionClasses> _expressionClasses = std::make_shared<ExpressionClasses>() std::shared_ptr<ExpressionClasses> _expressionClasses = std::make_shared<ExpressionClasses>()
): m_expressionClasses(_expressionClasses) ): m_expressionClasses(std::move(_expressionClasses))
{ {
} }

View File

@ -55,6 +55,7 @@
#include <cstdint> #include <cstdint>
#include <string> #include <string>
#include <tuple> #include <tuple>
#include <utility>
namespace solidity::langutil namespace solidity::langutil
{ {
@ -68,8 +69,8 @@ class CharStream
{ {
public: public:
CharStream() = default; CharStream() = default;
explicit CharStream(std::string const& _source, std::string const& name): explicit CharStream(std::string _source, std::string name):
m_source(_source), m_name(name) {} m_source(std::move(_source)), m_name(std::move(name)) {}
int position() const { return m_position; } int position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); } bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }

View File

@ -22,15 +22,16 @@
#pragma once #pragma once
#include <string>
#include <utility>
#include <vector>
#include <memory>
#include <libsolutil/Exceptions.h> #include <libsolutil/Exceptions.h>
#include <libsolutil/Assertions.h> #include <libsolutil/Assertions.h>
#include <libsolutil/CommonData.h> #include <libsolutil/CommonData.h>
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <string>
#include <utility>
#include <vector>
#include <memory>
namespace solidity::langutil namespace solidity::langutil
{ {
class Error; class Error;

View File

@ -23,7 +23,9 @@
#pragma once #pragma once
#include <liblangutil/Token.h> #include <liblangutil/Token.h>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace solidity::langutil namespace solidity::langutil
@ -80,8 +82,8 @@ struct SemVerMatchExpression
class SemVerMatchExpressionParser class SemVerMatchExpressionParser
{ {
public: public:
SemVerMatchExpressionParser(std::vector<Token> const& _tokens, std::vector<std::string> const& _literals): SemVerMatchExpressionParser(std::vector<Token> _tokens, std::vector<std::string> _literals):
m_tokens(_tokens), m_literals(_literals) m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
{} {}
SemVerMatchExpression parse(); SemVerMatchExpression parse();

View File

@ -12,6 +12,8 @@ set(sources
analysis/ControlFlowGraph.h analysis/ControlFlowGraph.h
analysis/DeclarationContainer.cpp analysis/DeclarationContainer.cpp
analysis/DeclarationContainer.h analysis/DeclarationContainer.h
analysis/DeclarationTypeChecker.cpp
analysis/DeclarationTypeChecker.h
analysis/DocStringAnalyser.cpp analysis/DocStringAnalyser.cpp
analysis/DocStringAnalyser.h analysis/DocStringAnalyser.h
analysis/ImmutableValidator.cpp analysis/ImmutableValidator.cpp
@ -73,6 +75,8 @@ set(sources
codegen/LValue.h codegen/LValue.h
codegen/MultiUseYulFunctionCollector.h codegen/MultiUseYulFunctionCollector.h
codegen/MultiUseYulFunctionCollector.cpp codegen/MultiUseYulFunctionCollector.cpp
codegen/ReturnInfo.h
codegen/ReturnInfo.cpp
codegen/YulUtilFunctions.h codegen/YulUtilFunctions.h
codegen/YulUtilFunctions.cpp codegen/YulUtilFunctions.cpp
codegen/ir/IRGenerator.cpp codegen/ir/IRGenerator.cpp

View File

@ -24,6 +24,8 @@
#include <libsolidity/ast/ASTVisitor.h> #include <libsolidity/ast/ASTVisitor.h>
#include <utility>
namespace solidity::langutil namespace solidity::langutil
{ {
class ErrorReporter; class ErrorReporter;
@ -47,7 +49,7 @@ public:
): ):
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_depth(_newDepth), m_depth(_newDepth),
m_types(_types) m_types(std::move(_types))
{ {
} }

View File

@ -591,7 +591,7 @@ bool ControlFlowBuilder::visit(Identifier const& _identifier)
if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration)) if (auto const* variableDeclaration = dynamic_cast<VariableDeclaration const*>(_identifier.annotation().referencedDeclaration))
m_currentNode->variableOccurrences.emplace_back( m_currentNode->variableOccurrences.emplace_back(
*variableDeclaration, *variableDeclaration,
static_cast<Expression const&>(_identifier).annotation().lValueRequested ? static_cast<Expression const&>(_identifier).annotation().willBeWrittenTo ?
VariableOccurrence::Kind::Assignment : VariableOccurrence::Kind::Assignment :
VariableOccurrence::Kind::Access, VariableOccurrence::Kind::Access,
_identifier.location() _identifier.location()

View File

@ -26,6 +26,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <stack> #include <stack>
#include <utility>
#include <vector> #include <vector>
namespace solidity::frontend namespace solidity::frontend
@ -48,8 +49,8 @@ public:
Assignment, Assignment,
InlineAssembly InlineAssembly
}; };
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> const& _occurrence = {}): VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> _occurrence = {}):
m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence) m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(std::move(_occurrence))
{ {
} }

View File

@ -0,0 +1,382 @@
/*
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/>.
*/
#include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/TypeProvider.h>
#include <liblangutil/ErrorReporter.h>
#include <libsolutil/Algorithms.h>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace solidity::langutil;
using namespace solidity::frontend;
bool DeclarationTypeChecker::visit(ElementaryTypeName const& _typeName)
{
if (_typeName.annotation().type)
return false;
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
if (_typeName.stateMutability().has_value())
{
// for non-address types this was already caught by the parser
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
switch (*_typeName.stateMutability())
{
case StateMutability::Payable:
_typeName.annotation().type = TypeProvider::payableAddress();
break;
case StateMutability::NonPayable:
_typeName.annotation().type = TypeProvider::address();
break;
default:
typeError(
_typeName.location(),
"Address types can only be payable or non-payable."
);
break;
}
}
return true;
}
bool DeclarationTypeChecker::visit(StructDefinition const& _struct)
{
if (_struct.annotation().recursive.has_value())
{
if (!m_currentStructsSeen.empty() && *_struct.annotation().recursive)
m_recursiveStructSeen = true;
return false;
}
if (m_currentStructsSeen.count(&_struct))
{
_struct.annotation().recursive = true;
m_recursiveStructSeen = true;
return false;
}
bool previousRecursiveStructSeen = m_recursiveStructSeen;
bool hasRecursiveChild = false;
m_currentStructsSeen.insert(&_struct);
for (auto const& member: _struct.members())
{
m_recursiveStructSeen = false;
member->accept(*this);
solAssert(member->annotation().type, "");
solAssert(member->annotation().type->canBeStored(), "Type cannot be used in struct.");
if (m_recursiveStructSeen)
hasRecursiveChild = true;
}
if (!_struct.annotation().recursive.has_value())
_struct.annotation().recursive = hasRecursiveChild;
m_recursiveStructSeen = previousRecursiveStructSeen || *_struct.annotation().recursive;
m_currentStructsSeen.erase(&_struct);
if (m_currentStructsSeen.empty())
m_recursiveStructSeen = false;
// Check direct recursion, fatal error if detected.
auto visitor = [&](StructDefinition const& _struct, auto& _cycleDetector, size_t _depth)
{
if (_depth >= 256)
fatalDeclarationError(_struct.location(), "Struct definition exhausts cyclic dependency validator.");
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
{
Type const* memberType = member->annotation().type;
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
{
if (arrayType->isDynamicallySized())
break;
memberType = arrayType->baseType();
}
if (auto structType = dynamic_cast<StructType const*>(memberType))
if (_cycleDetector.run(structType->structDefinition()))
return;
}
};
if (util::CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
fatalTypeError(_struct.location(), "Recursive struct definition.");
return false;
}
void DeclarationTypeChecker::endVisit(UserDefinedTypeName const& _typeName)
{
if (_typeName.annotation().type)
return;
Declaration const* declaration = _typeName.annotation().referencedDeclaration;
solAssert(declaration, "");
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
{
if (!m_insideFunctionType && !m_currentStructsSeen.empty())
structDef->accept(*this);
_typeName.annotation().type = TypeProvider::structType(*structDef, DataLocation::Storage);
}
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
_typeName.annotation().type = TypeProvider::enumType(*enumDef);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = TypeProvider::contract(*contract);
else
{
_typeName.annotation().type = TypeProvider::emptyTuple();
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
}
bool DeclarationTypeChecker::visit(FunctionTypeName const& _typeName)
{
if (_typeName.annotation().type)
return false;
bool previousInsideFunctionType = m_insideFunctionType;
m_insideFunctionType = true;
_typeName.parameterTypeList()->accept(*this);
_typeName.returnParameterTypeList()->accept(*this);
m_insideFunctionType = previousInsideFunctionType;
switch (_typeName.visibility())
{
case Visibility::Internal:
case Visibility::External:
break;
default:
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
return false;
}
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
{
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
return false;
}
_typeName.annotation().type = TypeProvider::function(_typeName);
return false;
}
void DeclarationTypeChecker::endVisit(Mapping const& _mapping)
{
if (_mapping.annotation().type)
return;
if (auto const* typeName = dynamic_cast<UserDefinedTypeName const*>(&_mapping.keyType()))
{
if (auto const* contractType = dynamic_cast<ContractType const*>(typeName->annotation().type))
{
if (contractType->contractDefinition().isLibrary())
m_errorReporter.fatalTypeError(
typeName->location(),
"Library types cannot be used as mapping keys."
);
}
else if (typeName->annotation().type->category() != Type::Category::Enum)
m_errorReporter.fatalTypeError(
typeName->location(),
"Only elementary types, contract types or enums are allowed as mapping keys."
);
}
else
solAssert(dynamic_cast<ElementaryTypeName const*>(&_mapping.keyType()), "");
TypePointer keyType = _mapping.keyType().annotation().type;
TypePointer valueType = _mapping.valueType().annotation().type;
// Convert key type to memory.
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);
// Convert value type to storage reference.
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
}
void DeclarationTypeChecker::endVisit(ArrayTypeName const& _typeName)
{
if (_typeName.annotation().type)
return;
TypePointer baseType = _typeName.baseType().annotation().type;
if (!baseType)
{
solAssert(!m_errorReporter.errors().empty(), "");
return;
}
if (baseType->storageBytes() == 0)
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length())
{
TypePointer& lengthTypeGeneric = length->annotation().type;
if (!lengthTypeGeneric)
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
u256 lengthValue = 0;
if (!lengthType || !lengthType->mobileType())
typeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
else if (lengthType->isZero())
typeError(length->location(), "Array with zero length specified.");
else if (lengthType->isFractional())
typeError(length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
typeError(length->location(), "Array with negative length specified.");
else
lengthValue = lengthType->literalValue(nullptr);
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthValue);
}
else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
}
void DeclarationTypeChecker::endVisit(VariableDeclaration const& _variable)
{
if (_variable.annotation().type)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (_variable.immutable() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
// This can still happen in very unusual cases where a developer uses constructs, such as
// `var a;`, however, such code will have generated errors already.
// However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
// invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
// after this step.
return;
}
using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory;
set<Location> allowedDataLocations = _variable.allowedDataLocations();
if (!allowedDataLocations.count(varLoc))
{
auto locationToString = [](VariableDeclaration::Location _location) -> string
{
switch (_location)
{
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Unspecified: return "none";
}
return {};
};
string errorString;
if (!_variable.hasReferenceOrMappingType())
errorString = "Data location can only be specified for array, struct or mapping types";
else
{
errorString = "Data location must be " +
util::joinHumanReadable(
allowedDataLocations | boost::adaptors::transformed(locationToString),
", ",
" or "
);
if (_variable.isCallableOrCatchParameter())
errorString +=
" for " +
string(_variable.isReturnParameter() ? "return " : "") +
"parameter in" +
string(_variable.isExternalCallableParameter() ? " external" : "") +
" function";
else
errorString += " for variable";
}
errorString += ", but " + locationToString(varLoc) + " was given.";
typeError(_variable.location(), errorString);
solAssert(!allowedDataLocations.empty(), "");
varLoc = *allowedDataLocations.begin();
}
// Find correct data location.
if (_variable.isEventParameter())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
dynamic_cast<EnumDefinition const*>(_variable.scope())
)
// The actual location will later be changed depending on how the type is used.
typeLoc = DataLocation::Storage;
else
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
TypePointer type = _variable.typeName()->annotation().type;
if (auto ref = dynamic_cast<ReferenceType const*>(type))
{
bool isPointer = !_variable.isStateVariable();
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
}
_variable.annotation().type = type;
}
void DeclarationTypeChecker::typeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.typeError(_location, _description);
}
void DeclarationTypeChecker::fatalTypeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalTypeError(_location, _description);
}
void DeclarationTypeChecker::fatalDeclarationError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalDeclarationError(_location, _description);
}
bool DeclarationTypeChecker::check(ASTNode const& _node)
{
_node.accept(*this);
return !m_errorOccurred;
}

View File

@ -0,0 +1,79 @@
/*
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 <libsolidity/ast/ASTVisitor.h>
#include <libsolidity/ast/ASTAnnotations.h>
#include <liblangutil/EVMVersion.h>
#include <boost/noncopyable.hpp>
#include <list>
#include <map>
namespace solidity::langutil
{
class ErrorReporter;
}
namespace solidity::frontend
{
/**
* Assigns types to declarations.
*/
class DeclarationTypeChecker: private ASTConstVisitor
{
public:
DeclarationTypeChecker(
langutil::ErrorReporter& _errorReporter,
langutil::EVMVersion _evmVersion
):
m_errorReporter(_errorReporter),
m_evmVersion(_evmVersion)
{}
bool check(ASTNode const& _contract);
private:
bool visit(ElementaryTypeName const& _typeName) override;
void endVisit(UserDefinedTypeName const& _typeName) override;
bool visit(FunctionTypeName const& _typeName) override;
void endVisit(Mapping const& _mapping) override;
void endVisit(ArrayTypeName const& _typeName) override;
void endVisit(VariableDeclaration const& _variable) override;
bool visit(StructDefinition const& _struct) override;
/// Adds a new error to the list of errors.
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalDeclarationError(langutil::SourceLocation const& _location, std::string const& _description);
langutil::ErrorReporter& m_errorReporter;
bool m_errorOccurred = false;
langutil::EVMVersion m_evmVersion;
bool m_insideFunctionType = false;
bool m_recursiveStructSeen = false;
std::set<StructDefinition const*> m_currentStructsSeen;
};
}

View File

@ -160,7 +160,7 @@ void ImmutableValidator::analyseVariableReference(VariableDeclaration const& _va
if (!_variableReference.isStateVariable() || !_variableReference.immutable()) if (!_variableReference.isStateVariable() || !_variableReference.immutable())
return; return;
if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment) if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment)
{ {
if (!m_currentConstructor) if (!m_currentConstructor)
m_errorReporter.typeError( m_errorReporter.typeError(

View File

@ -195,51 +195,6 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
return nullptr; return nullptr;
} }
vector<Declaration const*> NameAndTypeResolver::cleanedDeclarations(
Identifier const& _identifier,
vector<Declaration const*> const& _declarations
)
{
solAssert(_declarations.size() > 1, "");
vector<Declaration const*> uniqueFunctions;
for (Declaration const* declaration: _declarations)
{
solAssert(declaration, "");
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
solAssert(
dynamic_cast<FunctionDefinition const*>(declaration) ||
dynamic_cast<EventDefinition const*>(declaration) ||
dynamic_cast<VariableDeclaration const*>(declaration) ||
dynamic_cast<MagicVariableDeclaration const*>(declaration),
"Found overloading involving something not a function, event or a (magic) variable."
);
FunctionTypePointer functionType { declaration->functionType(false) };
if (!functionType)
functionType = declaration->functionType(true);
solAssert(functionType, "Failed to determine the function type of the overloaded.");
for (auto parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter)
m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
if (uniqueFunctions.end() == find_if(
uniqueFunctions.begin(),
uniqueFunctions.end(),
[&](Declaration const* d)
{
FunctionType const* newFunctionType = d->functionType(false);
if (!newFunctionType)
newFunctionType = d->functionType(true);
return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
}
))
uniqueFunctions.push_back(declaration);
}
return uniqueFunctions;
}
void NameAndTypeResolver::warnVariablesNamedLikeInstructions() void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
{ {
for (auto const& instruction: evmasm::c_instructions) for (auto const& instruction: evmasm::c_instructions)

View File

@ -95,12 +95,6 @@ public:
/// @note Returns a null pointer if any component in the path was not unique or not found. /// @note Returns a null pointer if any component in the path was not unique or not found.
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const; Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
/// returns the vector of declarations without repetitions
std::vector<Declaration const*> cleanedDeclarations(
Identifier const& _identifier,
std::vector<Declaration const*> const& _declarations
);
/// Generate and store warnings about variables that are named like instructions. /// Generate and store warnings about variables that are named like instructions.
void warnVariablesNamedLikeInstructions(); void warnVariablesNamedLikeInstructions();

View File

@ -22,9 +22,7 @@
#include <libsolidity/analysis/ReferencesResolver.h> #include <libsolidity/analysis/ReferencesResolver.h>
#include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libyul/AsmAnalysis.h> #include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h> #include <libyul/AsmAnalysisInfo.h>
@ -37,7 +35,6 @@
#include <libsolutil/StringUtils.h> #include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std; using namespace std;
using namespace solidity::langutil; using namespace solidity::langutil;
@ -126,40 +123,10 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
else if (declarations.size() == 1) else if (declarations.size() == 1)
_identifier.annotation().referencedDeclaration = declarations.front(); _identifier.annotation().referencedDeclaration = declarations.front();
else else
_identifier.annotation().overloadedDeclarations = _identifier.annotation().candidateDeclarations = declarations;
m_resolver.cleanedDeclarations(_identifier, declarations);
return false; return false;
} }
bool ReferencesResolver::visit(ElementaryTypeName const& _typeName)
{
if (!_typeName.annotation().type)
{
_typeName.annotation().type = TypeProvider::fromElementaryTypeName(_typeName.typeName());
if (_typeName.stateMutability().has_value())
{
// for non-address types this was already caught by the parser
solAssert(_typeName.annotation().type->category() == Type::Category::Address, "");
switch (*_typeName.stateMutability())
{
case StateMutability::Payable:
_typeName.annotation().type = TypeProvider::payableAddress();
break;
case StateMutability::NonPayable:
_typeName.annotation().type = TypeProvider::address();
break;
default:
m_errorReporter.typeError(
_typeName.location(),
"Address types can only be payable or non-payable."
);
break;
}
}
}
return true;
}
bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition) bool ReferencesResolver::visit(FunctionDefinition const& _functionDefinition)
{ {
m_returnParameters.push_back(_functionDefinition.returnParameterList().get()); m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
@ -194,113 +161,6 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
} }
_typeName.annotation().referencedDeclaration = declaration; _typeName.annotation().referencedDeclaration = declaration;
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
_typeName.annotation().type = TypeProvider::structType(*structDef, DataLocation::Storage);
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
_typeName.annotation().type = TypeProvider::enumType(*enumDef);
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = TypeProvider::contract(*contract);
else
{
_typeName.annotation().type = TypeProvider::emptyTuple();
fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
{
switch (_typeName.visibility())
{
case Visibility::Internal:
case Visibility::External:
break;
default:
fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
return;
}
if (_typeName.isPayable() && _typeName.visibility() != Visibility::External)
{
fatalTypeError(_typeName.location(), "Only external function types can be payable.");
return;
}
if (_typeName.visibility() == Visibility::External)
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->interfaceType(false).get())
{
fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
return;
}
}
_typeName.annotation().type = TypeProvider::function(_typeName);
}
void ReferencesResolver::endVisit(Mapping const& _mapping)
{
if (auto const* typeName = dynamic_cast<UserDefinedTypeName const*>(&_mapping.keyType()))
{
if (auto const* contractType = dynamic_cast<ContractType const*>(typeName->annotation().type))
{
if (contractType->contractDefinition().isLibrary())
m_errorReporter.fatalTypeError(
typeName->location(),
"Library types cannot be used as mapping keys."
);
}
else if (typeName->annotation().type->category() != Type::Category::Enum)
m_errorReporter.fatalTypeError(
typeName->location(),
"Only elementary types, contract types or enums are allowed as mapping keys."
);
}
else
solAssert(dynamic_cast<ElementaryTypeName const*>(&_mapping.keyType()), "");
TypePointer keyType = _mapping.keyType().annotation().type;
TypePointer valueType = _mapping.valueType().annotation().type;
// Convert key type to memory.
keyType = TypeProvider::withLocationIfReference(DataLocation::Memory, keyType);
// Convert value type to storage reference.
valueType = TypeProvider::withLocationIfReference(DataLocation::Storage, valueType);
_mapping.annotation().type = TypeProvider::mapping(keyType, valueType);
}
void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
{
TypePointer baseType = _typeName.baseType().annotation().type;
if (!baseType)
{
solAssert(!m_errorReporter.errors().empty(), "");
return;
}
if (baseType->storageBytes() == 0)
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length())
{
TypePointer& lengthTypeGeneric = length->annotation().type;
if (!lengthTypeGeneric)
lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric);
if (!lengthType || !lengthType->mobileType())
fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
else if (lengthType->isZero())
fatalTypeError(length->location(), "Array with zero length specified.");
else if (lengthType->isFractional())
fatalTypeError(length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
fatalTypeError(length->location(), "Array with negative length specified.");
else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
}
else
_typeName.annotation().type = TypeProvider::array(DataLocation::Storage, baseType);
} }
bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly) bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
@ -321,115 +181,6 @@ bool ReferencesResolver::visit(Return const& _return)
return true; return true;
} }
void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
{
if (_variable.annotation().type)
return;
if (_variable.isConstant() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"constant\" keyword can only be used for state variables.");
if (_variable.immutable() && !_variable.isStateVariable())
m_errorReporter.declarationError(_variable.location(), "The \"immutable\" keyword can only be used for state variables.");
if (!_variable.typeName())
{
// This can still happen in very unusual cases where a developer uses constructs, such as
// `var a;`, however, such code will have generated errors already.
// However, we cannot blindingly solAssert() for that here, as the TypeChecker (which is
// invoking ReferencesResolver) is generating it, so the error is most likely(!) generated
// after this step.
return;
}
using Location = VariableDeclaration::Location;
Location varLoc = _variable.referenceLocation();
DataLocation typeLoc = DataLocation::Memory;
set<Location> allowedDataLocations = _variable.allowedDataLocations();
if (!allowedDataLocations.count(varLoc))
{
auto locationToString = [](VariableDeclaration::Location _location) -> string
{
switch (_location)
{
case Location::Memory: return "\"memory\"";
case Location::Storage: return "\"storage\"";
case Location::CallData: return "\"calldata\"";
case Location::Unspecified: return "none";
}
return {};
};
string errorString;
if (!_variable.hasReferenceOrMappingType())
errorString = "Data location can only be specified for array, struct or mapping types";
else
{
errorString = "Data location must be " +
util::joinHumanReadable(
allowedDataLocations | boost::adaptors::transformed(locationToString),
", ",
" or "
);
if (_variable.isCallableOrCatchParameter())
errorString +=
" for " +
string(_variable.isReturnParameter() ? "return " : "") +
"parameter in" +
string(_variable.isExternalCallableParameter() ? " external" : "") +
" function";
else
errorString += " for variable";
}
errorString += ", but " + locationToString(varLoc) + " was given.";
typeError(_variable.location(), errorString);
solAssert(!allowedDataLocations.empty(), "");
varLoc = *allowedDataLocations.begin();
}
// Find correct data location.
if (_variable.isEventParameter())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = DataLocation::Memory;
}
else if (_variable.isStateVariable())
{
solAssert(varLoc == Location::Unspecified, "");
typeLoc = (_variable.isConstant() || _variable.immutable()) ? DataLocation::Memory : DataLocation::Storage;
}
else if (
dynamic_cast<StructDefinition const*>(_variable.scope()) ||
dynamic_cast<EnumDefinition const*>(_variable.scope())
)
// The actual location will later be changed depending on how the type is used.
typeLoc = DataLocation::Storage;
else
switch (varLoc)
{
case Location::Memory:
typeLoc = DataLocation::Memory;
break;
case Location::Storage:
typeLoc = DataLocation::Storage;
break;
case Location::CallData:
typeLoc = DataLocation::CallData;
break;
case Location::Unspecified:
solAssert(!_variable.hasReferenceOrMappingType(), "Data location not properly set.");
}
TypePointer type = _variable.typeName()->annotation().type;
if (auto ref = dynamic_cast<ReferenceType const*>(type))
{
bool isPointer = !_variable.isStateVariable();
type = TypeProvider::withLocation(ref, typeLoc, isPointer);
}
_variable.annotation().type = type;
}
void ReferencesResolver::operator()(yul::FunctionDefinition const& _function) void ReferencesResolver::operator()(yul::FunctionDefinition const& _function)
{ {
bool wasInsideFunction = m_yulInsideFunction; bool wasInsideFunction = m_yulInsideFunction;
@ -514,18 +265,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
visit(*_varDecl.value); visit(*_varDecl.value);
} }
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.typeError(_location, _description);
}
void ReferencesResolver::fatalTypeError(SourceLocation const& _location, string const& _description)
{
m_errorOccurred = true;
m_errorReporter.fatalTypeError(_location, _description);
}
void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description) void ReferencesResolver::declarationError(SourceLocation const& _location, string const& _description)
{ {
m_errorOccurred = true; m_errorOccurred = true;

View File

@ -76,29 +76,18 @@ private:
void endVisit(ForStatement const& _for) override; void endVisit(ForStatement const& _for) override;
void endVisit(VariableDeclarationStatement const& _varDeclStatement) override; void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
bool visit(Identifier const& _identifier) override; bool visit(Identifier const& _identifier) override;
bool visit(ElementaryTypeName const& _typeName) override;
bool visit(FunctionDefinition const& _functionDefinition) override; bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const& _functionDefinition) override; void endVisit(FunctionDefinition const& _functionDefinition) override;
bool visit(ModifierDefinition const& _modifierDefinition) override; bool visit(ModifierDefinition const& _modifierDefinition) override;
void endVisit(ModifierDefinition const& _modifierDefinition) override; void endVisit(ModifierDefinition const& _modifierDefinition) override;
void endVisit(UserDefinedTypeName const& _typeName) override; void endVisit(UserDefinedTypeName const& _typeName) override;
void endVisit(FunctionTypeName const& _typeName) override;
void endVisit(Mapping const& _mapping) override;
void endVisit(ArrayTypeName const& _typeName) override;
bool visit(InlineAssembly const& _inlineAssembly) override; bool visit(InlineAssembly const& _inlineAssembly) override;
bool visit(Return const& _return) override; bool visit(Return const& _return) override;
void endVisit(VariableDeclaration const& _variable) override;
void operator()(yul::FunctionDefinition const& _function) override; void operator()(yul::FunctionDefinition const& _function) override;
void operator()(yul::Identifier const& _identifier) override; void operator()(yul::Identifier const& _identifier) override;
void operator()(yul::VariableDeclaration const& _varDecl) override; void operator()(yul::VariableDeclaration const& _varDecl) override;
/// Adds a new error to the list of errors.
void typeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors and throws to abort reference resolving.
void fatalTypeError(langutil::SourceLocation const& _location, std::string const& _description);
/// Adds a new error to the list of errors. /// Adds a new error to the list of errors.
void declarationError(langutil::SourceLocation const& _location, std::string const& _description); void declarationError(langutil::SourceLocation const& _location, std::string const& _description);

View File

@ -289,39 +289,6 @@ void TypeChecker::endVisit(UsingForDirective const& _usingFor)
m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected."); m_errorReporter.fatalTypeError(_usingFor.libraryName().location(), "Library name expected.");
} }
bool TypeChecker::visit(StructDefinition const& _struct)
{
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
solAssert(type(*member)->canBeStored(), "Type cannot be used in struct.");
// Check recursion, fatal error if detected.
auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector, size_t _depth)
{
if (_depth >= 256)
m_errorReporter.fatalDeclarationError(_struct.location(), "Struct definition exhausting cyclic dependency validator.");
for (ASTPointer<VariableDeclaration> const& member: _struct.members())
{
Type const* memberType = type(*member);
while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
{
if (arrayType->isDynamicallySized())
break;
memberType = arrayType->baseType();
}
if (auto structType = dynamic_cast<StructType const*>(memberType))
if (_cycleDetector.run(structType->structDefinition()))
return;
}
};
if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
ASTNode::listAccept(_struct.members(), *this);
return false;
}
bool TypeChecker::visit(FunctionDefinition const& _function) bool TypeChecker::visit(FunctionDefinition const& _function)
{ {
bool isLibraryFunction = _function.inContractKind() == ContractKind::Library; bool isLibraryFunction = _function.inContractKind() == ContractKind::Library;
@ -520,19 +487,16 @@ bool TypeChecker::visit(VariableDeclaration const& _variable)
m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables."); m_errorReporter.typeError(_variable.location(), "Internal or recursive type is not allowed for public state variables.");
} }
switch (varType->category()) if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
{ {
case Type::Category::Array: auto result = referenceType->validForLocation(referenceType->location());
if (auto arrayType = dynamic_cast<ArrayType const*>(varType)) if (result && _variable.isPublicCallableParameter())
if ( result = referenceType->validForLocation(DataLocation::CallData);
((arrayType->location() == DataLocation::Memory) || if (!result)
(arrayType->location() == DataLocation::CallData)) && {
!arrayType->validForCalldata() solAssert(!result.message().empty(), "Expected detailed error message");
) m_errorReporter.typeError(_variable.location(), result.message());
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded."); }
break;
default:
break;
} }
return false; return false;
@ -633,7 +597,15 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
{ {
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type); FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
if (fun.kind() == FunctionType::Kind::External) if (fun.kind() == FunctionType::Kind::External)
{
for (auto const& t: _funType.parameterTypes() + _funType.returnParameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->interfaceType(false).get())
m_errorReporter.typeError(t->location(), "Internal type cannot be used for external function type.");
}
solAssert(fun.interfaceType(false), "External function type uses internal types."); solAssert(fun.interfaceType(false), "External function type uses internal types.");
}
} }
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly) bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
@ -1305,7 +1277,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
_conditional.trueExpression().annotation().isPure && _conditional.trueExpression().annotation().isPure &&
_conditional.falseExpression().annotation().isPure; _conditional.falseExpression().annotation().isPure;
if (_conditional.annotation().lValueRequested) if (_conditional.annotation().willBeWrittenTo)
m_errorReporter.typeError( m_errorReporter.typeError(
_conditional.location(), _conditional.location(),
"Conditional expression as left value is not supported yet." "Conditional expression as left value is not supported yet."
@ -1401,7 +1373,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
vector<ASTPointer<Expression>> const& components = _tuple.components(); vector<ASTPointer<Expression>> const& components = _tuple.components();
TypePointers types; TypePointers types;
if (_tuple.annotation().lValueRequested) if (_tuple.annotation().willBeWrittenTo)
{ {
if (_tuple.isInlineArray()) if (_tuple.isInlineArray())
m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue."); m_errorReporter.fatalTypeError(_tuple.location(), "Inline array type cannot be declared as LValue.");
@ -1432,41 +1404,37 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{ {
if (!components[i]) if (!components[i])
m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty."); m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
else if (components[i])
{
components[i]->accept(*this);
types.push_back(type(*components[i]));
if (types[i]->category() == Type::Category::Tuple) components[i]->accept(*this);
if (dynamic_cast<TupleType const&>(*types[i]).components().empty()) types.push_back(type(*components[i]));
{
if (_tuple.isInlineArray())
m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty.");
}
// Note: code generation will visit each of the expression even if they are not assigned from. if (types[i]->category() == Type::Category::Tuple)
if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1) if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType())
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number.");
if (_tuple.isInlineArray())
{ {
solAssert(!!types[i], "Inline array cannot have empty components"); if (_tuple.isInlineArray())
m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
if ((i == 0 || inlineArrayType) && !types[i]->mobileType()) m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty.");
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type.");
if (i == 0)
inlineArrayType = types[i]->mobileType();
else if (inlineArrayType)
inlineArrayType = Type::commonType(inlineArrayType, types[i]);
} }
if (!components[i]->annotation().isPure)
isPure = false; // Note: code generation will visit each of the expression even if they are not assigned from.
if (types[i]->category() == Type::Category::RationalNumber && components.size() > 1)
if (!dynamic_cast<RationalNumberType const&>(*types[i]).mobileType())
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid rational number.");
if (_tuple.isInlineArray())
{
solAssert(!!types[i], "Inline array cannot have empty components");
if ((i == 0 || inlineArrayType) && !types[i]->mobileType())
m_errorReporter.fatalTypeError(components[i]->location(), "Invalid mobile type.");
if (i == 0)
inlineArrayType = types[i]->mobileType();
else if (inlineArrayType)
inlineArrayType = Type::commonType(inlineArrayType, types[i]);
} }
else if (!components[i]->annotation().isPure)
types.push_back(TypePointer()); isPure = false;
} }
_tuple.annotation().isPure = isPure; _tuple.annotation().isPure = isPure;
if (_tuple.isInlineArray()) if (_tuple.isInlineArray())
@ -1802,6 +1770,10 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function) void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
{ {
solAssert(_function.isConstructor(), ""); solAssert(_function.isConstructor(), "");
if (_function.markedVirtual())
m_errorReporter.typeError(_function.location(), "Constructors cannot be virtual.");
if (_function.overrides())
m_errorReporter.typeError(_function.location(), "Constructors cannot override.");
if (!_function.returnParameters().empty()) if (!_function.returnParameters().empty())
m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor."); m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable) if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
@ -2782,11 +2754,57 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
return false; return false;
} }
vector<Declaration const*> TypeChecker::cleanOverloadedDeclarations(
Identifier const& _identifier,
vector<Declaration const*> const& _candidates
)
{
solAssert(_candidates.size() > 1, "");
vector<Declaration const*> uniqueDeclarations;
for (Declaration const* declaration: _candidates)
{
solAssert(declaration, "");
// the declaration is functionDefinition, eventDefinition or a VariableDeclaration while declarations > 1
solAssert(
dynamic_cast<FunctionDefinition const*>(declaration) ||
dynamic_cast<EventDefinition const*>(declaration) ||
dynamic_cast<VariableDeclaration const*>(declaration) ||
dynamic_cast<MagicVariableDeclaration const*>(declaration),
"Found overloading involving something not a function, event or a (magic) variable."
);
FunctionTypePointer functionType {declaration->functionType(false)};
if (!functionType)
functionType = declaration->functionType(true);
solAssert(functionType, "Failed to determine the function type of the overloaded.");
for (TypePointer parameter: functionType->parameterTypes() + functionType->returnParameterTypes())
if (!parameter)
m_errorReporter.fatalDeclarationError(_identifier.location(), "Function type can not be used in this context.");
if (uniqueDeclarations.end() == find_if(
uniqueDeclarations.begin(),
uniqueDeclarations.end(),
[&](Declaration const* d)
{
FunctionType const* newFunctionType = d->functionType(false);
if (!newFunctionType)
newFunctionType = d->functionType(true);
return newFunctionType && functionType->hasEqualParameterTypes(*newFunctionType);
}
))
uniqueDeclarations.push_back(declaration);
}
return uniqueDeclarations;
}
bool TypeChecker::visit(Identifier const& _identifier) bool TypeChecker::visit(Identifier const& _identifier)
{ {
IdentifierAnnotation& annotation = _identifier.annotation(); IdentifierAnnotation& annotation = _identifier.annotation();
if (!annotation.referencedDeclaration) if (!annotation.referencedDeclaration)
{ {
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
if (!annotation.arguments) if (!annotation.arguments)
{ {
// The identifier should be a public state variable shadowing other functions // The identifier should be a public state variable shadowing other functions
@ -3006,7 +3024,7 @@ bool TypeChecker::expectType(Expression const& _expression, Type const& _expecte
void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment) void TypeChecker::requireLValue(Expression const& _expression, bool _ordinaryAssignment)
{ {
_expression.annotation().lValueRequested = true; _expression.annotation().willBeWrittenTo = true;
_expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment; _expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment;
_expression.accept(*this); _expression.accept(*this);

View File

@ -112,7 +112,6 @@ private:
void endVisit(InheritanceSpecifier const& _inheritance) override; void endVisit(InheritanceSpecifier const& _inheritance) override;
void endVisit(UsingForDirective const& _usingFor) override; void endVisit(UsingForDirective const& _usingFor) override;
bool visit(StructDefinition const& _struct) override;
bool visit(FunctionDefinition const& _function) override; bool visit(FunctionDefinition const& _function) override;
bool visit(VariableDeclaration const& _variable) override; bool visit(VariableDeclaration const& _variable) override;
/// We need to do this manually because we want to pass the bases of the current contract in /// We need to do this manually because we want to pass the bases of the current contract in
@ -154,6 +153,11 @@ private:
/// @returns the referenced declaration and throws on error. /// @returns the referenced declaration and throws on error.
Declaration const& dereference(UserDefinedTypeName const& _typeName) const; Declaration const& dereference(UserDefinedTypeName const& _typeName) const;
std::vector<Declaration const*> cleanOverloadedDeclarations(
Identifier const& _reference,
std::vector<Declaration const*> const& _candidates
);
/// Runs type checks on @a _expression to infer its type and then checks that it is implicitly /// Runs type checks on @a _expression to infer its type and then checks that it is implicitly
/// convertible to @a _expectedType. /// convertible to @a _expectedType.
bool expectType(Expression const& _expression, Type const& _expectedType); bool expectType(Expression const& _expression, Type const& _expectedType);

View File

@ -23,6 +23,7 @@
#include <libevmasm/SemanticInformation.h> #include <libevmasm/SemanticInformation.h>
#include <functional> #include <functional>
#include <utility>
#include <variant> #include <variant>
using namespace std; using namespace std;
@ -41,7 +42,7 @@ public:
std::function<void(StateMutability, SourceLocation const&)> _reportMutability std::function<void(StateMutability, SourceLocation const&)> _reportMutability
): ):
m_dialect(_dialect), m_dialect(_dialect),
m_reportMutability(_reportMutability) {} m_reportMutability(std::move(_reportMutability)) {}
void operator()(yul::Literal const&) {} void operator()(yul::Literal const&) {}
void operator()(yul::Identifier const&) {} void operator()(yul::Identifier const&) {}
@ -196,7 +197,7 @@ void ViewPureChecker::endVisit(Identifier const& _identifier)
StateMutability mutability = StateMutability::Pure; StateMutability mutability = StateMutability::Pure;
bool writes = _identifier.annotation().lValueRequested; bool writes = _identifier.annotation().willBeWrittenTo;
if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
{ {
if (varDecl->isStateVariable() && !varDecl->isConstant()) if (varDecl->isStateVariable() && !varDecl->isConstant())
@ -331,7 +332,7 @@ bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
void ViewPureChecker::endVisit(MemberAccess const& _memberAccess) void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{ {
StateMutability mutability = StateMutability::Pure; StateMutability mutability = StateMutability::Pure;
bool writes = _memberAccess.annotation().lValueRequested; bool writes = _memberAccess.annotation().willBeWrittenTo;
ASTString const& member = _memberAccess.memberName(); ASTString const& member = _memberAccess.memberName();
switch (_memberAccess.expression().annotation().type->category()) switch (_memberAccess.expression().annotation().type->category())
@ -401,7 +402,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, ""); solAssert(_indexAccess.annotation().type->category() == Type::Category::TypeType, "");
else else
{ {
bool writes = _indexAccess.annotation().lValueRequested; bool writes = _indexAccess.annotation().willBeWrittenTo;
if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) if (_indexAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location()); reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexAccess.location());
} }
@ -409,7 +410,7 @@ void ViewPureChecker::endVisit(IndexAccess const& _indexAccess)
void ViewPureChecker::endVisit(IndexRangeAccess const& _indexRangeAccess) void ViewPureChecker::endVisit(IndexRangeAccess const& _indexRangeAccess)
{ {
bool writes = _indexRangeAccess.annotation().lValueRequested; bool writes = _indexRangeAccess.annotation().willBeWrittenTo;
if (_indexRangeAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage)) if (_indexRangeAccess.baseExpression().annotation().type->dataStoredIn(DataLocation::Storage))
reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexRangeAccess.location()); reportMutability(writes ? StateMutability::NonPayable : StateMutability::View, _indexRangeAccess.location());
} }

View File

@ -28,16 +28,18 @@
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <algorithm> #include <algorithm>
#include <functional> #include <functional>
#include <utility>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::frontend; using namespace solidity::frontend;
ASTNode::ASTNode(int64_t _id, SourceLocation const& _location): ASTNode::ASTNode(int64_t _id, SourceLocation _location):
m_id(_id), m_id(_id),
m_location(_location) m_location(std::move(_location))
{ {
} }
@ -255,12 +257,13 @@ TypeNameAnnotation& TypeName::annotation() const
TypePointer StructDefinition::type() const TypePointer StructDefinition::type() const
{ {
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
return TypeProvider::typeType(TypeProvider::structType(*this, DataLocation::Storage)); return TypeProvider::typeType(TypeProvider::structType(*this, DataLocation::Storage));
} }
TypeDeclarationAnnotation& StructDefinition::annotation() const StructDeclarationAnnotation& StructDefinition::annotation() const
{ {
return initAnnotation<TypeDeclarationAnnotation>(); return initAnnotation<StructDeclarationAnnotation>();
} }
TypePointer EnumValue::type() const TypePointer EnumValue::type() const
@ -556,6 +559,18 @@ bool VariableDeclaration::isExternalCallableParameter() const
return false; return false;
} }
bool VariableDeclaration::isPublicCallableParameter() const
{
if (!isCallableOrCatchParameter())
return false;
if (auto const* callable = dynamic_cast<CallableDeclaration const*>(scope()))
if (callable->visibility() == Visibility::Public)
return !isReturnParameter();
return false;
}
bool VariableDeclaration::isInternalCallableParameter() const bool VariableDeclaration::isInternalCallableParameter() const
{ {
if (!isCallableOrCatchParameter()) if (!isCallableOrCatchParameter())
@ -614,12 +629,20 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
else if (isLocalVariable()) else if (isLocalVariable())
{ {
solAssert(typeName(), ""); solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution"); auto dataLocations = [](TypePointer _type, auto&& _recursion) -> set<Location> {
if (typeName()->annotation().type->category() == Type::Category::Mapping) solAssert(_type, "Can only be called after reference resolution");
return set<Location>{ Location::Storage }; switch (_type->category())
else {
// TODO: add Location::Calldata once implemented for local variables. case Type::Category::Array:
return set<Location>{ Location::Memory, Location::Storage }; return _recursion(dynamic_cast<ArrayType const*>(_type)->baseType(), _recursion);
case Type::Category::Mapping:
return set<Location>{ Location::Storage };
default:
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
}
};
return dataLocations(typeName()->annotation().type, dataLocations);
} }
else else
// Struct members etc. // Struct members etc.
@ -756,3 +779,25 @@ string Literal::getChecksummedAddress() const
address.insert(address.begin(), 40 - address.size(), '0'); address.insert(address.begin(), 40 - address.size(), '0');
return util::getChecksummedAddress(address); return util::getChecksummedAddress(address);
} }
TryCatchClause const* TryStatement::successClause() const
{
solAssert(m_clauses.size() > 0, "");
return m_clauses[0].get();
}
TryCatchClause const* TryStatement::structuredClause() const
{
for (size_t i = 1; i < m_clauses.size(); ++i)
if (m_clauses[i]->errorName() == "Error")
return m_clauses[i].get();
return nullptr;
}
TryCatchClause const* TryStatement::fallbackClause() const
{
for (size_t i = 1; i < m_clauses.size(); ++i)
if (m_clauses[i]->errorName().empty())
return m_clauses[i].get();
return nullptr;
}

View File

@ -38,6 +38,7 @@
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <string> #include <string>
#include <utility>
#include <vector> #include <vector>
namespace solidity::yul namespace solidity::yul
@ -82,7 +83,7 @@ public:
using SourceLocation = langutil::SourceLocation; using SourceLocation = langutil::SourceLocation;
explicit ASTNode(int64_t _id, SourceLocation const& _location); explicit ASTNode(int64_t _id, SourceLocation _location);
virtual ~ASTNode() {} virtual ~ASTNode() {}
/// @returns an identifier of this AST node that is unique for a single compilation run. /// @returns an identifier of this AST node that is unique for a single compilation run.
@ -155,8 +156,8 @@ std::vector<T const*> ASTNode::filteredNodes(std::vector<ASTPointer<ASTNode>> co
class SourceUnit: public ASTNode class SourceUnit: public ASTNode
{ {
public: public:
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes): SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> _nodes):
ASTNode(_id, _location), m_nodes(_nodes) {} ASTNode(_id, _location), m_nodes(std::move(_nodes)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -224,10 +225,10 @@ public:
Declaration( Declaration(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> _name,
Visibility _visibility = Visibility::Default Visibility _visibility = Visibility::Default
): ):
ASTNode(_id, _location), m_name(_name), m_visibility(_visibility) {} ASTNode(_id, _location), m_name(std::move(_name)), m_visibility(_visibility) {}
/// @returns the declared name. /// @returns the declared name.
ASTString const& name() const { return *m_name; } ASTString const& name() const { return *m_name; }
@ -275,9 +276,9 @@ public:
PragmaDirective( PragmaDirective(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
std::vector<Token> const& _tokens, std::vector<Token> _tokens,
std::vector<ASTString> const& _literals std::vector<ASTString> _literals
): ASTNode(_id, _location), m_tokens(_tokens), m_literals(_literals) ): ASTNode(_id, _location), m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
@ -318,12 +319,12 @@ public:
ImportDirective( ImportDirective(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _path, ASTPointer<ASTString> _path,
ASTPointer<ASTString> const& _unitAlias, ASTPointer<ASTString> const& _unitAlias,
SymbolAliasList _symbolAliases SymbolAliasList _symbolAliases
): ):
Declaration(_id, _location, _unitAlias), Declaration(_id, _location, _unitAlias),
m_path(_path), m_path(std::move(_path)),
m_symbolAliases(move(_symbolAliases)) m_symbolAliases(move(_symbolAliases))
{ } { }
@ -372,8 +373,8 @@ public:
StructuredDocumentation( StructuredDocumentation(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _text ASTPointer<ASTString> _text
): ASTNode(_id, _location), m_text(_text) ): ASTNode(_id, _location), m_text(std::move(_text))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
@ -394,7 +395,7 @@ class Documented
{ {
public: public:
virtual ~Documented() = default; virtual ~Documented() = default;
explicit Documented(ASTPointer<ASTString> const& _documentation): m_documentation(_documentation) {} explicit Documented(ASTPointer<ASTString> _documentation): m_documentation(std::move(_documentation)) {}
/// @return A shared pointer of an ASTString. /// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
@ -411,7 +412,7 @@ class StructurallyDocumented
{ {
public: public:
virtual ~StructurallyDocumented() = default; virtual ~StructurallyDocumented() = default;
explicit StructurallyDocumented(ASTPointer<StructuredDocumentation> const& _documentation): m_documentation(_documentation) {} explicit StructurallyDocumented(ASTPointer<StructuredDocumentation> _documentation): m_documentation(std::move(_documentation)) {}
/// @return A shared pointer of a FormalDocumentation. /// @return A shared pointer of a FormalDocumentation.
/// Can contain a nullptr in which case indicates absence of documentation /// Can contain a nullptr in which case indicates absence of documentation
@ -453,15 +454,15 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<StructuredDocumentation> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts, std::vector<ASTPointer<InheritanceSpecifier>> _baseContracts,
std::vector<ASTPointer<ASTNode>> const& _subNodes, std::vector<ASTPointer<ASTNode>> _subNodes,
ContractKind _contractKind = ContractKind::Contract, ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false bool _abstract = false
): ):
Declaration(_id, _location, _name), Declaration(_id, _location, _name),
StructurallyDocumented(_documentation), StructurallyDocumented(_documentation),
m_baseContracts(_baseContracts), m_baseContracts(std::move(_baseContracts)),
m_subNodes(_subNodes), m_subNodes(std::move(_subNodes)),
m_contractKind(_contractKind), m_contractKind(_contractKind),
m_abstract(_abstract) m_abstract(_abstract)
{} {}
@ -538,10 +539,10 @@ public:
InheritanceSpecifier( InheritanceSpecifier(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _baseName, ASTPointer<UserDefinedTypeName> _baseName,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
): ):
ASTNode(_id, _location), m_baseName(_baseName), m_arguments(std::move(_arguments)) {} ASTNode(_id, _location), m_baseName(std::move(_baseName)), m_arguments(std::move(_arguments)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -568,10 +569,10 @@ public:
UsingForDirective( UsingForDirective(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _libraryName, ASTPointer<UserDefinedTypeName> _libraryName,
ASTPointer<TypeName> const& _typeName ASTPointer<TypeName> _typeName
): ):
ASTNode(_id, _location), m_libraryName(_libraryName), m_typeName(_typeName) {} ASTNode(_id, _location), m_libraryName(std::move(_libraryName)), m_typeName(std::move(_typeName)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -592,9 +593,9 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<VariableDeclaration>> const& _members std::vector<ASTPointer<VariableDeclaration>> _members
): ):
Declaration(_id, _location, _name), m_members(_members) {} Declaration(_id, _location, _name), m_members(std::move(_members)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -606,7 +607,7 @@ public:
bool isVisibleInDerivedContracts() const override { return true; } bool isVisibleInDerivedContracts() const override { return true; }
bool isVisibleViaContractTypeAccess() const override { return true; } bool isVisibleViaContractTypeAccess() const override { return true; }
TypeDeclarationAnnotation& annotation() const override; StructDeclarationAnnotation& annotation() const override;
private: private:
std::vector<ASTPointer<VariableDeclaration>> m_members; std::vector<ASTPointer<VariableDeclaration>> m_members;
@ -619,9 +620,9 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
std::vector<ASTPointer<EnumValue>> const& _members std::vector<ASTPointer<EnumValue>> _members
): ):
Declaration(_id, _location, _name), m_members(_members) {} Declaration(_id, _location, _name), m_members(std::move(_members)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -664,9 +665,9 @@ public:
ParameterList( ParameterList(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
std::vector<ASTPointer<VariableDeclaration>> const& _parameters std::vector<ASTPointer<VariableDeclaration>> _parameters
): ):
ASTNode(_id, _location), m_parameters(_parameters) {} ASTNode(_id, _location), m_parameters(std::move(_parameters)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -688,15 +689,15 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
Visibility _visibility, Visibility _visibility,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> _parameters,
bool _isVirtual = false, bool _isVirtual = false,
ASTPointer<OverrideSpecifier> const& _overrides = nullptr, ASTPointer<OverrideSpecifier> _overrides = nullptr,
ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>() ASTPointer<ParameterList> _returnParameters = ASTPointer<ParameterList>()
): ):
Declaration(_id, _location, _name, _visibility), Declaration(_id, _location, _name, _visibility),
m_parameters(_parameters), m_parameters(std::move(_parameters)),
m_overrides(_overrides), m_overrides(std::move(_overrides)),
m_returnParameters(_returnParameters), m_returnParameters(std::move(_returnParameters)),
m_isVirtual(_isVirtual) m_isVirtual(_isVirtual)
{ {
} }
@ -740,10 +741,10 @@ public:
OverrideSpecifier( OverrideSpecifier(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
std::vector<ASTPointer<UserDefinedTypeName>> const& _overrides std::vector<ASTPointer<UserDefinedTypeName>> _overrides
): ):
ASTNode(_id, _location), ASTNode(_id, _location),
m_overrides(_overrides) m_overrides(std::move(_overrides))
{ {
} }
@ -771,7 +772,7 @@ public:
ASTPointer<OverrideSpecifier> const& _overrides, ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<StructuredDocumentation> const& _documentation, ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers, std::vector<ASTPointer<ModifierInvocation>> _modifiers,
ASTPointer<ParameterList> const& _returnParameters, ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body ASTPointer<Block> const& _body
): ):
@ -780,7 +781,7 @@ public:
ImplementationOptional(_body != nullptr), ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability), m_stateMutability(_stateMutability),
m_kind(_kind), m_kind(_kind),
m_functionModifiers(_modifiers), m_functionModifiers(std::move(_modifiers)),
m_body(_body) m_body(_body)
{ {
solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, ""); solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, "");
@ -854,28 +855,38 @@ class VariableDeclaration: public Declaration
{ {
public: public:
enum Location { Unspecified, Storage, Memory, CallData }; enum Location { Unspecified, Storage, Memory, CallData };
enum class Constantness { Mutable, Immutable, Constant }; enum class Mutability { Mutable, Immutable, Constant };
static std::string mutabilityToString(Mutability _mutability)
{
switch (_mutability)
{
case Mutability::Mutable: return "mutable";
case Mutability::Immutable: return "immutable";
case Mutability::Constant: return "constant";
}
return {};
}
VariableDeclaration( VariableDeclaration(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<TypeName> const& _type, ASTPointer<TypeName> _type,
ASTPointer<ASTString> const& _name, ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value, ASTPointer<Expression> _value,
Visibility _visibility, Visibility _visibility,
bool _isStateVar = false, bool _isStateVar = false,
bool _isIndexed = false, bool _isIndexed = false,
Constantness _constantness = Constantness::Mutable, Mutability _mutability = Mutability::Mutable,
ASTPointer<OverrideSpecifier> const& _overrides = nullptr, ASTPointer<OverrideSpecifier> _overrides = nullptr,
Location _referenceLocation = Location::Unspecified Location _referenceLocation = Location::Unspecified
): ):
Declaration(_id, _location, _name, _visibility), Declaration(_id, _location, _name, _visibility),
m_typeName(_type), m_typeName(std::move(_type)),
m_value(_value), m_value(std::move(_value)),
m_isStateVariable(_isStateVar), m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed), m_isIndexed(_isIndexed),
m_constantness(_constantness), m_mutability(_mutability),
m_overrides(_overrides), m_overrides(std::move(_overrides)),
m_location(_referenceLocation) {} m_location(_referenceLocation) {}
@ -903,6 +914,8 @@ public:
/// @returns true if this variable is a parameter (not return parameter) of an external function. /// @returns true if this variable is a parameter (not return parameter) of an external function.
/// This excludes parameters of external function type names. /// This excludes parameters of external function type names.
bool isExternalCallableParameter() const; bool isExternalCallableParameter() const;
/// @returns true if this variable is a parameter (not return parameter) of a public function.
bool isPublicCallableParameter() const;
/// @returns true if this variable is a parameter or return parameter of an internal function /// @returns true if this variable is a parameter or return parameter of an internal function
/// or a function type of internal visibility. /// or a function type of internal visibility.
bool isInternalCallableParameter() const; bool isInternalCallableParameter() const;
@ -918,8 +931,9 @@ public:
bool hasReferenceOrMappingType() const; bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; } bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; } bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_constantness == Constantness::Constant; } Mutability mutability() const { return m_mutability; }
bool immutable() const { return m_constantness == Constantness::Immutable; } bool isConstant() const { return m_mutability == Mutability::Constant; }
bool immutable() const { return m_mutability == Mutability::Immutable; }
ASTPointer<OverrideSpecifier> const& overrides() const { return m_overrides; } ASTPointer<OverrideSpecifier> const& overrides() const { return m_overrides; }
Location referenceLocation() const { return m_location; } Location referenceLocation() const { return m_location; }
/// @returns a set of allowed storage locations for the variable. /// @returns a set of allowed storage locations for the variable.
@ -947,7 +961,7 @@ private:
bool m_isStateVariable = false; ///< Whether or not this is a contract state variable bool m_isStateVariable = false; ///< Whether or not this is a contract state variable
bool m_isIndexed = false; ///< Whether this is an indexed variable (used by events). bool m_isIndexed = false; ///< Whether this is an indexed variable (used by events).
/// Whether the variable is "constant", "immutable" or non-marked (mutable). /// Whether the variable is "constant", "immutable" or non-marked (mutable).
Constantness m_constantness = Constantness::Mutable; Mutability m_mutability = Mutability::Mutable;
ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node ASTPointer<OverrideSpecifier> m_overrides; ///< Contains the override specifier node
Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type. Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type.
}; };
@ -966,11 +980,11 @@ public:
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> const& _parameters,
bool _isVirtual, bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides, ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> const& _body ASTPointer<Block> _body
): ):
CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides), CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
StructurallyDocumented(_documentation), StructurallyDocumented(_documentation),
m_body(_body) m_body(std::move(_body))
{ {
} }
@ -1004,10 +1018,10 @@ public:
ModifierInvocation( ModifierInvocation(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Identifier> const& _name, ASTPointer<Identifier> _name,
std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments std::unique_ptr<std::vector<ASTPointer<Expression>>> _arguments
): ):
ASTNode(_id, _location), m_modifierName(_name), m_arguments(std::move(_arguments)) {} ASTNode(_id, _location), m_modifierName(std::move(_name)), m_arguments(std::move(_arguments)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1148,8 +1162,8 @@ private:
class UserDefinedTypeName: public TypeName class UserDefinedTypeName: public TypeName
{ {
public: public:
UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> const& _namePath): UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> _namePath):
TypeName(_id, _location), m_namePath(_namePath) {} TypeName(_id, _location), m_namePath(std::move(_namePath)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1170,12 +1184,12 @@ public:
FunctionTypeName( FunctionTypeName(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ParameterList> const& _parameterTypes, ASTPointer<ParameterList> _parameterTypes,
ASTPointer<ParameterList> const& _returnTypes, ASTPointer<ParameterList> _returnTypes,
Visibility _visibility, Visibility _visibility,
StateMutability _stateMutability StateMutability _stateMutability
): ):
TypeName(_id, _location), m_parameterTypes(_parameterTypes), m_returnTypes(_returnTypes), TypeName(_id, _location), m_parameterTypes(std::move(_parameterTypes)), m_returnTypes(std::move(_returnTypes)),
m_visibility(_visibility), m_stateMutability(_stateMutability) m_visibility(_visibility), m_stateMutability(_stateMutability)
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
@ -1209,10 +1223,10 @@ public:
Mapping( Mapping(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<TypeName> const& _keyType, ASTPointer<TypeName> _keyType,
ASTPointer<TypeName> const& _valueType ASTPointer<TypeName> _valueType
): ):
TypeName(_id, _location), m_keyType(_keyType), m_valueType(_valueType) {} TypeName(_id, _location), m_keyType(std::move(_keyType)), m_valueType(std::move(_valueType)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1233,10 +1247,10 @@ public:
ArrayTypeName( ArrayTypeName(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<TypeName> const& _baseType, ASTPointer<TypeName> _baseType,
ASTPointer<Expression> const& _length ASTPointer<Expression> _length
): ):
TypeName(_id, _location), m_baseType(_baseType), m_length(_length) {} TypeName(_id, _location), m_baseType(std::move(_baseType)), m_length(std::move(_length)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1280,9 +1294,9 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
yul::Dialect const& _dialect, yul::Dialect const& _dialect,
std::shared_ptr<yul::Block> const& _operations std::shared_ptr<yul::Block> _operations
): ):
Statement(_id, _location, _docString), m_dialect(_dialect), m_operations(_operations) {} Statement(_id, _location, _docString), m_dialect(_dialect), m_operations(std::move(_operations)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1306,9 +1320,9 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
std::vector<ASTPointer<Statement>> const& _statements std::vector<ASTPointer<Statement>> _statements
): ):
Statement(_id, _location, _docString), m_statements(_statements) {} Statement(_id, _location, _docString), m_statements(std::move(_statements)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1348,14 +1362,14 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition, ASTPointer<Expression> _condition,
ASTPointer<Statement> const& _trueBody, ASTPointer<Statement> _trueBody,
ASTPointer<Statement> const& _falseBody ASTPointer<Statement> _falseBody
): ):
Statement(_id, _location, _docString), Statement(_id, _location, _docString),
m_condition(_condition), m_condition(std::move(_condition)),
m_trueBody(_trueBody), m_trueBody(std::move(_trueBody)),
m_falseBody(_falseBody) m_falseBody(std::move(_falseBody))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1382,14 +1396,14 @@ public:
TryCatchClause( TryCatchClause(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _errorName, ASTPointer<ASTString> _errorName,
ASTPointer<ParameterList> const& _parameters, ASTPointer<ParameterList> _parameters,
ASTPointer<Block> const& _block ASTPointer<Block> _block
): ):
ASTNode(_id, _location), ASTNode(_id, _location),
m_errorName(_errorName), m_errorName(std::move(_errorName)),
m_parameters(_parameters), m_parameters(std::move(_parameters)),
m_block(_block) m_block(std::move(_block))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1427,12 +1441,12 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _externalCall, ASTPointer<Expression> _externalCall,
std::vector<ASTPointer<TryCatchClause>> const& _clauses std::vector<ASTPointer<TryCatchClause>> _clauses
): ):
Statement(_id, _location, _docString), Statement(_id, _location, _docString),
m_externalCall(_externalCall), m_externalCall(std::move(_externalCall)),
m_clauses(_clauses) m_clauses(std::move(_clauses))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1440,6 +1454,10 @@ public:
Expression const& externalCall() const { return *m_externalCall; } Expression const& externalCall() const { return *m_externalCall; }
std::vector<ASTPointer<TryCatchClause>> const& clauses() const { return m_clauses; } std::vector<ASTPointer<TryCatchClause>> const& clauses() const { return m_clauses; }
TryCatchClause const* successClause() const;
TryCatchClause const* structuredClause() const;
TryCatchClause const* fallbackClause() const;
private: private:
ASTPointer<Expression> m_externalCall; ASTPointer<Expression> m_externalCall;
std::vector<ASTPointer<TryCatchClause>> m_clauses; std::vector<ASTPointer<TryCatchClause>> m_clauses;
@ -1465,11 +1483,11 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition, ASTPointer<Expression> _condition,
ASTPointer<Statement> const& _body, ASTPointer<Statement> _body,
bool _isDoWhile bool _isDoWhile
): ):
BreakableStatement(_id, _location, _docString), m_condition(_condition), m_body(_body), BreakableStatement(_id, _location, _docString), m_condition(std::move(_condition)), m_body(std::move(_body)),
m_isDoWhile(_isDoWhile) {} m_isDoWhile(_isDoWhile) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1494,16 +1512,16 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Statement> const& _initExpression, ASTPointer<Statement> _initExpression,
ASTPointer<Expression> const& _conditionExpression, ASTPointer<Expression> _conditionExpression,
ASTPointer<ExpressionStatement> const& _loopExpression, ASTPointer<ExpressionStatement> _loopExpression,
ASTPointer<Statement> const& _body ASTPointer<Statement> _body
): ):
BreakableStatement(_id, _location, _docString), BreakableStatement(_id, _location, _docString),
m_initExpression(_initExpression), m_initExpression(std::move(_initExpression)),
m_condExpression(_conditionExpression), m_condExpression(std::move(_conditionExpression)),
m_loopExpression(_loopExpression), m_loopExpression(std::move(_loopExpression)),
m_body(_body) m_body(std::move(_body))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1552,7 +1570,7 @@ public:
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> _expression ASTPointer<Expression> _expression
): Statement(_id, _location, _docString), m_expression(_expression) {} ): Statement(_id, _location, _docString), m_expression(std::move(_expression)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1586,9 +1604,9 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<FunctionCall> const& _functionCall ASTPointer<FunctionCall> _functionCall
): ):
Statement(_id, _location, _docString), m_eventCall(_functionCall) {} Statement(_id, _location, _docString), m_eventCall(std::move(_functionCall)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1613,10 +1631,10 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
std::vector<ASTPointer<VariableDeclaration>> const& _variables, std::vector<ASTPointer<VariableDeclaration>> _variables,
ASTPointer<Expression> const& _initialValue ASTPointer<Expression> _initialValue
): ):
Statement(_id, _location, _docString), m_variables(_variables), m_initialValue(_initialValue) {} Statement(_id, _location, _docString), m_variables(std::move(_variables)), m_initialValue(std::move(_initialValue)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1645,7 +1663,7 @@ public:
ASTPointer<ASTString> const& _docString, ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> _expression ASTPointer<Expression> _expression
): ):
Statement(_id, _location, _docString), m_expression(_expression) {} Statement(_id, _location, _docString), m_expression(std::move(_expression)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1679,14 +1697,14 @@ public:
Conditional( Conditional(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _condition, ASTPointer<Expression> _condition,
ASTPointer<Expression> const& _trueExpression, ASTPointer<Expression> _trueExpression,
ASTPointer<Expression> const& _falseExpression ASTPointer<Expression> _falseExpression
): ):
Expression(_id, _location), Expression(_id, _location),
m_condition(_condition), m_condition(std::move(_condition)),
m_trueExpression(_trueExpression), m_trueExpression(std::move(_trueExpression)),
m_falseExpression(_falseExpression) m_falseExpression(std::move(_falseExpression))
{} {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1709,14 +1727,14 @@ public:
Assignment( Assignment(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _leftHandSide, ASTPointer<Expression> _leftHandSide,
Token _assignmentOperator, Token _assignmentOperator,
ASTPointer<Expression> const& _rightHandSide ASTPointer<Expression> _rightHandSide
): ):
Expression(_id, _location), Expression(_id, _location),
m_leftHandSide(_leftHandSide), m_leftHandSide(std::move(_leftHandSide)),
m_assigmentOperator(_assignmentOperator), m_assigmentOperator(_assignmentOperator),
m_rightHandSide(_rightHandSide) m_rightHandSide(std::move(_rightHandSide))
{ {
solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), ""); solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), "");
} }
@ -1747,11 +1765,11 @@ public:
TupleExpression( TupleExpression(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
std::vector<ASTPointer<Expression>> const& _components, std::vector<ASTPointer<Expression>> _components,
bool _isArray bool _isArray
): ):
Expression(_id, _location), Expression(_id, _location),
m_components(_components), m_components(std::move(_components)),
m_isArray(_isArray) {} m_isArray(_isArray) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1775,12 +1793,12 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
Token _operator, Token _operator,
ASTPointer<Expression> const& _subExpression, ASTPointer<Expression> _subExpression,
bool _isPrefix bool _isPrefix
): ):
Expression(_id, _location), Expression(_id, _location),
m_operator(_operator), m_operator(_operator),
m_subExpression(_subExpression), m_subExpression(std::move(_subExpression)),
m_isPrefix(_isPrefix) m_isPrefix(_isPrefix)
{ {
solAssert(TokenTraits::isUnaryOp(_operator), ""); solAssert(TokenTraits::isUnaryOp(_operator), "");
@ -1808,11 +1826,11 @@ public:
BinaryOperation( BinaryOperation(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _left, ASTPointer<Expression> _left,
Token _operator, Token _operator,
ASTPointer<Expression> const& _right ASTPointer<Expression> _right
): ):
Expression(_id, _location), m_left(_left), m_operator(_operator), m_right(_right) Expression(_id, _location), m_left(std::move(_left)), m_operator(_operator), m_right(std::move(_right))
{ {
solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), ""); solAssert(TokenTraits::isBinaryOp(_operator) || TokenTraits::isCompareOp(_operator), "");
} }
@ -1840,11 +1858,11 @@ public:
FunctionCall( FunctionCall(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _expression, ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> const& _arguments, std::vector<ASTPointer<Expression>> _arguments,
std::vector<ASTPointer<ASTString>> const& _names std::vector<ASTPointer<ASTString>> _names
): ):
Expression(_id, _location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {} Expression(_id, _location), m_expression(std::move(_expression)), m_arguments(std::move(_arguments)), m_names(std::move(_names)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1870,11 +1888,11 @@ public:
FunctionCallOptions( FunctionCallOptions(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _expression, ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> const& _options, std::vector<ASTPointer<Expression>> _options,
std::vector<ASTPointer<ASTString>> const& _names std::vector<ASTPointer<ASTString>> _names
): ):
Expression(_id, _location), m_expression(_expression), m_options(_options), m_names(_names) {} Expression(_id, _location), m_expression(std::move(_expression)), m_options(std::move(_options)), m_names(std::move(_names)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1899,9 +1917,9 @@ public:
NewExpression( NewExpression(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<TypeName> const& _typeName ASTPointer<TypeName> _typeName
): ):
Expression(_id, _location), m_typeName(_typeName) {} Expression(_id, _location), m_typeName(std::move(_typeName)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1921,9 +1939,9 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> _expression, ASTPointer<Expression> _expression,
ASTPointer<ASTString> const& _memberName ASTPointer<ASTString> _memberName
): ):
Expression(_id, _location), m_expression(_expression), m_memberName(_memberName) {} Expression(_id, _location), m_expression(std::move(_expression)), m_memberName(std::move(_memberName)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; } Expression const& expression() const { return *m_expression; }
@ -1945,10 +1963,10 @@ public:
IndexAccess( IndexAccess(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _base, ASTPointer<Expression> _base,
ASTPointer<Expression> const& _index ASTPointer<Expression> _index
): ):
Expression(_id, _location), m_base(_base), m_index(_index) {} Expression(_id, _location), m_base(std::move(_base)), m_index(std::move(_index)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -1969,11 +1987,11 @@ public:
IndexRangeAccess( IndexRangeAccess(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<Expression> const& _base, ASTPointer<Expression> _base,
ASTPointer<Expression> const& _start, ASTPointer<Expression> _start,
ASTPointer<Expression> const& _end ASTPointer<Expression> _end
): ):
Expression(_id, _location), m_base(_base), m_start(_start), m_end(_end) {} Expression(_id, _location), m_base(std::move(_base)), m_start(std::move(_start)), m_end(std::move(_end)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -2006,9 +2024,9 @@ public:
Identifier( Identifier(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ASTString> const& _name ASTPointer<ASTString> _name
): ):
PrimaryExpression(_id, _location), m_name(_name) {} PrimaryExpression(_id, _location), m_name(std::move(_name)) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;
@ -2031,10 +2049,10 @@ public:
ElementaryTypeNameExpression( ElementaryTypeNameExpression(
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
ASTPointer<ElementaryTypeName> const& _type ASTPointer<ElementaryTypeName> _type
): ):
PrimaryExpression(_id, _location), PrimaryExpression(_id, _location),
m_type(_type) m_type(std::move(_type))
{ {
} }
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
@ -2070,10 +2088,10 @@ public:
int64_t _id, int64_t _id,
SourceLocation const& _location, SourceLocation const& _location,
Token _token, Token _token,
ASTPointer<ASTString> const& _value, ASTPointer<ASTString> _value,
SubDenomination _sub = SubDenomination::None SubDenomination _sub = SubDenomination::None
): ):
PrimaryExpression(_id, _location), m_token(_token), m_value(_value), m_subDenomination(_sub) {} PrimaryExpression(_id, _location), m_token(_token), m_value(std::move(_value)), m_subDenomination(_sub) {}
void accept(ASTVisitor& _visitor) override; void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override; void accept(ASTConstVisitor& _visitor) const override;

View File

@ -128,6 +128,16 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation
std::string canonicalName; std::string canonicalName;
}; };
struct StructDeclarationAnnotation: TypeDeclarationAnnotation
{
/// Whether the struct is recursive, i.e. if the struct (recursively) contains a member that involves a struct of the same
/// type, either in a dynamic array, as member of another struct or inside a mapping.
/// Only cases in which the recursive occurrence is within a dynamic array or a mapping are valid, while direct
/// recursion immediately raises an error.
/// Will be filled in by the DeclarationTypeChecker.
std::optional<bool> recursive;
};
struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation struct ContractDefinitionAnnotation: TypeDeclarationAnnotation, StructurallyDocumentedAnnotation
{ {
/// List of functions without a body. Can also contain functions from base classes. /// List of functions without a body. Can also contain functions from base classes.
@ -234,7 +244,7 @@ struct ExpressionAnnotation: ASTAnnotation
/// Whether it is an LValue (i.e. something that can be assigned to). /// Whether it is an LValue (i.e. something that can be assigned to).
bool isLValue = false; bool isLValue = false;
/// Whether the expression is used in a context where the LValue is actually required. /// Whether the expression is used in a context where the LValue is actually required.
bool lValueRequested = false; bool willBeWrittenTo = false;
/// Whether the expression is an lvalue that is only assigned. /// Whether the expression is an lvalue that is only assigned.
/// Would be false for --, ++, delete, +=, -=, .... /// Would be false for --, ++, delete, +=, -=, ....
bool lValueOfOrdinaryAssignment = false; bool lValueOfOrdinaryAssignment = false;
@ -248,6 +258,8 @@ struct IdentifierAnnotation: ExpressionAnnotation
{ {
/// Referenced declaration, set at latest during overload resolution stage. /// Referenced declaration, set at latest during overload resolution stage.
Declaration const* referencedDeclaration = nullptr; Declaration const* referencedDeclaration = nullptr;
/// List of possible declarations it could refer to (can contain duplicates).
std::vector<Declaration const*> candidateDeclarations;
/// List of possible declarations it could refer to. /// List of possible declarations it could refer to.
std::vector<Declaration const*> overloadedDeclarations; std::vector<Declaration const*> overloadedDeclarations;
}; };

View File

@ -34,6 +34,7 @@
#include <boost/algorithm/string/join.hpp> #include <boost/algorithm/string/join.hpp>
#include <boost/range/algorithm/sort.hpp> #include <boost/range/algorithm/sort.hpp>
#include <utility>
#include <vector> #include <vector>
#include <algorithm> #include <algorithm>
@ -45,7 +46,7 @@ namespace solidity::frontend
ASTJsonConverter::ASTJsonConverter(bool _legacy, map<string, unsigned> _sourceIndices): ASTJsonConverter::ASTJsonConverter(bool _legacy, map<string, unsigned> _sourceIndices):
m_legacy(_legacy), m_legacy(_legacy),
m_sourceIndices(_sourceIndices) m_sourceIndices(std::move(_sourceIndices))
{ {
} }
@ -181,7 +182,7 @@ void ASTJsonConverter::appendExpressionAttributes(
make_pair("isConstant", _annotation.isConstant), make_pair("isConstant", _annotation.isConstant),
make_pair("isPure", _annotation.isPure), make_pair("isPure", _annotation.isPure),
make_pair("isLValue", _annotation.isLValue), make_pair("isLValue", _annotation.isLValue),
make_pair("lValueRequested", _annotation.lValueRequested), make_pair("lValueRequested", _annotation.willBeWrittenTo),
make_pair("argumentTypes", typePointerToJson(_annotation.arguments)) make_pair("argumentTypes", typePointerToJson(_annotation.arguments))
}; };
_attributes += exprAttributes; _attributes += exprAttributes;
@ -378,6 +379,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("name", _node.name()), make_pair("name", _node.name()),
make_pair("typeName", toJsonOrNull(_node.typeName())), make_pair("typeName", toJsonOrNull(_node.typeName())),
make_pair("constant", _node.isConstant()), make_pair("constant", _node.isConstant()),
make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())),
make_pair("stateVariable", _node.isStateVariable()), make_pair("stateVariable", _node.isStateVariable()),
make_pair("storageLocation", location(_node.referenceLocation())), make_pair("storageLocation", location(_node.referenceLocation())),
make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue), make_pair("overrides", _node.overrides() ? toJson(*_node.overrides()) : Json::nullValue),

View File

@ -411,11 +411,24 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
{ {
astAssert(_node["name"].isString(), "Expected 'name' to be a string!"); astAssert(_node["name"].isString(), "Expected 'name' to be a string!");
VariableDeclaration::Constantness constantness{}; VariableDeclaration::Mutability mutability{};
if (memberAsBool(_node, "constant")) astAssert(member(_node, "mutability").isString(), "'mutability' expected to be string.");
constantness = VariableDeclaration::Constantness::Constant; string const mutabilityStr = member(_node, "mutability").asString();
if (mutabilityStr == "constant")
{
mutability = VariableDeclaration::Mutability::Constant;
astAssert(memberAsBool(_node, "constant"), "");
}
else else
constantness = VariableDeclaration::Constantness::Mutable; {
astAssert(!memberAsBool(_node, "constant"), "");
if (mutabilityStr == "mutable")
mutability = VariableDeclaration::Mutability::Mutable;
else if (mutabilityStr == "immutable")
mutability = VariableDeclaration::Mutability::Immutable;
else
astAssert(false, "");
}
return createASTNode<VariableDeclaration>( return createASTNode<VariableDeclaration>(
_node, _node,
@ -425,7 +438,7 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
visibility(_node), visibility(_node),
memberAsBool(_node, "stateVariable"), memberAsBool(_node, "stateVariable"),
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false, _node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
constantness, mutability,
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")), _node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
location(_node) location(_node)
); );
@ -495,7 +508,7 @@ ASTPointer<UserDefinedTypeName> ASTJsonImporter::createUserDefinedTypeName(Json:
string nameString = member(_node, "name").asString(); string nameString = member(_node, "name").asString();
boost::algorithm::split(strs, nameString, boost::is_any_of(".")); boost::algorithm::split(strs, nameString, boost::is_any_of("."));
for (string s: strs) for (string s: strs)
namePath.push_back(ASTString(s)); namePath.emplace_back(s);
return createASTNode<UserDefinedTypeName>( return createASTNode<UserDefinedTypeName>(
_node, _node,
namePath namePath

View File

@ -23,9 +23,11 @@
#pragma once #pragma once
#include <libsolidity/ast/AST.h> #include <libsolidity/ast/AST.h>
#include <functional> #include <functional>
#include <string> #include <string>
#include <vector> #include <vector>
#include <utility>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -299,7 +301,7 @@ public:
SimpleASTVisitor( SimpleASTVisitor(
std::function<bool(ASTNode const&)> _onVisit, std::function<bool(ASTNode const&)> _onVisit,
std::function<void(ASTNode const&)> _onEndVisit std::function<void(ASTNode const&)> _onEndVisit
): m_onVisit(_onVisit), m_onEndVisit(_onEndVisit) {} ): m_onVisit(std::move(_onVisit)), m_onEndVisit(std::move(_onEndVisit)) {}
protected: protected:
bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; } bool visitNode(ASTNode const& _n) override { return m_onVisit ? m_onVisit(_n) : true; }
@ -329,7 +331,7 @@ public:
ASTReduce( ASTReduce(
std::function<bool(ASTNode const&)> _onNode, std::function<bool(ASTNode const&)> _onNode,
std::function<void(ASTNode const&, ASTNode const&)> _onEdge std::function<void(ASTNode const&, ASTNode const&)> _onEdge
): m_onNode(_onNode), m_onEdge(_onEdge) ): m_onNode(std::move(_onNode)), m_onEdge(std::move(_onEdge))
{ {
} }

View File

@ -26,6 +26,8 @@
#include <liblangutil/SourceLocation.h> #include <liblangutil/SourceLocation.h>
#include <libyul/AsmDataForward.h> #include <libyul/AsmDataForward.h>
#include <utility>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -35,7 +37,7 @@ namespace solidity::frontend
class AsmJsonImporter class AsmJsonImporter
{ {
public: public:
explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(_sourceName) {} explicit AsmJsonImporter(std::string _sourceName) : m_sourceName(std::move(_sourceName)) {}
yul::Block createBlock(Json::Value const& _node); yul::Block createBlock(Json::Value const& _node);
private: private:

View File

@ -43,6 +43,7 @@
#include <boost/range/algorithm/copy.hpp> #include <boost/range/algorithm/copy.hpp>
#include <limits> #include <limits>
#include <utility>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -1253,8 +1254,8 @@ StringLiteralType::StringLiteralType(Literal const& _literal):
{ {
} }
StringLiteralType::StringLiteralType(string const& _value): StringLiteralType::StringLiteralType(string _value):
m_value{_value} m_value{std::move(_value)}
{ {
} }
@ -1648,12 +1649,50 @@ bool ArrayType::operator==(Type const& _other) const
return isDynamicallySized() || length() == other.length(); return isDynamicallySized() || length() == other.length();
} }
bool ArrayType::validForCalldata() const BoolResult ArrayType::validForLocation(DataLocation _loc) const
{ {
if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType())) if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType()))
if (!arrayBaseType->validForCalldata()) {
return false; BoolResult result = arrayBaseType->validForLocation(_loc);
return isDynamicallySized() || unlimitedStaticCalldataSize(true) <= numeric_limits<unsigned>::max(); if (!result)
return result;
}
if (isDynamicallySized())
return true;
switch (_loc)
{
case DataLocation::Memory:
{
bigint size = bigint(length());
auto type = m_baseType;
while (auto arrayType = dynamic_cast<ArrayType const*>(type))
{
if (arrayType->isDynamicallySized())
break;
else
{
size *= arrayType->length();
type = arrayType->baseType();
}
}
if (type->isDynamicallySized())
size *= type->memoryHeadSize();
else
size *= type->memoryDataSize();
if (size >= numeric_limits<unsigned>::max())
return BoolResult::err("Type too large for memory.");
break;
}
case DataLocation::CallData:
{
if (unlimitedStaticCalldataSize(true) >= numeric_limits<unsigned>::max())
return BoolResult::err("Type too large for calldata.");
break;
}
case DataLocation::Storage:
break;
}
return true;
} }
bigint ArrayType::unlimitedStaticCalldataSize(bool _padded) const bigint ArrayType::unlimitedStaticCalldataSize(bool _padded) const
@ -2174,93 +2213,119 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
TypeResult StructType::interfaceType(bool _inLibrary) const TypeResult StructType::interfaceType(bool _inLibrary) const
{ {
if (_inLibrary && m_interfaceType_library.has_value()) if (!_inLibrary)
return *m_interfaceType_library; {
if (!m_interfaceType.has_value())
if (!_inLibrary && m_interfaceType.has_value()) {
if (recursive())
m_interfaceType = TypeResult::err("Recursive type not allowed for public or external contract functions.");
else
{
TypeResult result{TypePointer{}};
for (ASTPointer<VariableDeclaration> const& member: m_struct.members())
{
if (!member->annotation().type)
{
result = TypeResult::err("Invalid type!");
break;
}
auto interfaceType = member->annotation().type->interfaceType(false);
if (!interfaceType.get())
{
solAssert(!interfaceType.message().empty(), "Expected detailed error message!");
result = interfaceType;
break;
}
}
if (result.message().empty())
m_interfaceType = TypeProvider::withLocation(this, DataLocation::Memory, true);
else
m_interfaceType = result;
}
}
return *m_interfaceType; return *m_interfaceType;
}
else if (m_interfaceType_library.has_value())
return *m_interfaceType_library;
TypeResult result{TypePointer{}}; TypeResult result{TypePointer{}};
m_recursive = false; util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}};
breadthFirstSearch.run(
auto visitor = [&]( [&](StructDefinition const* _struct, auto&& _addChild) {
StructDefinition const& _struct, // Check that all members have interface types.
util::CycleDetector<StructDefinition>& _cycleDetector, // Return an error if at least one struct member does not have a type.
size_t /*_depth*/ // This might happen, for example, if the type of the member does not exist.
) for (ASTPointer<VariableDeclaration> const& variable: _struct->members())
{
// Check that all members have interface types.
// Return an error if at least one struct member does not have a type.
// This might happen, for example, if the type of the member does not exist.
for (ASTPointer<VariableDeclaration> const& variable: _struct.members())
{
// If the struct member does not have a type return false.
// A TypeError is expected in this case.
if (!variable->annotation().type)
{
result = TypeResult::err("Invalid type!");
return;
}
Type const* memberType = variable->annotation().type;
while (dynamic_cast<ArrayType const*>(memberType))
memberType = dynamic_cast<ArrayType const*>(memberType)->baseType();
if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
if (
innerStruct->m_recursive == true ||
_cycleDetector.run(innerStruct->structDefinition())
)
{ {
m_recursive = true; // If the struct member does not have a type return false.
if (_inLibrary && location() == DataLocation::Storage) // A TypeError is expected in this case.
continue; if (!variable->annotation().type)
else
{ {
result = TypeResult::err("Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions."); result = TypeResult::err("Invalid type!");
breadthFirstSearch.abort();
return; return;
} }
}
auto iType = memberType->interfaceType(_inLibrary); Type const* memberType = variable->annotation().type;
if (!iType.get())
{ while (dynamic_cast<ArrayType const*>(memberType))
solAssert(!iType.message().empty(), "Expected detailed error message!"); memberType = dynamic_cast<ArrayType const*>(memberType)->baseType();
result = iType;
return; if (StructType const* innerStruct = dynamic_cast<StructType const*>(memberType))
{
if (innerStruct->recursive() && !(_inLibrary && location() == DataLocation::Storage))
{
result = TypeResult::err(
"Recursive structs can only be passed as storage pointers to libraries, not as memory objects to contract functions."
);
breadthFirstSearch.abort();
return;
}
else
_addChild(&innerStruct->structDefinition());
}
else
{
auto iType = memberType->interfaceType(_inLibrary);
if (!iType.get())
{
solAssert(!iType.message().empty(), "Expected detailed error message!");
result = iType;
breadthFirstSearch.abort();
return;
}
}
} }
} }
}; );
m_recursive = m_recursive.value() || (util::CycleDetector<StructDefinition>(visitor).run(structDefinition()) != nullptr); if (!result.message().empty())
return result;
std::string const recursiveErrMsg = "Recursive type not allowed for public or external contract functions."; if (location() == DataLocation::Storage)
m_interfaceType_library = this;
if (_inLibrary)
{
if (!result.message().empty())
m_interfaceType_library = result;
else if (location() == DataLocation::Storage)
m_interfaceType_library = this;
else
m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
if (m_recursive.value())
m_interfaceType = TypeResult::err(recursiveErrMsg);
return *m_interfaceType_library;
}
if (m_recursive.value())
m_interfaceType = TypeResult::err(recursiveErrMsg);
else if (!result.message().empty())
m_interfaceType = result;
else else
m_interfaceType = TypeProvider::withLocation(this, DataLocation::Memory, true); m_interfaceType_library = TypeProvider::withLocation(this, DataLocation::Memory, true);
return *m_interfaceType_library;
}
return *m_interfaceType; BoolResult StructType::validForLocation(DataLocation _loc) const
{
for (auto const& member: m_struct.members())
if (auto referenceType = dynamic_cast<ReferenceType const*>(member->annotation().type))
{
BoolResult result = referenceType->validForLocation(_loc);
if (!result)
return result;
}
return true;
}
bool StructType::recursive() const
{
solAssert(m_struct.annotation().recursive.has_value(), "Called StructType::recursive() before DeclarationTypeChecker.");
return *m_struct.annotation().recursive;
} }
std::unique_ptr<ReferenceType> StructType::copyForLocation(DataLocation _location, bool _isPointer) const std::unique_ptr<ReferenceType> StructType::copyForLocation(DataLocation _location, bool _isPointer) const
@ -2643,21 +2708,11 @@ FunctionType::FunctionType(FunctionTypeName const& _typeName):
for (auto const& t: _typeName.parameterTypes()) for (auto const& t: _typeName.parameterTypes())
{ {
solAssert(t->annotation().type, "Type not set for parameter."); solAssert(t->annotation().type, "Type not set for parameter.");
if (m_kind == Kind::External)
solAssert(
t->annotation().type->interfaceType(false).get(),
"Internal type used as parameter for external function."
);
m_parameterTypes.push_back(t->annotation().type); m_parameterTypes.push_back(t->annotation().type);
} }
for (auto const& t: _typeName.returnParameterTypes()) for (auto const& t: _typeName.returnParameterTypes())
{ {
solAssert(t->annotation().type, "Type not set for return parameter."); solAssert(t->annotation().type, "Type not set for return parameter.");
if (m_kind == Kind::External)
solAssert(
t->annotation().type->interfaceType(false).get(),
"Internal type used as return parameter for external function."
);
m_returnParameterTypes.push_back(t->annotation().type); m_returnParameterTypes.push_back(t->annotation().type);
} }
@ -3112,6 +3167,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _sco
TypePointer FunctionType::encodingType() const TypePointer FunctionType::encodingType() const
{ {
if (m_gasSet || m_valueSet)
return nullptr;
// Only external functions can be encoded, internal functions cannot leave code boundaries. // Only external functions can be encoded, internal functions cannot leave code boundaries.
if (m_kind == Kind::External) if (m_kind == Kind::External)
return this; return this;

View File

@ -38,6 +38,7 @@
#include <optional> #include <optional>
#include <set> #include <set>
#include <string> #include <string>
#include <utility>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -92,8 +93,8 @@ class MemberList
public: public:
struct Member struct Member
{ {
Member(std::string const& _name, Type const* _type, Declaration const* _declaration = nullptr): Member(std::string _name, Type const* _type, Declaration const* _declaration = nullptr):
name(_name), name(std::move(_name)),
type(_type), type(_type),
declaration(_declaration) declaration(_declaration)
{ {
@ -106,7 +107,7 @@ public:
using MemberMap = std::vector<Member>; using MemberMap = std::vector<Member>;
explicit MemberList(MemberMap const& _members): m_memberTypes(_members) {} explicit MemberList(MemberMap _members): m_memberTypes(std::move(_members)) {}
void combine(MemberList const& _other); void combine(MemberList const& _other);
TypePointer memberType(std::string const& _name) const TypePointer memberType(std::string const& _name) const
@ -520,8 +521,8 @@ private:
class RationalNumberType: public Type class RationalNumberType: public Type
{ {
public: public:
explicit RationalNumberType(rational const& _value, Type const* _compatibleBytesType = nullptr): explicit RationalNumberType(rational _value, Type const* _compatibleBytesType = nullptr):
m_value(_value), m_compatibleBytesType(_compatibleBytesType) m_value(std::move(_value)), m_compatibleBytesType(_compatibleBytesType)
{} {}
Category category() const override { return Category::RationalNumber; } Category category() const override { return Category::RationalNumber; }
@ -543,7 +544,7 @@ public:
/// @returns the smallest integer type that can hold the value or an empty pointer if not possible. /// @returns the smallest integer type that can hold the value or an empty pointer if not possible.
IntegerType const* integerType() const; IntegerType const* integerType() const;
/// @returns the smallest fixed type that can hold the value or incurs the least precision loss, /// @returns the smallest fixed type that can hold the value or incurs the least precision loss,
/// unless the value was truncated, then a suitable type will be chosen to indicate such event. /// unless the value was truncated, then a suitable type will be chosen to indicate such event.
/// If the integer part does not fit, returns an empty pointer. /// If the integer part does not fit, returns an empty pointer.
FixedPointType const* fixedPointType() const; FixedPointType const* fixedPointType() const;
@ -582,7 +583,7 @@ class StringLiteralType: public Type
{ {
public: public:
explicit StringLiteralType(Literal const& _literal); explicit StringLiteralType(Literal const& _literal);
explicit StringLiteralType(std::string const& _value); explicit StringLiteralType(std::string _value);
Category category() const override { return Category::StringLiteral; } Category category() const override { return Category::StringLiteral; }
@ -705,6 +706,9 @@ public:
/// never change the contents of the original value. /// never change the contents of the original value.
bool isPointer() const; bool isPointer() const;
/// @returns true if this is valid to be stored in data location _loc
virtual BoolResult validForLocation(DataLocation _loc) const = 0;
bool operator==(ReferenceType const& _other) const bool operator==(ReferenceType const& _other) const
{ {
return location() == _other.location() && isPointer() == _other.isPointer(); return location() == _other.location() && isPointer() == _other.isPointer();
@ -744,11 +748,11 @@ public:
} }
/// Constructor for a fixed-size array type ("type[20]") /// Constructor for a fixed-size array type ("type[20]")
ArrayType(DataLocation _location, Type const* _baseType, u256 const& _length): ArrayType(DataLocation _location, Type const* _baseType, u256 _length):
ReferenceType(_location), ReferenceType(_location),
m_baseType(copyForLocationIfReference(_baseType)), m_baseType(copyForLocationIfReference(_baseType)),
m_hasDynamicLength(false), m_hasDynamicLength(false),
m_length(_length) m_length(std::move(_length))
{} {}
Category category() const override { return Category::Array; } Category category() const override { return Category::Array; }
@ -771,8 +775,7 @@ public:
TypePointer decodingType() const override; TypePointer decodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override; TypeResult interfaceType(bool _inLibrary) const override;
/// @returns true if this is valid to be stored in calldata BoolResult validForLocation(DataLocation _loc) const override;
bool validForCalldata() const;
/// @returns true if this is a byte array or a string /// @returns true if this is a byte array or a string
bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; } bool isByteArray() const { return m_arrayKind != ArrayKind::Ordinary; }
@ -826,8 +829,7 @@ public:
bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); } bool canLiveOutsideStorage() const override { return m_arrayType.canLiveOutsideStorage(); }
std::string toString(bool _short) const override; std::string toString(bool _short) const override;
/// @returns true if this is valid to be stored in calldata BoolResult validForLocation(DataLocation _loc) const override { return m_arrayType.validForLocation(_loc); }
bool validForCalldata() const { return m_arrayType.validForCalldata(); }
ArrayType const& arrayType() const { return m_arrayType; } ArrayType const& arrayType() const { return m_arrayType; }
u256 memoryDataSize() const override { solAssert(false, ""); } u256 memoryDataSize() const override { solAssert(false, ""); }
@ -933,15 +935,9 @@ public:
Type const* encodingType() const override; Type const* encodingType() const override;
TypeResult interfaceType(bool _inLibrary) const override; TypeResult interfaceType(bool _inLibrary) const override;
bool recursive() const BoolResult validForLocation(DataLocation _loc) const override;
{
if (m_recursive.has_value())
return m_recursive.value();
interfaceType(false); bool recursive() const;
return m_recursive.value();
}
std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override; std::unique_ptr<ReferenceType> copyForLocation(DataLocation _location, bool _isPointer) const override;
@ -970,7 +966,6 @@ private:
// Caches for interfaceType(bool) // Caches for interfaceType(bool)
mutable std::optional<TypeResult> m_interfaceType; mutable std::optional<TypeResult> m_interfaceType;
mutable std::optional<TypeResult> m_interfaceType_library; mutable std::optional<TypeResult> m_interfaceType_library;
mutable std::optional<bool> m_recursive;
}; };
/** /**
@ -1131,8 +1126,8 @@ public:
/// Detailed constructor, use with care. /// Detailed constructor, use with care.
FunctionType( FunctionType(
TypePointers const& _parameterTypes, TypePointers _parameterTypes,
TypePointers const& _returnParameterTypes, TypePointers _returnParameterTypes,
strings _parameterNames = strings(), strings _parameterNames = strings(),
strings _returnParameterNames = strings(), strings _returnParameterNames = strings(),
Kind _kind = Kind::Internal, Kind _kind = Kind::Internal,
@ -1144,10 +1139,10 @@ public:
bool _saltSet = false, bool _saltSet = false,
bool _bound = false bool _bound = false
): ):
m_parameterTypes(_parameterTypes), m_parameterTypes(std::move(_parameterTypes)),
m_returnParameterTypes(_returnParameterTypes), m_returnParameterTypes(std::move(_returnParameterTypes)),
m_parameterNames(_parameterNames), m_parameterNames(std::move(_parameterNames)),
m_returnParameterNames(_returnParameterNames), m_returnParameterNames(std::move(_returnParameterNames)),
m_kind(_kind), m_kind(_kind),
m_stateMutability(_stateMutability), m_stateMutability(_stateMutability),
m_arbitraryParameters(_arbitraryParameters), m_arbitraryParameters(_arbitraryParameters),

View File

@ -843,7 +843,7 @@ string ABIFunctions::abiEncodingFunctionStruct(
if (dynamicMember) if (dynamicMember)
solAssert(dynamic, ""); solAssert(dynamic, "");
members.push_back({}); members.emplace_back();
members.back()["preprocess"] = ""; members.back()["preprocess"] = "";
switch (_from.location()) switch (_from.location())
@ -1336,7 +1336,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name))); memberTempl("memoryOffset", toCompactHexWithPrefix(_type.memoryOffsetOfMember(member.name)));
memberTempl("abiDecode", abiDecodingFunction(*member.type, _fromMemory, false)); memberTempl("abiDecode", abiDecodingFunction(*member.type, _fromMemory, false));
members.push_back({}); members.emplace_back();
members.back()["decode"] = memberTempl.render(); members.back()["decode"] = memberTempl.render();
members.back()["memberName"] = member.name; members.back()["memberName"] = member.name;
headPos += decodingType->calldataHeadSize(); headPos += decodingType->calldataHeadSize();

View File

@ -1108,12 +1108,12 @@ void CompilerUtils::convertType(
// Value shrank // Value shrank
for (unsigned j = targetSize; j < sourceSize; ++j) for (unsigned j = targetSize; j < sourceSize; ++j)
{ {
moveToStackTop(depth - 1, 1); moveToStackTop(depth + targetSize - sourceSize, 1);
m_context << Instruction::POP; m_context << Instruction::POP;
} }
// Value grew // Value grew
if (targetSize > sourceSize) if (targetSize > sourceSize)
moveIntoStack(depth + targetSize - sourceSize - 1, targetSize - sourceSize); moveIntoStack(depth - sourceSize, targetSize - sourceSize);
} }
} }
depth -= sourceSize; depth -= sourceSize;

View File

@ -949,43 +949,7 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
// Try to decode the error message. // Try to decode the error message.
// If this fails, leaves 0 on the stack, otherwise the pointer to the data string. // If this fails, leaves 0 on the stack, otherwise the pointer to the data string.
m_context << u256(0); m_context.callYulFunction(m_context.utilFunctions().tryDecodeErrorMessageFunction(), 0, 1);
m_context.appendInlineAssembly(
util::Whiskers(R"({
data := mload(0x40)
mstore(data, 0)
for {} 1 {} {
if lt(returndatasize(), 0x44) { data := 0 break }
returndatacopy(0, 0, 4)
let sig := <getSig>
if iszero(eq(sig, 0x<ErrorSignature>)) { data := 0 break }
returndatacopy(data, 4, sub(returndatasize(), 4))
let offset := mload(data)
if or(
gt(offset, 0xffffffffffffffff),
gt(add(offset, 0x24), returndatasize())
) {
data := 0
break
}
let msg := add(data, offset)
let length := mload(msg)
if gt(length, 0xffffffffffffffff) { data := 0 break }
let end := add(add(msg, 0x20), length)
if gt(end, add(data, returndatasize())) { data := 0 break }
mstore(0x40, and(add(end, 0x1f), not(0x1f)))
data := msg
break
}
})")
("ErrorSignature", errorHash)
("getSig",
m_context.evmVersion().hasBitwiseShifting() ?
"shr(224, mload(0))" :
"div(mload(0), " + (u256(1) << 224).str() + ")"
).render(),
{"data"}
);
m_context << Instruction::DUP1; m_context << Instruction::DUP1;
AssemblyItem decodeSuccessTag = m_context.appendConditionalJump(); AssemblyItem decodeSuccessTag = m_context.appendConditionalJump();
m_context << Instruction::POP; m_context << Instruction::POP;

View File

@ -22,12 +22,14 @@
#include <libsolidity/codegen/ExpressionCompiler.h> #include <libsolidity/codegen/ExpressionCompiler.h>
#include <libsolidity/ast/AST.h> #include <libsolidity/codegen/ReturnInfo.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libsolidity/codegen/CompilerContext.h> #include <libsolidity/codegen/CompilerContext.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/LValue.h> #include <libsolidity/codegen/LValue.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libevmasm/GasMeter.h> #include <libevmasm/GasMeter.h>
#include <libsolutil/Common.h> #include <libsolutil/Common.h>
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
@ -346,15 +348,15 @@ bool ExpressionCompiler::visit(TupleExpression const& _tuple)
if (component) if (component)
{ {
component->accept(*this); component->accept(*this);
if (_tuple.annotation().lValueRequested) if (_tuple.annotation().willBeWrittenTo)
{ {
solAssert(!!m_currentLValue, ""); solAssert(!!m_currentLValue, "");
lvalues.push_back(move(m_currentLValue)); lvalues.push_back(move(m_currentLValue));
} }
} }
else if (_tuple.annotation().lValueRequested) else if (_tuple.annotation().willBeWrittenTo)
lvalues.push_back(unique_ptr<LValue>()); lvalues.push_back(unique_ptr<LValue>());
if (_tuple.annotation().lValueRequested) if (_tuple.annotation().willBeWrittenTo)
{ {
if (_tuple.components().size() == 1) if (_tuple.components().size() == 1)
m_currentLValue = move(lvalues[0]); m_currentLValue = move(lvalues[0]);
@ -2185,30 +2187,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
solAssert(!_functionType.isBareCall(), ""); solAssert(!_functionType.isBareCall(), "");
} }
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); ReturnInfo const returnInfo{m_context.evmVersion(), _functionType};
unsigned retSize = 0; bool const haveReturndatacopy = m_context.evmVersion().supportsReturndata();
bool dynamicReturnSize = false; unsigned const retSize = returnInfo.estimatedReturnSize;
TypePointers returnTypes; bool const dynamicReturnSize = returnInfo.dynamicReturnSize;
if (!returnSuccessConditionAndReturndata) TypePointers const& returnTypes = returnInfo.returnTypes;
{
if (haveReturndatacopy)
returnTypes = _functionType.returnParameterTypes();
else
returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes();
for (auto const& retType: returnTypes)
if (retType->isDynamicallyEncoded())
{
solAssert(haveReturndatacopy, "");
dynamicReturnSize = true;
retSize = 0;
break;
}
else if (retType->decodingType())
retSize += retType->decodingType()->calldataEncodedSize();
else
retSize += retType->calldataEncodedSize();
}
// Evaluate arguments. // Evaluate arguments.
TypePointers argumentTypes; TypePointers argumentTypes;

View File

@ -148,7 +148,7 @@ void ExpressionCompiler::setLValue(Expression const& _expression, Arguments cons
{ {
solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one."); solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one.");
std::unique_ptr<LValueType> lvalue = std::make_unique<LValueType>(m_context, _arguments...); std::unique_ptr<LValueType> lvalue = std::make_unique<LValueType>(m_context, _arguments...);
if (_expression.annotation().lValueRequested) if (_expression.annotation().willBeWrittenTo)
m_currentLValue = move(lvalue); m_currentLValue = move(lvalue);
else else
lvalue->retrieveValue(_expression.location(), true); lvalue->retrieveValue(_expression.location(), true);

View File

@ -34,6 +34,7 @@ string MultiUseYulFunctionCollector::requestedFunctions()
{ {
string result; string result;
for (auto const& f: m_requestedFunctions) for (auto const& f: m_requestedFunctions)
// std::map guarantees ascending order when iterating through its keys.
result += f.second; result += f.second;
m_requestedFunctions.clear(); m_requestedFunctions.clear();
return result; return result;

View File

@ -41,10 +41,15 @@ public:
std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator); std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator);
/// @returns concatenation of all generated functions. /// @returns concatenation of all generated functions.
/// Guarantees that the order of functions in the generated code is deterministic and
/// platform-independent.
/// Clears the internal list, i.e. calling it again will result in an /// Clears the internal list, i.e. calling it again will result in an
/// empty return value. /// empty return value.
std::string requestedFunctions(); std::string requestedFunctions();
/// @returns true IFF a function with the specified name has already been collected.
bool contains(std::string const& _name) const { return m_requestedFunctions.count(_name) > 0; }
private: private:
/// Map from function name to code for a multi-use function. /// Map from function name to code for a multi-use function.
std::map<std::string, std::string> m_requestedFunctions; std::map<std::string, std::string> m_requestedFunctions;

View File

@ -0,0 +1,55 @@
/*
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/>.
*/
#include <libsolidity/codegen/ReturnInfo.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/ast/AST.h>
using namespace solidity::frontend;
using namespace solidity::langutil;
ReturnInfo::ReturnInfo(EVMVersion const& _evmVersion, FunctionType const& _functionType)
{
FunctionType::Kind const funKind = _functionType.kind();
bool const haveReturndatacopy = _evmVersion.supportsReturndata();
bool const returnSuccessConditionAndReturndata =
funKind == FunctionType::Kind::BareCall ||
funKind == FunctionType::Kind::BareDelegateCall ||
funKind == FunctionType::Kind::BareStaticCall;
if (!returnSuccessConditionAndReturndata)
{
if (haveReturndatacopy)
returnTypes = _functionType.returnParameterTypes();
else
returnTypes = _functionType.returnParameterTypesWithoutDynamicTypes();
for (auto const& retType: returnTypes)
if (retType->isDynamicallyEncoded())
{
solAssert(haveReturndatacopy, "");
dynamicReturnSize = true;
estimatedReturnSize = 0;
break;
}
else if (retType->decodingType())
estimatedReturnSize += retType->decodingType()->calldataEncodedSize();
else
estimatedReturnSize += retType->calldataEncodedSize();
}
}

View File

@ -0,0 +1,47 @@
/*
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/>.
*/
/**
* Component that computes information relevant during decoding an external function
* call's return values.
*/
#pragma once
#include <liblangutil/EVMVersion.h>
#include <libsolidity/ast/Types.h>
namespace solidity::frontend
{
/**
* Computes and holds information relevant during decoding an external function
* call's return values.
*/
struct ReturnInfo
{
ReturnInfo(langutil::EVMVersion const& _evmVersion, FunctionType const& _functionType);
/// Vector of TypePointer, for each return variable. Dynamic types are already replaced if required.
TypePointers returnTypes = {};
/// Boolean, indicating whether or not return size is only known at runtime.
bool dynamicReturnSize = false;
/// Contains the at compile time estimated return size.
unsigned estimatedReturnSize = 0;
};
}

View File

@ -2079,7 +2079,7 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
{ {
conversions += conversions +=
suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) + suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) +
" := " + (toComponent->sizeOnStack() > 0 ? " := " : "") +
conversionFunction(*fromComponent, *toComponent) + conversionFunction(*fromComponent, *toComponent) +
"(" + "(" +
suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) + suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) +
@ -2089,12 +2089,13 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
sourceStackSize += fromComponent->sizeOnStack(); sourceStackSize += fromComponent->sizeOnStack();
} }
return Whiskers(R"( return Whiskers(R"(
function <functionName>(<values>) -> <converted> { function <functionName>(<values>) <arrow> <converted> {
<conversions> <conversions>
} }
)") )")
("functionName", functionName) ("functionName", functionName)
("values", suffixedVariableNameList("value", 0, sourceStackSize)) ("values", suffixedVariableNameList("value", 0, sourceStackSize))
("arrow", destStackSize > 0 ? "->" : "")
("converted", suffixedVariableNameList("converted", 0, destStackSize)) ("converted", suffixedVariableNameList("converted", 0, destStackSize))
("conversions", conversions) ("conversions", conversions)
.render(); .render();
@ -2252,3 +2253,84 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _message)
{ {
return revertReasonIfDebug(m_revertStrings, _message); return revertReasonIfDebug(m_revertStrings, _message);
} }
string YulUtilFunctions::tryDecodeErrorMessageFunction()
{
string const functionName = "try_decode_error_message";
return m_functionCollector.createFunction(functionName, [&]() {
return util::Whiskers(R"(
function <functionName>() -> ret {
if lt(returndatasize(), 0x44) { leave }
returndatacopy(0, 0, 4)
let sig := <shr224>(mload(0))
if iszero(eq(sig, 0x<ErrorSignature>)) { leave }
let data := mload(<freeMemoryPointer>)
returndatacopy(data, 4, sub(returndatasize(), 4))
let offset := mload(data)
if or(
gt(offset, 0xffffffffffffffff),
gt(add(offset, 0x24), returndatasize())
) {
leave
}
let msg := add(data, offset)
let length := mload(msg)
if gt(length, 0xffffffffffffffff) { leave }
let end := add(add(msg, 0x20), length)
if gt(end, add(data, returndatasize())) { leave }
mstore(<freeMemoryPointer>, add(add(msg, 0x20), <roundUp>(length)))
ret := msg
}
)")
("functionName", functionName)
("shr224", shiftRightFunction(224))
("ErrorSignature", FixedHash<4>(util::keccak256("Error(string)")).hex())
("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer))
("roundUp", roundUpFunction())
.render();
});
}
string YulUtilFunctions::extractReturndataFunction()
{
string const functionName = "extract_returndata";
return m_functionCollector.createFunction(functionName, [&]() {
return util::Whiskers(R"(
function <functionName>() -> data {
<?supportsReturndata>
switch returndatasize()
case 0 {
data := <emptyArray>()
}
default {
// allocate some memory into data of size returndatasize() + PADDING
data := <allocate>(<roundUp>(add(returndatasize(), 0x20)))
// store array length into the front
mstore(data, returndatasize())
// append to data
returndatacopy(add(data, 0x20), 0, returndatasize())
}
<!supportsReturndata>
data := <emptyArray>()
</supportsReturndata>
}
)")
("functionName", functionName)
("supportsReturndata", m_evmVersion.supportsReturndata())
("allocate", allocationFunction())
("roundUp", roundUpFunction())
("emptyArray", zeroValueFunction(*TypeProvider::bytesMemory()))
.render();
});
}

View File

@ -22,6 +22,7 @@
#include <liblangutil/EVMVersion.h> #include <liblangutil/EVMVersion.h>
#include <libsolidity/ast/Types.h>
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h> #include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/interface/DebugSettings.h> #include <libsolidity/interface/DebugSettings.h>
@ -322,6 +323,20 @@ public:
static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = ""); static std::string revertReasonIfDebug(RevertStrings revertStrings, std::string const& _message = "");
std::string revertReasonIfDebug(std::string const& _message = ""); std::string revertReasonIfDebug(std::string const& _message = "");
/// Returns the name of a function that decodes an error message.
/// signature: () -> arrayPtr
///
/// Returns a newly allocated `bytes memory` array containing the decoded error message
/// or 0 on failure.
std::string tryDecodeErrorMessageFunction();
/// Returns a function name that returns a newly allocated `bytes` array that contains the return data.
///
/// If returndatacopy() is not supported by the underlying target, a empty array will be returned instead.
std::string extractReturndataFunction();
private: private:
/// Special case of conversionFunction - handles everything that does not /// Special case of conversionFunction - handles everything that does not
/// use exactly one variable to hold the value. /// use exactly one variable to hold the value.

View File

@ -32,6 +32,25 @@ using namespace solidity;
using namespace solidity::util; using namespace solidity::util;
using namespace solidity::frontend; using namespace solidity::frontend;
string IRGenerationContext::enqueueFunctionForCodeGeneration(FunctionDefinition const& _function)
{
string name = functionName(_function);
if (!m_functions.contains(name))
m_functionGenerationQueue.insert(&_function);
return name;
}
FunctionDefinition const* IRGenerationContext::dequeueFunctionForCodeGeneration()
{
solAssert(!m_functionGenerationQueue.empty(), "");
FunctionDefinition const* result = *m_functionGenerationQueue.begin();
m_functionGenerationQueue.erase(m_functionGenerationQueue.begin());
return result;
}
ContractDefinition const& IRGenerationContext::mostDerivedContract() const ContractDefinition const& IRGenerationContext::mostDerivedContract() const
{ {
solAssert(m_mostDerivedContract, "Most derived contract requested but not set."); solAssert(m_mostDerivedContract, "Most derived contract requested but not set.");
@ -77,16 +96,22 @@ string IRGenerationContext::functionName(VariableDeclaration const& _varDecl)
return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id()); return "getter_fun_" + _varDecl.name() + "_" + to_string(_varDecl.id());
} }
string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration)
{
return functionName(_functionDeclaration.resolveVirtual(mostDerivedContract()));
}
string IRGenerationContext::newYulVariable() string IRGenerationContext::newYulVariable()
{ {
return "_" + to_string(++m_varCounter); return "_" + to_string(++m_varCounter);
} }
string IRGenerationContext::trySuccessConditionVariable(Expression const& _expression) const
{
// NB: The TypeChecker already ensured that the Expression is of type FunctionCall.
solAssert(
static_cast<FunctionCallAnnotation const&>(_expression.annotation()).tryCall,
"Parameter must be a FunctionCall with tryCall-annotation set."
);
return "trySuccessCondition_" + to_string(_expression.id());
}
string IRGenerationContext::internalDispatch(size_t _in, size_t _out) string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
{ {
string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out); string funName = "dispatch_internal_in_" + to_string(_in) + "_out_" + to_string(_out);
@ -126,6 +151,8 @@ string IRGenerationContext::internalDispatch(size_t _in, size_t _out)
{ "funID", to_string(function->id()) }, { "funID", to_string(function->id()) },
{ "name", functionName(*function)} { "name", functionName(*function)}
}); });
enqueueFunctionForCodeGeneration(*function);
} }
templ("cases", move(functions)); templ("cases", move(functions));
return templ.render(); return templ.render();
@ -141,3 +168,4 @@ std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message
{ {
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message); return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
} }

View File

@ -30,6 +30,7 @@
#include <libsolutil/Common.h> #include <libsolutil/Common.h>
#include <set>
#include <string> #include <string>
#include <memory> #include <memory>
#include <vector> #include <vector>
@ -61,6 +62,15 @@ public:
MultiUseYulFunctionCollector& functionCollector() { return m_functions; } MultiUseYulFunctionCollector& functionCollector() { return m_functions; }
/// Adds a Solidity function to the function generation queue and returns the name of the
/// corresponding Yul function.
std::string enqueueFunctionForCodeGeneration(FunctionDefinition const& _function);
/// Pops one item from the function generation queue. Must not be called if the queue is empty.
FunctionDefinition const* dequeueFunctionForCodeGeneration();
bool functionGenerationQueueEmpty() { return m_functionGenerationQueue.empty(); }
/// Sets the most derived contract (the one currently being compiled)> /// Sets the most derived contract (the one currently being compiled)>
void setMostDerivedContract(ContractDefinition const& _mostDerivedContract) void setMostDerivedContract(ContractDefinition const& _mostDerivedContract)
{ {
@ -82,7 +92,6 @@ public:
std::string functionName(FunctionDefinition const& _function); std::string functionName(FunctionDefinition const& _function);
std::string functionName(VariableDeclaration const& _varDecl); std::string functionName(VariableDeclaration const& _varDecl);
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
std::string newYulVariable(); std::string newYulVariable();
@ -99,6 +108,10 @@ public:
RevertStrings revertStrings() const { return m_revertStrings; } RevertStrings revertStrings() const { return m_revertStrings; }
/// @returns the variable name that can be used to inspect the success or failure of an external
/// function call that was invoked as part of the try statement.
std::string trySuccessConditionVariable(Expression const& _expression) const;
private: private:
langutil::EVMVersion m_evmVersion; langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings; RevertStrings m_revertStrings;
@ -109,6 +122,15 @@ private:
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables; std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
MultiUseYulFunctionCollector m_functions; MultiUseYulFunctionCollector m_functions;
size_t m_varCounter = 0; size_t m_varCounter = 0;
/// Function definitions queued for code generation. They're the Solidity functions whose calls
/// were discovered by the IR generator during AST traversal.
/// Note that the queue gets filled in a lazy way - new definitions can be added while the
/// collected ones get removed and traversed.
/// The order and duplicates are irrelevant here (hence std::set rather than std::queue) as
/// long as the order of Yul functions in the generated code is deterministic and the same on
/// all platforms - which is a property guaranteed by MultiUseYulFunctionCollector.
std::set<FunctionDefinition const*> m_functionGenerationQueue;
}; };
} }

View File

@ -101,21 +101,13 @@ string IRGenerator::generate(ContractDefinition const& _contract)
t("memoryInit", memoryInit()); t("memoryInit", memoryInit());
t("constructor", constructorCode(_contract)); t("constructor", constructorCode(_contract));
t("deploy", deployCode(_contract)); t("deploy", deployCode(_contract));
// We generate code for all functions and rely on the optimizer to remove them again generateQueuedFunctions();
// TODO it would probably be better to only generate functions when internalDispatch or
// virtualFunctionName is called - same below.
for (auto const* contract: _contract.annotation().linearizedBaseContracts)
for (auto const* fun: contract->definedFunctions())
generateFunction(*fun);
t("functions", m_context.functionCollector().requestedFunctions()); t("functions", m_context.functionCollector().requestedFunctions());
resetContext(_contract); resetContext(_contract);
m_context.setMostDerivedContract(_contract);
t("RuntimeObject", runtimeObjectName(_contract)); t("RuntimeObject", runtimeObjectName(_contract));
t("dispatch", dispatchRoutine(_contract)); t("dispatch", dispatchRoutine(_contract));
for (auto const* contract: _contract.annotation().linearizedBaseContracts) generateQueuedFunctions();
for (auto const* fun: contract->definedFunctions())
generateFunction(*fun);
t("runtimeFunctions", m_context.functionCollector().requestedFunctions()); t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
return t.render(); return t.render();
} }
@ -127,6 +119,13 @@ string IRGenerator::generate(Block const& _block)
return generator.code(); return generator.code();
} }
void IRGenerator::generateQueuedFunctions()
{
while (!m_context.functionGenerationQueueEmpty())
// NOTE: generateFunction() may modify function generation queue
generateFunction(*m_context.dequeueFunctionForCodeGeneration());
}
string IRGenerator::generateFunction(FunctionDefinition const& _function) string IRGenerator::generateFunction(FunctionDefinition const& _function)
{ {
string functionName = m_context.functionName(_function); string functionName = m_context.functionName(_function);
@ -291,7 +290,7 @@ string IRGenerator::constructorCode(ContractDefinition const& _contract)
t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := "); t("assignToParams", paramVars == 0 ? "" : "let " + suffixedVariableNameList("param_", 0, paramVars) + " := ");
t("params", suffixedVariableNameList("param_", 0, paramVars)); t("params", suffixedVariableNameList("param_", 0, paramVars));
t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true)); t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true));
t("constructorName", m_context.functionName(*constructor)); t("constructorName", m_context.enqueueFunctionForCodeGeneration(*constructor));
out << t.render(); out << t.render();
} }
@ -352,7 +351,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
vector<map<string, string>> functions; vector<map<string, string>> functions;
for (auto const& function: _contract.interfaceFunctions()) for (auto const& function: _contract.interfaceFunctions())
{ {
functions.push_back({}); functions.emplace_back();
map<string, string>& templ = functions.back(); map<string, string>& templ = functions.back();
templ["functionSelector"] = "0x" + function.first.hex(); templ["functionSelector"] = "0x" + function.first.hex();
FunctionTypePointer const& type = function.second; FunctionTypePointer const& type = function.second;
@ -370,7 +369,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0); templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration())) if (FunctionDefinition const* funDef = dynamic_cast<FunctionDefinition const*>(&type->declaration()))
templ["function"] = generateFunction(*funDef); templ["function"] = m_context.enqueueFunctionForCodeGeneration(*funDef);
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(&type->declaration())) else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(&type->declaration()))
templ["function"] = generateGetter(*varDecl); templ["function"] = generateGetter(*varDecl);
else else
@ -386,14 +385,14 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
string fallbackCode; string fallbackCode;
if (!fallback->isPayable()) if (!fallback->isPayable())
fallbackCode += callValueCheck(); fallbackCode += callValueCheck();
fallbackCode += generateFunction(*fallback) + "() stop()"; fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
t("fallback", fallbackCode); t("fallback", fallbackCode);
} }
else else
t("fallback", "revert(0, 0)"); t("fallback", "revert(0, 0)");
if (FunctionDefinition const* etherReceiver = _contract.receiveFunction()) if (FunctionDefinition const* etherReceiver = _contract.receiveFunction())
t("receiveEther", generateFunction(*etherReceiver) + "() stop()"); t("receiveEther", m_context.enqueueFunctionForCodeGeneration(*etherReceiver) + "() stop()");
else else
t("receiveEther", ""); t("receiveEther", "");
return t.render(); return t.render();
@ -413,6 +412,10 @@ string IRGenerator::memoryInit()
void IRGenerator::resetContext(ContractDefinition const& _contract) void IRGenerator::resetContext(ContractDefinition const& _contract)
{ {
solAssert(
m_context.functionGenerationQueueEmpty(),
"Reset function generation queue while it still had functions."
);
solAssert( solAssert(
m_context.functionCollector().requestedFunctions().empty(), m_context.functionCollector().requestedFunctions().empty(),
"Reset context while it still had functions." "Reset context while it still had functions."

View File

@ -56,6 +56,9 @@ private:
std::string generate(ContractDefinition const& _contract); std::string generate(ContractDefinition const& _contract);
std::string generate(Block const& _block); std::string generate(Block const& _block);
/// Generates code for all the functions from the function generation queue.
/// The resulting code is stored in the function collector in IRGenerationContext.
void generateQueuedFunctions();
/// Generates code for and returns the name of the function. /// Generates code for and returns the name of the function.
std::string generateFunction(FunctionDefinition const& _function); std::string generateFunction(FunctionDefinition const& _function);
/// Generates a getter for the given declaration and returns its name /// Generates a getter for the given declaration and returns its name

View File

@ -27,6 +27,7 @@
#include <libsolidity/codegen/YulUtilFunctions.h> #include <libsolidity/codegen/YulUtilFunctions.h>
#include <libsolidity/codegen/ABIFunctions.h> #include <libsolidity/codegen/ABIFunctions.h>
#include <libsolidity/codegen/CompilerUtils.h> #include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ReturnInfo.h>
#include <libsolidity/ast/TypeProvider.h> #include <libsolidity/ast/TypeProvider.h>
#include <libevmasm/GasMeter.h> #include <libevmasm/GasMeter.h>
@ -41,6 +42,7 @@
#include <libsolutil/Keccak256.h> #include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h> #include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/transformed.hpp> #include <boost/range/adaptor/transformed.hpp>
using namespace std; using namespace std;
@ -255,14 +257,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
solUnimplementedAssert(false, ""); solUnimplementedAssert(false, "");
else else
{ {
bool lValueRequested = _tuple.annotation().lValueRequested; bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
if (lValueRequested) if (willBeWrittenTo)
solAssert(!m_currentLValue, ""); solAssert(!m_currentLValue, "");
if (_tuple.components().size() == 1) if (_tuple.components().size() == 1)
{ {
solAssert(_tuple.components().front(), ""); solAssert(_tuple.components().front(), "");
_tuple.components().front()->accept(*this); _tuple.components().front()->accept(*this);
if (lValueRequested) if (willBeWrittenTo)
solAssert(!!m_currentLValue, ""); solAssert(!!m_currentLValue, "");
else else
define(_tuple, *_tuple.components().front()); define(_tuple, *_tuple.components().front());
@ -274,7 +276,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
if (auto const& component = _tuple.components()[i]) if (auto const& component = _tuple.components()[i])
{ {
component->accept(*this); component->accept(*this);
if (lValueRequested) if (willBeWrittenTo)
{ {
solAssert(!!m_currentLValue, ""); solAssert(!!m_currentLValue, "");
lvalues.emplace_back(std::move(m_currentLValue)); lvalues.emplace_back(std::move(m_currentLValue));
@ -283,10 +285,10 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
else else
define(IRVariable(_tuple).tupleComponent(i), *component); define(IRVariable(_tuple).tupleComponent(i), *component);
} }
else if (lValueRequested) else if (willBeWrittenTo)
lvalues.emplace_back(); lvalues.emplace_back();
if (_tuple.annotation().lValueRequested) if (_tuple.annotation().willBeWrittenTo)
m_currentLValue.emplace(IRLValue{ m_currentLValue.emplace(IRLValue{
*_tuple.annotation().type, *_tuple.annotation().type,
IRLValue::Tuple{std::move(lvalues)} IRLValue::Tuple{std::move(lvalues)}
@ -575,7 +577,9 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration)) if (auto functionDef = dynamic_cast<FunctionDefinition const*>(identifier->annotation().referencedDeclaration))
{ {
define(_functionCall) << define(_functionCall) <<
m_context.virtualFunctionName(*functionDef) << m_context.enqueueFunctionForCodeGeneration(
functionDef->resolveVirtual(m_context.mostDerivedContract())
) <<
"(" << "(" <<
joinHumanReadable(args) << joinHumanReadable(args) <<
")\n"; ")\n";
@ -584,6 +588,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
} }
define(_functionCall) << define(_functionCall) <<
// NOTE: internalDispatch() takes care of adding the function to function generation queue
m_context.internalDispatch( m_context.internalDispatch(
TupleType(functionType->parameterTypes()).sizeOnStack(), TupleType(functionType->parameterTypes()).sizeOnStack(),
TupleType(functionType->returnParameterTypes()).sizeOnStack() TupleType(functionType->returnParameterTypes()).sizeOnStack()
@ -840,11 +845,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
case Type::Category::Function: case Type::Category::Function:
if (member == "selector") if (member == "selector")
{ {
solUnimplementedAssert( FunctionType const& functionType = dynamic_cast<FunctionType const&>(
dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() == *_memberAccess.expression().annotation().type
FunctionType::Kind::External, ""
); );
define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionIdentifier")); if (functionType.kind() == FunctionType::Kind::External)
define(IRVariable{_memberAccess}, IRVariable(_memberAccess.expression()).part("functionIdentifier"));
else if (functionType.kind() == FunctionType::Kind::Declaration)
{
solAssert(functionType.hasDeclaration(), "");
define(IRVariable{_memberAccess}) << formatNumber(functionType.externalIdentifier() << 224) << "\n";
}
else
solAssert(false, "Invalid use of .selector");
} }
else if (member == "address") else if (member == "address")
{ {
@ -971,6 +983,78 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(false, "Illegal fixed bytes member."); solAssert(false, "Illegal fixed bytes member.");
break; break;
} }
case Type::Category::TypeType:
{
Type const& actualType = *dynamic_cast<TypeType const&>(
*_memberAccess.expression().annotation().type
).actualType();
if (actualType.category() == Type::Category::Contract)
{
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
handleVariableReference(*variable, _memberAccess);
else if (auto const* funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type))
{
switch (funType->kind())
{
case FunctionType::Kind::Declaration:
break;
case FunctionType::Kind::Internal:
if (auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration))
define(_memberAccess) << to_string(function->id()) << "\n";
else
solAssert(false, "Function not found in member access");
break;
case FunctionType::Kind::Event:
solAssert(
dynamic_cast<EventDefinition const*>(_memberAccess.annotation().referencedDeclaration),
"Event not found"
);
// the call will do the resolving
break;
case FunctionType::Kind::DelegateCall:
define(IRVariable(_memberAccess).part("address"), _memberAccess.expression());
define(IRVariable(_memberAccess).part("functionIdentifier")) << formatNumber(funType->externalIdentifier()) << "\n";
break;
case FunctionType::Kind::External:
case FunctionType::Kind::Creation:
case FunctionType::Kind::Send:
case FunctionType::Kind::BareCall:
case FunctionType::Kind::BareCallCode:
case FunctionType::Kind::BareDelegateCall:
case FunctionType::Kind::BareStaticCall:
case FunctionType::Kind::Transfer:
case FunctionType::Kind::Log0:
case FunctionType::Kind::Log1:
case FunctionType::Kind::Log2:
case FunctionType::Kind::Log3:
case FunctionType::Kind::Log4:
case FunctionType::Kind::ECRecover:
case FunctionType::Kind::SHA256:
case FunctionType::Kind::RIPEMD160:
default:
solAssert(false, "unsupported member function");
}
}
else if (dynamic_cast<TypeType const*>(_memberAccess.annotation().type))
{
// no-op
}
else
// The old code generator had a generic "else" case here
// without any specific code being generated,
// but it would still be better to have an exhaustive list.
solAssert(false, "");
}
else if (EnumType const* enumType = dynamic_cast<EnumType const*>(&actualType))
define(_memberAccess) << to_string(enumType->memberValue(_memberAccess.memberName())) << "\n";
else
// The old code generator had a generic "else" case here
// without any specific code being generated,
// but it would still be better to have an exhaustive list.
solAssert(false, "");
break;
}
default: default:
solAssert(false, "Member access to unknown type."); solAssert(false, "Member access to unknown type.");
} }
@ -1186,28 +1270,7 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier)
else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration)) else if (FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(declaration))
define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n"; define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n";
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration)) else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
{ handleVariableReference(*varDecl, _identifier);
// TODO for the constant case, we have to be careful:
// If the value is visited twice, `defineExpression` is called twice on
// the same expression.
solUnimplementedAssert(!varDecl->isConstant(), "");
solUnimplementedAssert(!varDecl->immutable(), "");
if (m_context.isLocalVariable(*varDecl))
setLValue(_identifier, IRLValue{
*varDecl->annotation().type,
IRLValue::Stack{m_context.localVariable(*varDecl)}
});
else if (m_context.isStateVariable(*varDecl))
setLValue(_identifier, IRLValue{
*varDecl->annotation().type,
IRLValue::Storage{
toCompactHexWithPrefix(m_context.storageLocationOfVariable(*varDecl).first),
m_context.storageLocationOfVariable(*varDecl).second
}
});
else
solAssert(false, "Invalid variable kind.");
}
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration)) else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{ {
solUnimplementedAssert(!contract->isLibrary(), "Libraries not yet supported."); solUnimplementedAssert(!contract->isLibrary(), "Libraries not yet supported.");
@ -1249,6 +1312,33 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
return false; return false;
} }
void IRGeneratorForStatements::handleVariableReference(
VariableDeclaration const& _variable,
Expression const& _referencingExpression
)
{
// TODO for the constant case, we have to be careful:
// If the value is visited twice, `defineExpression` is called twice on
// the same expression.
solUnimplementedAssert(!_variable.isConstant(), "");
solUnimplementedAssert(!_variable.immutable(), "");
if (m_context.isLocalVariable(_variable))
setLValue(_referencingExpression, IRLValue{
*_variable.annotation().type,
IRLValue::Stack{m_context.localVariable(_variable)}
});
else if (m_context.isStateVariable(_variable))
setLValue(_referencingExpression, IRLValue{
*_variable.annotation().type,
IRLValue::Storage{
toCompactHexWithPrefix(m_context.storageLocationOfVariable(_variable).first),
m_context.storageLocationOfVariable(_variable).second
}
});
else
solAssert(false, "Invalid variable kind.");
}
void IRGeneratorForStatements::appendExternalFunctionCall( void IRGeneratorForStatements::appendExternalFunctionCall(
FunctionCall const& _functionCall, FunctionCall const& _functionCall,
vector<ASTPointer<Expression const>> const& _arguments vector<ASTPointer<Expression const>> const& _arguments
@ -1260,39 +1350,15 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
_arguments.size() == funType.parameterTypes().size(), "" _arguments.size() == funType.parameterTypes().size(), ""
); );
solUnimplementedAssert(!funType.bound(), ""); solUnimplementedAssert(!funType.bound(), "");
FunctionType::Kind funKind = funType.kind(); FunctionType::Kind const funKind = funType.kind();
solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), ""); solAssert(funKind != FunctionType::Kind::BareStaticCall || m_context.evmVersion().hasStaticCall(), "");
solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed."); solAssert(funKind != FunctionType::Kind::BareCallCode, "Callcode has been removed.");
bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall; bool const isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall; bool const useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata(); ReturnInfo const returnInfo{m_context.evmVersion(), funType};
unsigned estimatedReturnSize = 0;
bool dynamicReturnSize = false;
TypePointers returnTypes;
if (!returnSuccessConditionAndReturndata)
{
if (haveReturndatacopy)
returnTypes = funType.returnParameterTypes();
else
returnTypes = funType.returnParameterTypesWithoutDynamicTypes();
for (auto const& retType: returnTypes)
if (retType->isDynamicallyEncoded())
{
solAssert(haveReturndatacopy, "");
dynamicReturnSize = true;
estimatedReturnSize = 0;
break;
}
else if (retType->decodingType())
estimatedReturnSize += retType->decodingType()->calldataEncodedSize();
else
estimatedReturnSize += retType->calldataEncodedSize();
}
TypePointers argumentTypes; TypePointers argumentTypes;
vector<string> argumentStrings; vector<string> argumentStrings;
@ -1311,8 +1377,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
// (which we would have to subtract from the gas left) // (which we would have to subtract from the gas left)
// We could also just use MLOAD; POP right before the gas calculation, but the optimizer // We could also just use MLOAD; POP right before the gas calculation, but the optimizer
// would remove that, so we use MSTORE here. // would remove that, so we use MSTORE here.
if (!funType.gasSet() && estimatedReturnSize > 0) if (!funType.gasSet() && returnInfo.estimatedReturnSize > 0)
m_code << "mstore(add(" << freeMemory() << ", " << to_string(estimatedReturnSize) << "), 0)\n"; m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
} }
ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector()); ABIFunctions abi(m_context.evmVersion(), m_context.revertStrings(), m_context.functionCollector());
@ -1323,30 +1389,61 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
if iszero(extcodesize(<address>)) { revert(0, 0) } if iszero(extcodesize(<address>)) { revert(0, 0) }
</checkExistence> </checkExistence>
// storage for arguments and returned data
let <pos> := <freeMemory> let <pos> := <freeMemory>
mstore(<pos>, <shl28>(<funId>)) mstore(<pos>, <shl28>(<funId>))
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>) let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
let <result> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>) let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
if iszero(<result>) { <forwardingRevert>() } <?noTryCall>
if iszero(<success>) { <forwardingRevert>() }
</noTryCall>
<?hasRetVars> let <retVars> </hasRetVars>
if <success> {
<?dynamicReturnSize>
// copy dynamic return data out
returndatacopy(<pos>, 0, returndatasize())
</dynamicReturnSize>
<?dynamicReturnSize> // update freeMemoryPointer according to dynamic return size
returndatacopy(<pos>, 0, returndatasize()) mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
</dynamicReturnSize>
mstore(<freeMemoryPointer>, add(<pos>, and(add(<returnSize>, 0x1f), not(0x1f)))) // decode return parameters from external try-call into retVars
<?returns> let <retVars> := </returns> <abiDecode>(<pos>, add(<pos>, <returnSize>)) <?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
}
)"); )");
templ("pos", m_context.newYulVariable()); templ("pos", m_context.newYulVariable());
templ("end", m_context.newYulVariable()); templ("end", m_context.newYulVariable());
templ("result", m_context.newYulVariable()); if (_functionCall.annotation().tryCall)
templ("success", m_context.trySuccessConditionVariable(_functionCall));
else
templ("success", m_context.newYulVariable());
templ("freeMemory", freeMemory()); templ("freeMemory", freeMemory());
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4))); templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name()); templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").name());
templ("address", IRVariable(_functionCall.expression()).part("address").name()); templ("address", IRVariable(_functionCall.expression()).part("address").name());
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
// This ensures it can catch badly formatted input from external calls.
if (m_context.evmVersion().supportsReturndata())
templ("returnSize", "returndatasize()");
else
templ("returnSize", to_string(returnInfo.estimatedReturnSize));
templ("reservedReturnSize", returnInfo.dynamicReturnSize ? "0" : to_string(returnInfo.estimatedReturnSize));
string const retVars = IRVariable(_functionCall).commaSeparatedList();
templ("retVars", retVars);
templ("hasRetVars", !retVars.empty());
solAssert(retVars.empty() == returnInfo.returnTypes.empty(), "");
templ("roundUp", m_utils.roundUpFunction());
templ("abiDecode", abi.tupleDecoder(returnInfo.returnTypes, true));
templ("dynamicReturnSize", returnInfo.dynamicReturnSize);
templ("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("noTryCall", !_functionCall.annotation().tryCall);
// If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place. // If the function takes arbitrary parameters or is a bare call, copy dynamic length data in place.
// Move arguments to memory, will not update the free memory pointer (but will update the memory // Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack). // pointer on the stack).
@ -1401,24 +1498,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("forwardingRevert", m_utils.forwardingRevertFunction()); templ("forwardingRevert", m_utils.forwardingRevertFunction());
solUnimplementedAssert(!returnSuccessConditionAndReturndata, "");
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, ""); solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, ""); solUnimplementedAssert(funKind != FunctionType::Kind::ECRecover, "");
templ("dynamicReturnSize", dynamicReturnSize);
// Always use the actual return length, and not our calculated expected length, if returndatacopy is supported.
// This ensures it can catch badly formatted input from external calls.
if (haveReturndatacopy)
templ("returnSize", "returndatasize()");
else
templ("returnSize", to_string(estimatedReturnSize));
templ("reservedReturnSize", dynamicReturnSize ? "0" : to_string(estimatedReturnSize));
templ("abiDecode", abi.tupleDecoder(returnTypes, true));
templ("returns", !returnTypes.empty());
templ("retVars", IRVariable(_functionCall).commaSeparatedList());
m_code << templ.render(); m_code << templ.render();
} }
@ -1471,14 +1553,17 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable
else else
m_code << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n"; m_code << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n";
else else
m_code << {
(_declare ? "let ": "") << if (_lhs.type().sizeOnStack() > 0)
_lhs.commaSeparatedList() << m_code <<
" := " << (_declare ? "let ": "") <<
m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) << _lhs.commaSeparatedList() <<
" := ";
m_code << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
"(" << "(" <<
_rhs.commaSeparatedList() << _rhs.commaSeparatedList() <<
")\n"; ")\n";
}
} }
IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes) IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes)
@ -1540,6 +1625,15 @@ string IRGeneratorForStatements::binaryOperation(
case Token::Mod: case Token::Mod:
fun = m_utils.checkedIntModFunction(*type); fun = m_utils.checkedIntModFunction(*type);
break; break;
case Token::BitOr:
fun = "or";
break;
case Token::BitXor:
fun = "xor";
break;
case Token::BitAnd:
fun = "and";
break;
default: default:
break; break;
} }
@ -1688,7 +1782,7 @@ void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue
{ {
solAssert(!m_currentLValue, ""); solAssert(!m_currentLValue, "");
if (_expression.annotation().lValueRequested) if (_expression.annotation().willBeWrittenTo)
{ {
m_currentLValue.emplace(std::move(_lvalue)); m_currentLValue.emplace(std::move(_lvalue));
solAssert(!_lvalue.type.dataStoredIn(DataLocation::CallData), ""); solAssert(!_lvalue.type.dataStoredIn(DataLocation::CallData), "");
@ -1749,3 +1843,118 @@ Type const& IRGeneratorForStatements::type(Expression const& _expression)
solAssert(_expression.annotation().type, "Type of expression not set."); solAssert(_expression.annotation().type, "Type of expression not set.");
return *_expression.annotation().type; return *_expression.annotation().type;
} }
bool IRGeneratorForStatements::visit(TryStatement const& _tryStatement)
{
Expression const& externalCall = _tryStatement.externalCall();
externalCall.accept(*this);
m_code << "switch iszero(" << m_context.trySuccessConditionVariable(externalCall) << ")\n";
m_code << "case 0 { // success case\n";
TryCatchClause const& successClause = *_tryStatement.clauses().front();
if (successClause.parameters())
{
size_t i = 0;
for (ASTPointer<VariableDeclaration> const& varDecl: successClause.parameters()->parameters())
{
solAssert(varDecl, "");
define(m_context.addLocalVariable(*varDecl),
successClause.parameters()->parameters().size() == 1 ?
IRVariable(externalCall) :
IRVariable(externalCall).tupleComponent(i++)
);
}
}
successClause.block().accept(*this);
m_code << "}\n";
m_code << "default { // failure case\n";
handleCatch(_tryStatement);
m_code << "}\n";
return false;
}
void IRGeneratorForStatements::handleCatch(TryStatement const& _tryStatement)
{
if (_tryStatement.structuredClause())
handleCatchStructuredAndFallback(*_tryStatement.structuredClause(), _tryStatement.fallbackClause());
else if (_tryStatement.fallbackClause())
handleCatchFallback(*_tryStatement.fallbackClause());
else
rethrow();
}
void IRGeneratorForStatements::handleCatchStructuredAndFallback(
TryCatchClause const& _structured,
TryCatchClause const* _fallback
)
{
solAssert(
_structured.parameters() &&
_structured.parameters()->parameters().size() == 1 &&
_structured.parameters()->parameters().front() &&
*_structured.parameters()->parameters().front()->annotation().type == *TypeProvider::stringMemory(),
""
);
solAssert(m_context.evmVersion().supportsReturndata(), "");
// Try to decode the error message.
// If this fails, leaves 0 on the stack, otherwise the pointer to the data string.
string const dataVariable = m_context.newYulVariable();
m_code << "let " << dataVariable << " := " << m_utils.tryDecodeErrorMessageFunction() << "()\n";
m_code << "switch iszero(" << dataVariable << ") \n";
m_code << "case 0 { // decoding success\n";
if (_structured.parameters())
{
solAssert(_structured.parameters()->parameters().size() == 1, "");
IRVariable const& var = m_context.addLocalVariable(*_structured.parameters()->parameters().front());
define(var) << dataVariable << "\n";
}
_structured.accept(*this);
m_code << "}\n";
m_code << "default { // decoding failure\n";
if (_fallback)
handleCatchFallback(*_fallback);
else
rethrow();
m_code << "}\n";
}
void IRGeneratorForStatements::handleCatchFallback(TryCatchClause const& _fallback)
{
if (_fallback.parameters())
{
solAssert(m_context.evmVersion().supportsReturndata(), "");
solAssert(
_fallback.parameters()->parameters().size() == 1 &&
_fallback.parameters()->parameters().front() &&
*_fallback.parameters()->parameters().front()->annotation().type == *TypeProvider::bytesMemory(),
""
);
VariableDeclaration const& paramDecl = *_fallback.parameters()->parameters().front();
define(m_context.addLocalVariable(paramDecl)) << m_utils.extractReturndataFunction() << "()\n";
}
_fallback.accept(*this);
}
void IRGeneratorForStatements::rethrow()
{
if (m_context.evmVersion().supportsReturndata())
m_code << R"(
returndatacopy(0, 0, returndatasize())
revert(0, returndatasize())
)"s;
else
m_code << "revert(0, 0) // rethrow\n"s;
}
bool IRGeneratorForStatements::visit(TryCatchClause const& _clause)
{
_clause.block().accept(*this);
return false;
}

View File

@ -24,6 +24,8 @@
#include <libsolidity/codegen/ir/IRLValue.h> #include <libsolidity/codegen/ir/IRLValue.h>
#include <libsolidity/codegen/ir/IRVariable.h> #include <libsolidity/codegen/ir/IRVariable.h>
#include <functional>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -70,7 +72,26 @@ public:
void endVisit(Identifier const& _identifier) override; void endVisit(Identifier const& _identifier) override;
bool visit(Literal const& _literal) override; bool visit(Literal const& _literal) override;
bool visit(TryStatement const& _tryStatement) override;
bool visit(TryCatchClause const& _tryCatchClause) override;
private: private:
/// Handles all catch cases of a try statement, except the success-case.
void handleCatch(TryStatement const& _tryStatement);
void handleCatchStructuredAndFallback(
TryCatchClause const& _structured,
TryCatchClause const* _fallback
);
void handleCatchFallback(TryCatchClause const& _fallback);
/// Generates code to rethrow an exception.
void rethrow();
void handleVariableReference(
VariableDeclaration const& _variable,
Expression const& _referencingExpression
);
/// Appends code to call an external function with the given arguments. /// Appends code to call an external function with the given arguments.
/// All involved expressions have already been visited. /// All involved expressions have already been visited.
void appendExternalFunctionCall( void appendExternalFunctionCall(
@ -123,7 +144,7 @@ private:
/// @returns a fresh IR variable containing the value of the lvalue @a _lvalue. /// @returns a fresh IR variable containing the value of the lvalue @a _lvalue.
IRVariable readFromLValue(IRLValue const& _lvalue); IRVariable readFromLValue(IRLValue const& _lvalue);
/// Stores the given @a _lvalue in m_currentLValue, if it will be written to (lValueRequested). Otherwise /// Stores the given @a _lvalue in m_currentLValue, if it will be written to (willBeWrittenTo). Otherwise
/// defines the expression @a _expression by reading the value from @a _lvalue. /// defines the expression @a _expression by reading the value from @a _lvalue.
void setLValue(Expression const& _expression, IRLValue _lvalue); void setLValue(Expression const& _expression, IRLValue _lvalue);
void generateLoop( void generateLoop(

View File

@ -31,6 +31,7 @@
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
using namespace solidity::util;
using namespace solidity::langutil; using namespace solidity::langutil;
using namespace solidity::frontend; using namespace solidity::frontend;
@ -130,15 +131,10 @@ bool CHC::visit(ContractDefinition const& _contract)
clearIndices(&_contract); clearIndices(&_contract);
auto errorFunctionSort = make_shared<smt::FunctionSort>(
vector<smt::SortPointer>(),
smt::SortProvider::boolSort
);
string suffix = _contract.name() + "_" + to_string(_contract.id()); string suffix = _contract.name() + "_" + to_string(_contract.id());
m_errorPredicate = createSymbolicBlock(errorFunctionSort, "error_" + suffix); m_errorPredicate = createSymbolicBlock(arity0FunctionSort(), "error_" + suffix);
m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix); m_constructorSummaryPredicate = createSymbolicBlock(constructorSort(), "summary_constructor_" + suffix);
m_implicitConstructorPredicate = createSymbolicBlock(interfaceSort(), "implicit_constructor_" + suffix); m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix);
auto stateExprs = currentStateVariables(); auto stateExprs = currentStateVariables();
setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs); setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs);
@ -148,15 +144,7 @@ bool CHC::visit(ContractDefinition const& _contract)
void CHC::endVisit(ContractDefinition const& _contract) void CHC::endVisit(ContractDefinition const& _contract)
{ {
for (auto const& var: m_stateVariables) auto implicitConstructor = (*m_implicitConstructorPredicate)({});
{
solAssert(m_context.knownVariable(*var), "");
auto const& symbVar = m_context.variable(*var);
symbVar->resetIndex();
m_context.setZeroValue(*var);
symbVar->increaseIndex();
}
auto implicitConstructor = (*m_implicitConstructorPredicate)(initialStateVariables());
connectBlocks(genesis(), implicitConstructor); connectBlocks(genesis(), implicitConstructor);
m_currentBlock = implicitConstructor; m_currentBlock = implicitConstructor;
m_context.addAssertion(m_error.currentValue() == 0); m_context.addAssertion(m_error.currentValue() == 0);
@ -643,19 +631,19 @@ set<Expression const*, CHC::IdCompare> CHC::transactionAssertions(ASTNode const*
vector<VariableDeclaration const*> CHC::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract) vector<VariableDeclaration const*> CHC::stateVariablesIncludingInheritedAndPrivate(ContractDefinition const& _contract)
{ {
vector<VariableDeclaration const*> stateVars; return fold(
for (auto const& contract: _contract.annotation().linearizedBaseContracts) _contract.annotation().linearizedBaseContracts,
for (auto var: contract->stateVariables()) vector<VariableDeclaration const*>{},
stateVars.push_back(var); [](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); }
return stateVars; );
} }
vector<smt::SortPointer> CHC::stateSorts(ContractDefinition const& _contract) vector<smt::SortPointer> CHC::stateSorts(ContractDefinition const& _contract)
{ {
vector<smt::SortPointer> stateSorts; return applyMap(
for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract)) stateVariablesIncludingInheritedAndPrivate(_contract),
stateSorts.push_back(smt::smtSortAbstractFunction(*var->type())); [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }
return stateSorts; );
} }
smt::SortPointer CHC::constructorSort() smt::SortPointer CHC::constructorSort()
@ -682,6 +670,14 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract)
); );
} }
smt::SortPointer CHC::arity0FunctionSort()
{
return make_shared<smt::FunctionSort>(
vector<smt::SortPointer>(),
smt::SortProvider::boolSort
);
}
/// A function in the symbolic CFG requires: /// A function in the symbolic CFG requires:
/// - Index of failed assertion. 0 means no assertion failed. /// - Index of failed assertion. 0 means no assertion failed.
/// - 2 sets of state variables: /// - 2 sets of state variables:
@ -695,12 +691,9 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract)
/// - 1 set of output variables /// - 1 set of output variables
smt::SortPointer CHC::sort(FunctionDefinition const& _function) smt::SortPointer CHC::sort(FunctionDefinition const& _function)
{ {
vector<smt::SortPointer> inputSorts; auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
for (auto const& var: _function.parameters()) auto inputSorts = applyMap(_function.parameters(), smtSort);
inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); auto outputSorts = applyMap(_function.returnParameters(), smtSort);
vector<smt::SortPointer> outputSorts;
for (auto const& var: _function.returnParameters())
outputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
return make_shared<smt::FunctionSort>( return make_shared<smt::FunctionSort>(
vector<smt::SortPointer>{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts, vector<smt::SortPointer>{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts,
smt::SortProvider::boolSort smt::SortProvider::boolSort
@ -715,11 +708,9 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
auto fSort = dynamic_pointer_cast<smt::FunctionSort>(sort(*m_currentFunction)); auto fSort = dynamic_pointer_cast<smt::FunctionSort>(sort(*m_currentFunction));
solAssert(fSort, ""); solAssert(fSort, "");
vector<smt::SortPointer> varSorts; auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
for (auto const& var: m_currentFunction->localVariables())
varSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
return make_shared<smt::FunctionSort>( return make_shared<smt::FunctionSort>(
fSort->domain + varSorts, fSort->domain + applyMap(m_currentFunction->localVariables(), smtSort),
smt::SortProvider::boolSort smt::SortProvider::boolSort
); );
} }
@ -729,11 +720,9 @@ smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractD
auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract); auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract);
auto sorts = stateSorts(_contract); auto sorts = stateSorts(_contract);
vector<smt::SortPointer> inputSorts, outputSorts; auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
for (auto const& var: _function.parameters()) auto inputSorts = applyMap(_function.parameters(), smtSort);
inputSorts.push_back(smt::smtSortAbstractFunction(*var->type())); auto outputSorts = applyMap(_function.returnParameters(), smtSort);
for (auto const& var: _function.returnParameters())
outputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
return make_shared<smt::FunctionSort>( return make_shared<smt::FunctionSort>(
vector<smt::SortPointer>{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts, vector<smt::SortPointer>{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts,
smt::SortProvider::boolSort smt::SortProvider::boolSort
@ -769,9 +758,10 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
smt::Expression CHC::interface() smt::Expression CHC::interface()
{ {
vector<smt::Expression> paramExprs; auto paramExprs = applyMap(
for (auto const& var: m_stateVariables) m_stateVariables,
paramExprs.push_back(m_context.variable(*var)->currentValue()); [this](auto _var) { return m_context.variable(*_var)->currentValue(); }
);
return (*m_interfaces.at(m_currentContract))(paramExprs); return (*m_interfaces.at(m_currentContract))(paramExprs);
} }
@ -803,11 +793,9 @@ smt::Expression CHC::summary(FunctionDefinition const& _function)
vector<smt::Expression> args{m_error.currentValue()}; vector<smt::Expression> args{m_error.currentValue()};
auto contract = _function.annotation().contract; auto contract = _function.annotation().contract;
args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables(); args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables();
for (auto const& var: _function.parameters()) args += applyMap(_function.parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); });
args.push_back(m_context.variable(*var)->valueAtIndex(0));
args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables(); args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables();
for (auto const& var: _function.returnParameters()) args += applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); });
args.push_back(m_context.variable(*var)->currentValue());
return (*m_summaries.at(m_currentContract).at(&_function))(args); return (*m_summaries.at(m_currentContract).at(&_function))(args);
} }
@ -854,27 +842,21 @@ vector<smt::Expression> CHC::initialStateVariables()
vector<smt::Expression> CHC::stateVariablesAtIndex(int _index) vector<smt::Expression> CHC::stateVariablesAtIndex(int _index)
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
vector<smt::Expression> exprs; return applyMap(m_stateVariables, [&](auto _var) { return valueAtIndex(*_var, _index); });
for (auto const& var: m_stateVariables)
exprs.push_back(m_context.variable(*var)->valueAtIndex(_index));
return exprs;
} }
vector<smt::Expression> CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract) vector<smt::Expression> CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract)
{ {
vector<smt::Expression> exprs; return applyMap(
for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract)) stateVariablesIncludingInheritedAndPrivate(_contract),
exprs.push_back(m_context.variable(*var)->valueAtIndex(_index)); [&](auto _var) { return valueAtIndex(*_var, _index); }
return exprs; );
} }
vector<smt::Expression> CHC::currentStateVariables() vector<smt::Expression> CHC::currentStateVariables()
{ {
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
vector<smt::Expression> exprs; return applyMap(m_stateVariables, [this](auto _var) { return currentValue(*_var); });
for (auto const& var: m_stateVariables)
exprs.push_back(m_context.variable(*var)->currentValue());
return exprs;
} }
vector<smt::Expression> CHC::currentFunctionVariables() vector<smt::Expression> CHC::currentFunctionVariables()
@ -886,9 +868,7 @@ vector<smt::Expression> CHC::currentFunctionVariables()
initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0)); initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0));
mutableInputExprs.push_back(m_context.variable(*var)->currentValue()); mutableInputExprs.push_back(m_context.variable(*var)->currentValue());
} }
vector<smt::Expression> returnExprs; auto returnExprs = applyMap(m_currentFunction->returnParameters(), [this](auto _var) { return currentValue(*_var); });
for (auto const& var: m_currentFunction->returnParameters())
returnExprs.push_back(m_context.variable(*var)->currentValue());
return vector<smt::Expression>{m_error.currentValue()} + return vector<smt::Expression>{m_error.currentValue()} +
initialStateVariables() + initialStateVariables() +
initInputExprs + initInputExprs +
@ -899,11 +879,10 @@ vector<smt::Expression> CHC::currentFunctionVariables()
vector<smt::Expression> CHC::currentBlockVariables() vector<smt::Expression> CHC::currentBlockVariables()
{ {
vector<smt::Expression> paramExprs;
if (m_currentFunction) if (m_currentFunction)
for (auto const& var: m_currentFunction->localVariables()) return currentFunctionVariables() + applyMap(m_currentFunction->localVariables(), [this](auto _var) { return currentValue(*_var); });
paramExprs.push_back(m_context.variable(*var)->currentValue());
return currentFunctionVariables() + paramExprs; return currentFunctionVariables();
} }
string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract) string CHC::predicateName(ASTNode const* _node, ContractDefinition const* _contract)
@ -958,8 +937,7 @@ smt::Expression CHC::predicate(FunctionCall const& _funCall)
m_context.variable(*param)->increaseIndex(); m_context.variable(*param)->increaseIndex();
else else
createVariable(*param); createVariable(*param);
for (auto const& var: function->returnParameters()) args += applyMap(function->returnParameters(), [this](auto _var) { return currentValue(*_var); });
args.push_back(m_context.variable(*var)->currentValue());
if (contract->isLibrary()) if (contract->isLibrary())
return (*m_summaries.at(contract).at(function))(args); return (*m_summaries.at(contract).at(function))(args);

View File

@ -106,6 +106,7 @@ private:
smt::SortPointer constructorSort(); smt::SortPointer constructorSort();
smt::SortPointer interfaceSort(); smt::SortPointer interfaceSort();
static smt::SortPointer interfaceSort(ContractDefinition const& _const); static smt::SortPointer interfaceSort(ContractDefinition const& _const);
smt::SortPointer arity0FunctionSort();
smt::SortPointer sort(FunctionDefinition const& _function); smt::SortPointer sort(FunctionDefinition const& _function);
smt::SortPointer sort(ASTNode const* _block); smt::SortPointer sort(ASTNode const* _block);
/// @returns the sort of a predicate that represents the summary of _function in the scope of _contract. /// @returns the sort of a predicate that represents the summary of _function in the scope of _contract.

View File

@ -196,6 +196,16 @@ CVC4::Expr CVC4Interface::toCVC4Expr(Expression const& _expr)
solAssert(sortSort, ""); solAssert(sortSort, "");
return m_context.mkConst(CVC4::ArrayStoreAll(cvc4Sort(*sortSort->inner), arguments[1])); return m_context.mkConst(CVC4::ArrayStoreAll(cvc4Sort(*sortSort->inner), arguments[1]));
} }
else if (n == "tuple_get")
{
shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_expr.arguments[0].sort);
solAssert(tupleSort, "");
CVC4::DatatypeType tt = m_context.mkTupleType(cvc4Sort(tupleSort->components));
CVC4::Datatype const& dt = tt.getDatatype();
size_t index = std::stoi(_expr.arguments[1].name);
CVC4::Expr s = dt[0][index].getSelector();
return m_context.mkExpr(CVC4::kind::APPLY_SELECTOR, s, arguments[0]);
}
solAssert(false, ""); solAssert(false, "");
} }
@ -229,6 +239,11 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
return m_context.mkArrayType(cvc4Sort(*arraySort.domain), cvc4Sort(*arraySort.range)); return m_context.mkArrayType(cvc4Sort(*arraySort.domain), cvc4Sort(*arraySort.range));
} }
case Kind::Tuple:
{
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
return m_context.mkTupleType(cvc4Sort(tupleSort.components));
}
default: default:
break; break;
} }

View File

@ -192,7 +192,7 @@ void EncodingContext::pushSolver()
if (m_accumulateAssertions) if (m_accumulateAssertions)
m_assertions.push_back(assertions()); m_assertions.push_back(assertions());
else else
m_assertions.push_back(smt::Expression(true)); m_assertions.emplace_back(true);
} }
void EncodingContext::popSolver() void EncodingContext::popSolver()

View File

@ -107,6 +107,9 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract)
solAssert(m_currentContract == &_contract, ""); solAssert(m_currentContract == &_contract, "");
m_currentContract = nullptr; m_currentContract = nullptr;
if (m_callStack.empty())
m_context.popSolver();
} }
void SMTEncoder::endVisit(VariableDeclaration const& _varDecl) void SMTEncoder::endVisit(VariableDeclaration const& _varDecl)
@ -296,16 +299,22 @@ void SMTEncoder::endVisit(VariableDeclarationStatement const& _varDecl)
{ {
auto symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*init)); auto symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*init));
solAssert(symbTuple, ""); solAssert(symbTuple, "");
auto const& components = symbTuple->components(); auto const& symbComponents = symbTuple->components();
auto tupleType = dynamic_cast<TupleType const*>(init->annotation().type);
solAssert(tupleType, "");
solAssert(tupleType->components().size() == symbTuple->components().size(), "");
auto const& components = tupleType->components();
auto const& declarations = _varDecl.declarations(); auto const& declarations = _varDecl.declarations();
solAssert(components.size() == declarations.size(), ""); solAssert(symbComponents.size() == declarations.size(), "");
for (unsigned i = 0; i < declarations.size(); ++i) for (unsigned i = 0; i < declarations.size(); ++i)
if ( if (
components.at(i) && components.at(i) &&
declarations.at(i) && declarations.at(i) &&
m_context.knownVariable(*declarations.at(i)) m_context.knownVariable(*declarations.at(i))
) )
assignment(*declarations.at(i), components.at(i)->currentValue(declarations.at(i)->type())); assignment(*declarations.at(i), symbTuple->component(i, components.at(i), declarations.at(i)->type()));
} }
} }
else if (m_context.knownVariable(*_varDecl.declarations().front())) else if (m_context.knownVariable(*_varDecl.declarations().front()))
@ -354,7 +363,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
{ {
auto const& type = _assignment.annotation().type; auto const& type = _assignment.annotation().type;
vector<smt::Expression> rightArguments; vector<smt::Expression> rightArguments;
if (_assignment.rightHandSide().annotation().type->category() == Type::Category::Tuple) if (auto const* tupleTypeRight = dynamic_cast<TupleType const*>(_assignment.rightHandSide().annotation().type))
{ {
auto symbTupleLeft = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.leftHandSide())); auto symbTupleLeft = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(_assignment.leftHandSide()));
solAssert(symbTupleLeft, ""); solAssert(symbTupleLeft, "");
@ -365,17 +374,16 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
auto const& rightComponents = symbTupleRight->components(); auto const& rightComponents = symbTupleRight->components();
solAssert(leftComponents.size() == rightComponents.size(), ""); solAssert(leftComponents.size() == rightComponents.size(), "");
for (unsigned i = 0; i < leftComponents.size(); ++i) auto tupleTypeLeft = dynamic_cast<TupleType const*>(_assignment.leftHandSide().annotation().type);
{ solAssert(tupleTypeLeft, "");
auto const& left = leftComponents.at(i); solAssert(tupleTypeLeft->components().size() == leftComponents.size(), "");
auto const& right = rightComponents.at(i); auto const& typesLeft = tupleTypeLeft->components();
/// Right hand side tuple component cannot be empty.
solAssert(right, ""); solAssert(tupleTypeRight->components().size() == rightComponents.size(), "");
if (left) auto const& typesRight = tupleTypeRight->components();
rightArguments.push_back(right->currentValue(left->originalType()));
else for (unsigned i = 0; i < rightComponents.size(); ++i)
rightArguments.push_back(right->currentValue()); rightArguments.push_back(symbTupleRight->component(i, typesRight.at(i), typesLeft.at(i)));
}
} }
else else
{ {
@ -418,17 +426,16 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
solAssert(symbComponents.size() == tupleComponents->size(), ""); solAssert(symbComponents.size() == tupleComponents->size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i) for (unsigned i = 0; i < symbComponents.size(); ++i)
{ {
auto sComponent = symbComponents.at(i);
auto tComponent = tupleComponents->at(i); auto tComponent = tupleComponents->at(i);
if (sComponent && tComponent) if (tComponent)
{ {
if (auto varDecl = identifierToVariable(*tComponent)) if (auto varDecl = identifierToVariable(*tComponent))
m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl)); m_context.addAssertion(symbTuple->component(i) == currentValue(*varDecl));
else else
{ {
if (!m_context.knownExpression(*tComponent)) if (!m_context.knownExpression(*tComponent))
createExpr(*tComponent); createExpr(*tComponent);
m_context.addAssertion(sComponent->currentValue() == expr(*tComponent)); m_context.addAssertion(symbTuple->component(i) == expr(*tComponent));
} }
} }
} }
@ -463,7 +470,7 @@ void SMTEncoder::endVisit(UnaryOperation const& _op)
{ {
solAssert(smt::isInteger(_op.annotation().type->category()), ""); solAssert(smt::isInteger(_op.annotation().type->category()), "");
solAssert(_op.subExpression().annotation().lValueRequested, ""); solAssert(_op.subExpression().annotation().willBeWrittenTo, "");
if (auto identifier = dynamic_cast<Identifier const*>(&_op.subExpression())) if (auto identifier = dynamic_cast<Identifier const*>(&_op.subExpression()))
{ {
auto decl = identifierToVariable(*identifier); auto decl = identifierToVariable(*identifier);
@ -658,7 +665,6 @@ void SMTEncoder::initFunction(FunctionDefinition const& _function)
{ {
solAssert(m_callStack.empty(), ""); solAssert(m_callStack.empty(), "");
solAssert(m_currentContract, ""); solAssert(m_currentContract, "");
m_context.reset();
m_context.pushSolver(); m_context.pushSolver();
m_pathConditions.clear(); m_pathConditions.clear();
pushCallStack({&_function, nullptr}); pushCallStack({&_function, nullptr});
@ -700,7 +706,7 @@ void SMTEncoder::visitGasLeft(FunctionCall const& _funCall)
void SMTEncoder::endVisit(Identifier const& _identifier) void SMTEncoder::endVisit(Identifier const& _identifier)
{ {
if (_identifier.annotation().lValueRequested) if (_identifier.annotation().willBeWrittenTo)
{ {
// Will be translated as part of the node that requested the lvalue. // Will be translated as part of the node that requested the lvalue.
} }
@ -807,13 +813,15 @@ void SMTEncoder::endVisit(Return const& _return)
{ {
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*_return.expression())); auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*_return.expression()));
solAssert(symbTuple, ""); solAssert(symbTuple, "");
auto const& components = symbTuple->components(); solAssert(symbTuple->components().size() == returnParams.size(), "");
solAssert(components.size() == returnParams.size(), "");
auto const* tupleType = dynamic_cast<TupleType const*>(_return.expression()->annotation().type);
solAssert(tupleType, "");
auto const& types = tupleType->components();
solAssert(types.size() == returnParams.size(), "");
for (unsigned i = 0; i < returnParams.size(); ++i) for (unsigned i = 0; i < returnParams.size(); ++i)
{ m_context.addAssertion(symbTuple->component(i, types.at(i), returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
solAssert(components.at(i), "");
m_context.addAssertion(components.at(i)->currentValue(returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
}
} }
else if (returnParams.size() == 1) else if (returnParams.size() == 1)
m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front())); m_context.addAssertion(expr(*_return.expression(), returnParams.front()->type()) == m_context.newValue(*returnParams.front()));
@ -1676,14 +1684,10 @@ void SMTEncoder::createReturnedExpressions(FunctionCall const& _funCall)
solAssert(symbComponents.size() == returnParams.size(), ""); solAssert(symbComponents.size() == returnParams.size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i) for (unsigned i = 0; i < symbComponents.size(); ++i)
{ {
auto sComponent = symbComponents.at(i);
auto param = returnParams.at(i); auto param = returnParams.at(i);
solAssert(param, ""); solAssert(param, "");
if (sComponent) solAssert(m_context.knownVariable(*param), "");
{ m_context.addAssertion(symbTuple->component(i) == currentValue(*param));
solAssert(m_context.knownVariable(*param), "");
m_context.addAssertion(sComponent->currentValue() == currentValue(*param));
}
} }
} }
else if (returnParams.size() == 1) else if (returnParams.size() == 1)

View File

@ -28,6 +28,8 @@
#include <iostream> #include <iostream>
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <utility>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -37,10 +39,10 @@ using namespace solidity::frontend::smt;
SMTLib2Interface::SMTLib2Interface( SMTLib2Interface::SMTLib2Interface(
map<h256, string> const& _queryResponses, map<h256, string> const& _queryResponses,
ReadCallback::Callback const& _smtCallback ReadCallback::Callback _smtCallback
): ):
m_queryResponses(_queryResponses), m_queryResponses(_queryResponses),
m_smtCallback(_smtCallback) m_smtCallback(std::move(_smtCallback))
{ {
reset(); reset();
} }
@ -50,6 +52,7 @@ void SMTLib2Interface::reset()
m_accumulatedOutput.clear(); m_accumulatedOutput.clear();
m_accumulatedOutput.emplace_back(); m_accumulatedOutput.emplace_back();
m_variables.clear(); m_variables.clear();
m_userSorts.clear();
write("(set-option :produce-models true)"); write("(set-option :produce-models true)");
write("(set-logic ALL)"); write("(set-logic ALL)");
} }
@ -145,6 +148,14 @@ string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
sexpr += "(as const " + toSmtLibSort(*arraySort) + ") "; sexpr += "(as const " + toSmtLibSort(*arraySort) + ") ";
sexpr += toSExpr(_expr.arguments.at(1)); sexpr += toSExpr(_expr.arguments.at(1));
} }
else if (_expr.name == "tuple_get")
{
solAssert(_expr.arguments.size() == 2, "");
auto tupleSort = dynamic_pointer_cast<TupleSort>(_expr.arguments.at(0).sort);
unsigned index = std::stoi(_expr.arguments.at(1).name);
solAssert(index < tupleSort->members.size(), "");
sexpr += tupleSort->members.at(index) + " " + toSExpr(_expr.arguments.at(0));
}
else else
{ {
sexpr += _expr.name; sexpr += _expr.name;
@ -169,6 +180,22 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
solAssert(arraySort.domain && arraySort.range, ""); solAssert(arraySort.domain && arraySort.range, "");
return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')'; return "(Array " + toSmtLibSort(*arraySort.domain) + ' ' + toSmtLibSort(*arraySort.range) + ')';
} }
case Kind::Tuple:
{
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
if (!m_userSorts.count(tupleSort.name))
{
m_userSorts.insert(tupleSort.name);
string decl("(declare-datatypes ((" + tupleSort.name + " 0)) (((" + tupleSort.name);
solAssert(tupleSort.members.size() == tupleSort.components.size(), "");
for (unsigned i = 0; i < tupleSort.members.size(); ++i)
decl += " (" + tupleSort.members.at(i) + " " + toSmtLibSort(*tupleSort.components.at(i)) + ")";
decl += "))))";
write(decl);
}
return tupleSort.name;
}
default: default:
solAssert(false, "Invalid SMT sort"); solAssert(false, "Invalid SMT sort");
} }

View File

@ -39,7 +39,7 @@ class SMTLib2Interface: public SolverInterface, public boost::noncopyable
public: public:
explicit SMTLib2Interface( explicit SMTLib2Interface(
std::map<util::h256, std::string> const& _queryResponses, std::map<util::h256, std::string> const& _queryResponses,
ReadCallback::Callback const& _smtCallback ReadCallback::Callback _smtCallback
); );
void reset() override; void reset() override;
@ -74,6 +74,7 @@ private:
std::vector<std::string> m_accumulatedOutput; std::vector<std::string> m_accumulatedOutput;
std::map<std::string, SortPointer> m_variables; std::map<std::string, SortPointer> m_variables;
std::set<std::string> m_userSorts;
std::map<util::h256, std::string> const& m_queryResponses; std::map<util::h256, std::string> const& m_queryResponses;
std::vector<std::string> m_unhandledQueries; std::vector<std::string> m_unhandledQueries;

View File

@ -94,7 +94,8 @@ public:
{"mod", 2}, {"mod", 2},
{"select", 2}, {"select", 2},
{"store", 3}, {"store", 3},
{"const_array", 2} {"const_array", 2},
{"tuple_get", 2}
}; };
return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size(); return operatorsArity.count(name) && operatorsArity.at(name) == arguments.size();
} }
@ -166,6 +167,19 @@ public:
); );
} }
static Expression tuple_get(Expression _tuple, size_t _index)
{
solAssert(_tuple.sort->kind == Kind::Tuple, "");
std::shared_ptr<TupleSort> tupleSort = std::dynamic_pointer_cast<TupleSort>(_tuple.sort);
solAssert(tupleSort, "");
solAssert(_index < tupleSort->components.size(), "");
return Expression(
"tuple_get",
std::vector<Expression>{std::move(_tuple), Expression(_index)},
tupleSort->components.at(_index)
);
}
friend Expression operator!(Expression _a) friend Expression operator!(Expression _a)
{ {
return Expression("not", std::move(_a), Kind::Bool); return Expression("not", std::move(_a), Kind::Bool);

View File

@ -33,7 +33,8 @@ enum class Kind
Bool, Bool,
Function, Function,
Array, Array,
Sort Sort,
Tuple
}; };
struct Sort struct Sort
@ -115,6 +116,46 @@ struct SortSort: public Sort
SortPointer inner; SortPointer inner;
}; };
struct TupleSort: public Sort
{
TupleSort(
std::string _name,
std::vector<std::string> _members,
std::vector<SortPointer> _components
):
Sort(Kind::Tuple),
name(std::move(_name)),
members(std::move(_members)),
components(std::move(_components))
{}
bool operator==(Sort const& _other) const override
{
if (!Sort::operator==(_other))
return false;
auto _otherTuple = dynamic_cast<TupleSort const*>(&_other);
solAssert(_otherTuple, "");
if (name != _otherTuple->name)
return false;
if (members != _otherTuple->members)
return false;
if (components.size() != _otherTuple->components.size())
return false;
if (!std::equal(
components.begin(),
components.end(),
_otherTuple->components.begin(),
[&](SortPointer _a, SortPointer _b) { return *_a == *_b; }
))
return false;
return true;
}
std::string const name;
std::vector<std::string> const members;
std::vector<SortPointer> const components;
};
/** Frequently used sorts.*/ /** Frequently used sorts.*/
struct SortProvider struct SortProvider
{ {

View File

@ -75,6 +75,21 @@ SortPointer smtSort(frontend::Type const& _type)
return make_shared<ArraySort>(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType())); return make_shared<ArraySort>(SortProvider::intSort, smtSortAbstractFunction(*arrayType->baseType()));
} }
} }
case Kind::Tuple:
{
auto tupleType = dynamic_cast<frontend::TupleType const*>(&_type);
solAssert(tupleType, "");
vector<string> members;
auto const& tupleName = _type.identifier();
auto const& components = tupleType->components();
for (unsigned i = 0; i < components.size(); ++i)
members.emplace_back(tupleName + "_accessor_" + to_string(i));
return make_shared<TupleSort>(
tupleName,
members,
smtSortAbstractFunction(tupleType->components())
);
}
default: default:
// Abstract case. // Abstract case.
return SortProvider::intSort; return SortProvider::intSort;
@ -96,6 +111,17 @@ SortPointer smtSortAbstractFunction(frontend::Type const& _type)
return smtSort(_type); return smtSort(_type);
} }
vector<SortPointer> smtSortAbstractFunction(vector<frontend::TypePointer> const& _types)
{
vector<SortPointer> sorts;
for (auto const& type: _types)
if (type)
sorts.push_back(smtSortAbstractFunction(*type));
else
sorts.push_back(SortProvider::intSort);
return sorts;
}
Kind smtKind(frontend::Type::Category _category) Kind smtKind(frontend::Type::Category _category)
{ {
if (isNumber(_category)) if (isNumber(_category))
@ -106,6 +132,8 @@ Kind smtKind(frontend::Type::Category _category)
return Kind::Function; return Kind::Function;
else if (isMapping(_category) || isArray(_category)) else if (isMapping(_category) || isArray(_category))
return Kind::Array; return Kind::Array;
else if (isTuple(_category))
return Kind::Tuple;
// Abstract case. // Abstract case.
return Kind::Int; return Kind::Int;
} }
@ -350,4 +378,17 @@ void setSymbolicUnknownValue(Expression _expr, frontend::TypePointer const& _typ
} }
} }
optional<Expression> symbolicTypeConversion(TypePointer _from, TypePointer _to)
{
if (_to && _from)
// StringLiterals are encoded as SMT arrays in the generic case,
// but they can also be compared/assigned to fixed bytes, in which
// case they'd need to be encoded as numbers.
if (auto strType = dynamic_cast<StringLiteralType const*>(_from))
if (_to->category() == frontend::Type::Category::FixedBytes)
return smt::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add)));
return std::nullopt;
}
} }

View File

@ -31,6 +31,7 @@ std::vector<SortPointer> smtSort(std::vector<frontend::TypePointer> const& _type
/// If _type has type Function, abstract it to Integer. /// If _type has type Function, abstract it to Integer.
/// Otherwise return smtSort(_type). /// Otherwise return smtSort(_type).
SortPointer smtSortAbstractFunction(frontend::Type const& _type); SortPointer smtSortAbstractFunction(frontend::Type const& _type);
std::vector<SortPointer> smtSortAbstractFunction(std::vector<frontend::TypePointer> const& _types);
/// Returns the SMT kind that models the Solidity type type category _category. /// Returns the SMT kind that models the Solidity type type category _category.
Kind smtKind(frontend::Type::Category _category); Kind smtKind(frontend::Type::Category _category);
@ -69,4 +70,5 @@ void setSymbolicZeroValue(Expression _expr, frontend::TypePointer const& _type,
void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context); void setSymbolicUnknownValue(SymbolicVariable const& _variable, EncodingContext& _context);
void setSymbolicUnknownValue(Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context); void setSymbolicUnknownValue(Expression _expr, frontend::TypePointer const& _type, EncodingContext& _context);
std::optional<Expression> symbolicTypeConversion(TypePointer _from, TypePointer _to);
} }

View File

@ -230,16 +230,9 @@ SymbolicArrayVariable::SymbolicArrayVariable(
smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const smt::Expression SymbolicArrayVariable::currentValue(frontend::TypePointer const& _targetType) const
{ {
if (_targetType) optional<smt::Expression> conversion = symbolicTypeConversion(m_originalType, _targetType);
{ if (conversion)
solAssert(m_originalType, ""); return *conversion;
// StringLiterals are encoded as SMT arrays in the generic case,
// but they can also be compared/assigned to fixed bytes, in which
// case they'd need to be encoded as numbers.
if (auto strType = dynamic_cast<StringLiteralType const*>(m_originalType))
if (_targetType->category() == frontend::Type::Category::FixedBytes)
return smt::Expression(u256(toHex(util::asBytes(strType->value()), util::HexPrefix::Add)));
}
return SymbolicVariable::currentValue(_targetType); return SymbolicVariable::currentValue(_targetType);
} }
@ -262,16 +255,34 @@ SymbolicTupleVariable::SymbolicTupleVariable(
SymbolicVariable(_type, _type, move(_uniqueName), _context) SymbolicVariable(_type, _type, move(_uniqueName), _context)
{ {
solAssert(isTuple(m_type->category()), ""); solAssert(isTuple(m_type->category()), "");
auto const& tupleType = dynamic_cast<TupleType const&>(*m_type); }
auto const& componentsTypes = tupleType.components();
for (unsigned i = 0; i < componentsTypes.size(); ++i) SymbolicTupleVariable::SymbolicTupleVariable(
if (componentsTypes.at(i)) SortPointer _sort,
{ string _uniqueName,
string componentName = m_uniqueName + "_component_" + to_string(i); EncodingContext& _context
auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context); ):
solAssert(result.second, ""); SymbolicVariable(move(_sort), move(_uniqueName), _context)
m_components.emplace_back(move(result.second)); {
} solAssert(m_sort->kind == Kind::Tuple, "");
else }
m_components.emplace_back(nullptr);
vector<SortPointer> const& SymbolicTupleVariable::components()
{
auto tupleSort = dynamic_pointer_cast<TupleSort>(m_sort);
solAssert(tupleSort, "");
return tupleSort->components;
}
smt::Expression SymbolicTupleVariable::component(
size_t _index,
TypePointer _fromType,
TypePointer _toType
)
{
optional<smt::Expression> conversion = symbolicTypeConversion(_fromType, _toType);
if (conversion)
return *conversion;
return smt::Expression::tuple_get(currentValue(), _index);
} }

View File

@ -249,14 +249,18 @@ public:
std::string _uniqueName, std::string _uniqueName,
EncodingContext& _context EncodingContext& _context
); );
SymbolicTupleVariable(
SortPointer _sort,
std::string _uniqueName,
EncodingContext& _context
);
std::vector<std::shared_ptr<SymbolicVariable>> const& components() std::vector<SortPointer> const& components();
{ Expression component(
return m_components; size_t _index,
} TypePointer _fromType = nullptr,
TypePointer _toType = nullptr
private: );
std::vector<std::shared_ptr<SymbolicVariable>> m_components;
}; };
} }

View File

@ -40,15 +40,15 @@ set<VariableDeclaration const*> VariableUsage::touchedVariables(ASTNode const& _
void VariableUsage::endVisit(Identifier const& _identifier) void VariableUsage::endVisit(Identifier const& _identifier)
{ {
if (_identifier.annotation().lValueRequested) if (_identifier.annotation().willBeWrittenTo)
checkIdentifier(_identifier); checkIdentifier(_identifier);
} }
void VariableUsage::endVisit(IndexAccess const& _indexAccess) void VariableUsage::endVisit(IndexAccess const& _indexAccess)
{ {
if (_indexAccess.annotation().lValueRequested) if (_indexAccess.annotation().willBeWrittenTo)
{ {
/// identifier.annotation().lValueRequested == false, that's why we /// identifier.annotation().willBeWrittenTo == false, that's why we
/// need to check that before. /// need to check that before.
auto identifier = dynamic_cast<Identifier const*>(SMTEncoder::leftmostBase(_indexAccess)); auto identifier = dynamic_cast<Identifier const*>(SMTEncoder::leftmostBase(_indexAccess));
if (identifier) if (identifier)

View File

@ -193,6 +193,11 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
solAssert(arraySort && arraySort->domain, ""); solAssert(arraySort && arraySort->domain, "");
return z3::const_array(z3Sort(*arraySort->domain), arguments[1]); return z3::const_array(z3Sort(*arraySort->domain), arguments[1]);
} }
else if (n == "tuple_get")
{
size_t index = std::stoi(_expr.arguments[1].name);
return z3::func_decl(m_context, Z3_get_tuple_sort_field_decl(m_context, z3Sort(*_expr.arguments[0].sort), index))(arguments[0]);
}
solAssert(false, ""); solAssert(false, "");
} }
@ -217,6 +222,28 @@ z3::sort Z3Interface::z3Sort(Sort const& _sort)
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort); auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
return m_context.array_sort(z3Sort(*arraySort.domain), z3Sort(*arraySort.range)); return m_context.array_sort(z3Sort(*arraySort.domain), z3Sort(*arraySort.range));
} }
case Kind::Tuple:
{
auto const& tupleSort = dynamic_cast<TupleSort const&>(_sort);
vector<char const*> cMembers;
for (auto const& member: tupleSort.members)
cMembers.emplace_back(member.c_str());
/// Using this instead of the function below because with that one
/// we can't use `&sorts[0]` here.
vector<z3::sort> sorts;
for (auto const& sort: tupleSort.components)
sorts.push_back(z3Sort(*sort));
z3::func_decl_vector projs(m_context);
z3::func_decl tupleConstructor = m_context.tuple_sort(
tupleSort.name.c_str(),
tupleSort.members.size(),
&cMembers[0],
&sorts[0],
projs
);
return tupleConstructor.range();
}
default: default:
break; break;
} }

View File

@ -27,6 +27,7 @@
#include <libsolidity/analysis/ControlFlowAnalyzer.h> #include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h> #include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/ContractLevelChecker.h> #include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/DocStringAnalyser.h> #include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/GlobalContext.h> #include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h> #include <libsolidity/analysis/NameAndTypeResolver.h>
@ -67,6 +68,7 @@
#include <json/json.h> #include <json/json.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <utility>
using namespace std; using namespace std;
using namespace solidity; using namespace solidity;
@ -78,8 +80,8 @@ using solidity::util::toHex;
static int g_compilerStackCounts = 0; static int g_compilerStackCounts = 0;
CompilerStack::CompilerStack(ReadCallback::Callback const& _readFile): CompilerStack::CompilerStack(ReadCallback::Callback _readFile):
m_readFile{_readFile}, m_readFile{std::move(_readFile)},
m_enabledSMTSolvers{smt::SMTSolverChoice::All()}, m_enabledSMTSolvers{smt::SMTSolverChoice::All()},
m_generateIR{false}, m_generateIR{false},
m_generateEwasm{false}, m_generateEwasm{false},
@ -348,6 +350,11 @@ bool CompilerStack::analyze()
} }
DeclarationTypeChecker declarationTypeChecker(m_errorReporter, m_evmVersion);
for (Source const* source: m_sourceOrder)
if (source->ast && !declarationTypeChecker.check(*source->ast))
return false;
// Next, we check inheritance, overrides, function collisions and other things at // Next, we check inheritance, overrides, function collisions and other things at
// contract or function level. // contract or function level.
// This also calculates whether a contract is abstract, which is needed by the // This also calculates whether a contract is abstract, which is needed by the

View File

@ -108,7 +108,7 @@ public:
/// Creates a new compiler stack. /// Creates a new compiler stack.
/// @param _readFile callback used to read files for import statements. Must return /// @param _readFile callback used to read files for import statements. Must return
/// and must not emit exceptions. /// and must not emit exceptions.
explicit CompilerStack(ReadCallback::Callback const& _readFile = ReadCallback::Callback()); explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback());
~CompilerStack(); ~CompilerStack();

View File

@ -200,7 +200,7 @@ bool isArtifactRequested(Json::Value const& _outputSelection, string const& _fil
/// for Contract-level targets try both contract name and wildcard /// for Contract-level targets try both contract name and wildcard
vector<string> contracts{ _contract }; vector<string> contracts{ _contract };
if (!_contract.empty()) if (!_contract.empty())
contracts.push_back("*"); contracts.emplace_back("*");
for (auto const& contract: contracts) for (auto const& contract: contracts)
if ( if (
_outputSelection[file].isMember(contract) && _outputSelection[file].isMember(contract) &&
@ -591,7 +591,7 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
)); ));
else else
{ {
ret.sources[sourceName] = result.responseOrErrorMessage; ret.sources[sourceName] = result.responseOrErrorMessage;
found = true; found = true;
break; break;
} }

View File

@ -24,8 +24,9 @@
#include <libsolidity/interface/CompilerStack.h> #include <libsolidity/interface/CompilerStack.h>
#include <optional>
#include <boost/variant.hpp> #include <boost/variant.hpp>
#include <optional>
#include <utility>
namespace solidity::frontend namespace solidity::frontend
{ {
@ -40,8 +41,8 @@ public:
/// Creates a new StandardCompiler. /// Creates a new StandardCompiler.
/// @param _readFile callback used to read files for import statements. Must return /// @param _readFile callback used to read files for import statements. Must return
/// and must not emit exceptions. /// and must not emit exceptions.
explicit StandardCompiler(ReadCallback::Callback const& _readFile = ReadCallback::Callback()): explicit StandardCompiler(ReadCallback::Callback _readFile = ReadCallback::Callback()):
m_readFile(_readFile) m_readFile(std::move(_readFile))
{ {
} }

View File

@ -695,7 +695,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
); );
bool isIndexed = false; bool isIndexed = false;
VariableDeclaration::Constantness constantness = VariableDeclaration::Constantness::Mutable; VariableDeclaration::Mutability mutability = VariableDeclaration::Mutability::Mutable;
ASTPointer<OverrideSpecifier> overrides = nullptr; ASTPointer<OverrideSpecifier> overrides = nullptr;
Visibility visibility(Visibility::Default); Visibility visibility(Visibility::Default);
VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified; VariableDeclaration::Location location = VariableDeclaration::Location::Unspecified;
@ -732,15 +732,15 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
isIndexed = true; isIndexed = true;
else if (token == Token::Constant || token == Token::Immutable) else if (token == Token::Constant || token == Token::Immutable)
{ {
if (constantness != VariableDeclaration::Constantness::Mutable) if (mutability != VariableDeclaration::Mutability::Mutable)
parserError( parserError(
string("Constantness already set to ") + string("Mutability already set to ") +
(constantness == VariableDeclaration::Constantness::Constant ? "\"constant\"" : "\"immutable\"") (mutability == VariableDeclaration::Mutability::Constant ? "\"constant\"" : "\"immutable\"")
); );
else if (token == Token::Constant) else if (token == Token::Constant)
constantness = VariableDeclaration::Constantness::Constant; mutability = VariableDeclaration::Mutability::Constant;
else if (token == Token::Immutable) else if (token == Token::Immutable)
constantness = VariableDeclaration::Constantness::Immutable; mutability = VariableDeclaration::Mutability::Immutable;
} }
else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token)) else if (_options.allowLocationSpecifier && TokenTraits::isLocationSpecifier(token))
{ {
@ -800,7 +800,7 @@ ASTPointer<VariableDeclaration> Parser::parseVariableDeclaration(
visibility, visibility,
_options.isStateVariable, _options.isStateVariable,
isIndexed, isIndexed,
constantness, mutability,
overrides, overrides,
location location
); );

View File

@ -114,6 +114,10 @@ struct BreadthFirstSearch
} }
return *this; return *this;
} }
void abort()
{
verticesToTraverse.clear();
}
std::set<V> verticesToTraverse; std::set<V> verticesToTraverse;
std::set<V> visited{}; std::set<V> visited{};

View File

@ -47,6 +47,7 @@
#include <boost/multiprecision/cpp_int.hpp> #include <boost/multiprecision/cpp_int.hpp>
#include <map> #include <map>
#include <utility>
#include <vector> #include <vector>
#include <functional> #include <functional>
#include <string> #include <string>
@ -120,7 +121,7 @@ inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes)
class ScopeGuard class ScopeGuard
{ {
public: public:
explicit ScopeGuard(std::function<void(void)> _f): m_f(_f) {} explicit ScopeGuard(std::function<void(void)> _f): m_f(std::move(_f)) {}
~ScopeGuard() { m_f(); } ~ScopeGuard() { m_f(); }
private: private:

View File

@ -34,11 +34,12 @@
#include <set> #include <set>
#include <functional> #include <functional>
#include <utility> #include <utility>
#include <type_traits>
/// Operators need to stay in the global namespace. /// Operators need to stay in the global namespace.
/// Concatenate the contents of a container onto a vector /// Concatenate the contents of a container onto a vector
template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U const& _b) template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U& _b)
{ {
for (auto const& i: _b) for (auto const& i: _b)
_a.push_back(i); _a.push_back(i);
@ -51,7 +52,7 @@ template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U&& _
return _a; return _a;
} }
/// Concatenate the contents of a container onto a multiset /// Concatenate the contents of a container onto a multiset
template <class U, class... T> std::multiset<T...>& operator+=(std::multiset<T...>& _a, U const& _b) template <class U, class... T> std::multiset<T...>& operator+=(std::multiset<T...>& _a, U& _b)
{ {
_a.insert(_b.begin(), _b.end()); _a.insert(_b.begin(), _b.end());
return _a; return _a;
@ -64,7 +65,7 @@ template <class U, class... T> std::multiset<T...>& operator+=(std::multiset<T..
return _a; return _a;
} }
/// Concatenate the contents of a container onto a set /// Concatenate the contents of a container onto a set
template <class U, class... T> std::set<T...>& operator+=(std::set<T...>& _a, U const& _b) template <class U, class... T> std::set<T...>& operator+=(std::set<T...>& _a, U& _b)
{ {
_a.insert(_b.begin(), _b.end()); _a.insert(_b.begin(), _b.end());
return _a; return _a;
@ -141,6 +142,36 @@ inline std::multiset<T...>& operator-=(std::multiset<T...>& _a, C const& _b)
namespace solidity::util namespace solidity::util
{ {
/// Functional map.
/// Returns a container _oc applying @param _op to each element in @param _c.
/// By default _oc is a vector.
/// If another return type is desired, an empty contained of that type
/// is given as @param _oc.
template<class Container, class Callable, class OutputContainer =
std::vector<std::invoke_result_t<
Callable,
decltype(*std::begin(std::declval<Container>()))
>>>
auto applyMap(Container const& _c, Callable&& _op, OutputContainer _oc = OutputContainer{})
{
std::transform(std::begin(_c), std::end(_c), std::inserter(_oc, std::end(_oc)), _op);
return _oc;
}
/// Functional fold.
/// Given a container @param _c, an initial value @param _acc,
/// and a binary operator @param _binaryOp(T, U), accumulate
/// the elements of _c over _acc.
/// Note that <numeric> has a similar function `accumulate` which
/// until C++20 does *not* std::move the partial accumulated.
template<class C, class T, class Callable>
auto fold(C const& _c, T _acc, Callable&& _binaryOp)
{
for (auto const& e: _c)
_acc = _binaryOp(std::move(_acc), e);
return _acc;
}
template <class T, class U> template <class T, class U>
T convertContainer(U const& _from) T convertContainer(U const& _from)
{ {

View File

@ -160,7 +160,7 @@ bytes solidity::util::ipfsHash(string _data)
Chunks allChunks; Chunks allChunks;
for (unsigned long chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++) for (size_t chunkIndex = 0; chunkIndex < chunkCount; chunkIndex++)
{ {
bytes chunkBytes = asBytes( bytes chunkBytes = asBytes(
_data.substr(chunkIndex * maxChunkSize, min(maxChunkSize, _data.length() - chunkIndex * maxChunkSize)) _data.substr(chunkIndex * maxChunkSize, min(maxChunkSize, _data.length() - chunkIndex * maxChunkSize))

View File

@ -36,7 +36,7 @@ namespace solidity::util
/// ///
template <class ResultType> template <class ResultType>
class Result class [[nodiscard]] Result
{ {
public: public:
/// Constructs a result with _value and an empty message. /// Constructs a result with _value and an empty message.

View File

@ -255,14 +255,14 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
yulAssert(!_funCall.functionName.name.empty(), ""); yulAssert(!_funCall.functionName.name.empty(), "");
vector<YulString> const* parameterTypes = nullptr; vector<YulString> const* parameterTypes = nullptr;
vector<YulString> const* returnTypes = nullptr; vector<YulString> const* returnTypes = nullptr;
bool needsLiteralArguments = false; vector<bool> const* needsLiteralArguments = nullptr;
if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name)) if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name))
{ {
parameterTypes = &f->parameters; parameterTypes = &f->parameters;
returnTypes = &f->returns; returnTypes = &f->returns;
if (f->literalArguments) if (f->literalArguments)
needsLiteralArguments = true; needsLiteralArguments = &f->literalArguments.value();
} }
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{ else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
[&](Scope::Variable const&) [&](Scope::Variable const&)
@ -293,11 +293,13 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
); );
vector<YulString> argTypes; vector<YulString> argTypes;
for (auto const& arg: _funCall.arguments | boost::adaptors::reversed) for (size_t i = _funCall.arguments.size(); i > 0; i--)
{ {
Expression const& arg = _funCall.arguments[i - 1];
argTypes.emplace_back(expectExpression(arg)); argTypes.emplace_back(expectExpression(arg));
if (needsLiteralArguments) if (needsLiteralArguments && (*needsLiteralArguments)[i - 1])
{ {
if (!holds_alternative<Literal>(arg)) if (!holds_alternative<Literal>(arg))
typeError( typeError(

View File

@ -34,6 +34,7 @@
#include <list> #include <list>
#include <memory> #include <memory>
#include <optional> #include <optional>
#include <utility>
namespace solidity::langutil namespace solidity::langutil
{ {
@ -58,14 +59,14 @@ public:
AsmAnalysisInfo& _analysisInfo, AsmAnalysisInfo& _analysisInfo,
langutil::ErrorReporter& _errorReporter, langutil::ErrorReporter& _errorReporter,
Dialect const& _dialect, Dialect const& _dialect,
ExternalIdentifierAccess::Resolver const& _resolver = ExternalIdentifierAccess::Resolver(), ExternalIdentifierAccess::Resolver _resolver = ExternalIdentifierAccess::Resolver(),
std::set<YulString> const& _dataNames = {} std::set<YulString> _dataNames = {}
): ):
m_resolver(_resolver), m_resolver(std::move(_resolver)),
m_info(_analysisInfo), m_info(_analysisInfo),
m_errorReporter(_errorReporter), m_errorReporter(_errorReporter),
m_dialect(_dialect), m_dialect(_dialect),
m_dataNames(_dataNames) m_dataNames(std::move(_dataNames))
{ {
if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect)) if (EVMDialect const* evmDialect = dynamic_cast<EVMDialect const*>(&m_dialect))
m_evmVersion = evmDialect->evmVersion(); m_evmVersion = evmDialect->evmVersion();

View File

@ -28,6 +28,7 @@
#include <vector> #include <vector>
#include <set> #include <set>
#include <optional>
namespace solidity::yul namespace solidity::yul
{ {
@ -46,8 +47,8 @@ struct BuiltinFunction
ControlFlowSideEffects controlFlowSideEffects; ControlFlowSideEffects controlFlowSideEffects;
/// If true, this is the msize instruction. /// If true, this is the msize instruction.
bool isMSize = false; bool isMSize = false;
/// If true, can only accept literals as arguments and they cannot be moved to variables. /// If set, same length as the arguments, if true at index i, the i'th argument has to be a literal which means it can't be moved to variables.
bool literalArguments = false; std::optional<std::vector<bool>> literalArguments;
}; };
struct Dialect: boost::noncopyable struct Dialect: boost::noncopyable

View File

@ -29,6 +29,7 @@
#include <boost/range/adaptor/reversed.hpp> #include <boost/range/adaptor/reversed.hpp>
#include <utility>
#include <variant> #include <variant>
using namespace std; using namespace std;
@ -100,7 +101,7 @@ CodeTransform::CodeTransform(
EVMDialect const& _dialect, EVMDialect const& _dialect,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _evm15, bool _evm15,
ExternalIdentifierAccess const& _identifierAccess, ExternalIdentifierAccess _identifierAccess,
bool _useNamedLabelsForFunctions, bool _useNamedLabelsForFunctions,
shared_ptr<Context> _context shared_ptr<Context> _context
): ):
@ -111,8 +112,8 @@ CodeTransform::CodeTransform(
m_allowStackOpt(_allowStackOpt), m_allowStackOpt(_allowStackOpt),
m_evm15(_evm15), m_evm15(_evm15),
m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions), m_useNamedLabelsForFunctions(_useNamedLabelsForFunctions),
m_identifierAccess(_identifierAccess), m_identifierAccess(std::move(_identifierAccess)),
m_context(_context) m_context(std::move(_context))
{ {
if (!m_context) if (!m_context)
{ {

View File

@ -152,7 +152,7 @@ protected:
EVMDialect const& _dialect, EVMDialect const& _dialect,
BuiltinContext& _builtinContext, BuiltinContext& _builtinContext,
bool _evm15, bool _evm15,
ExternalIdentifierAccess const& _identifierAccess, ExternalIdentifierAccess _identifierAccess,
bool _useNamedLabelsForFunctions, bool _useNamedLabelsForFunctions,
std::shared_ptr<Context> _context std::shared_ptr<Context> _context
); );

View File

@ -55,7 +55,7 @@ pair<YulString, BuiltinFunctionForEVM> createEVMFunction(
f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction); f.controlFlowSideEffects.terminates = evmasm::SemanticInformation::terminatesControlFlow(_instruction);
f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction); f.controlFlowSideEffects.reverts = evmasm::SemanticInformation::reverts(_instruction);
f.isMSize = _instruction == evmasm::Instruction::MSIZE; f.isMSize = _instruction == evmasm::Instruction::MSIZE;
f.literalArguments = false; f.literalArguments.reset();
f.instruction = _instruction; f.instruction = _instruction;
f.generateCode = [_instruction]( f.generateCode = [_instruction](
FunctionCall const&, FunctionCall const&,
@ -75,17 +75,22 @@ pair<YulString, BuiltinFunctionForEVM> createFunction(
size_t _params, size_t _params,
size_t _returns, size_t _returns,
SideEffects _sideEffects, SideEffects _sideEffects,
bool _literalArguments, vector<bool> _literalArguments,
std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode std::function<void(FunctionCall const&, AbstractAssembly&, BuiltinContext&, std::function<void()>)> _generateCode
) )
{ {
solAssert(_literalArguments.size() == _params || _literalArguments.empty(), "");
YulString name{std::move(_name)}; YulString name{std::move(_name)};
BuiltinFunctionForEVM f; BuiltinFunctionForEVM f;
f.name = name; f.name = name;
f.parameters.resize(_params); f.parameters.resize(_params);
f.returns.resize(_returns); f.returns.resize(_returns);
f.sideEffects = std::move(_sideEffects); f.sideEffects = std::move(_sideEffects);
f.literalArguments = _literalArguments; if (!_literalArguments.empty())
f.literalArguments = std::move(_literalArguments);
else
f.literalArguments.reset();
f.isMSize = false; f.isMSize = false;
f.instruction = {}; f.instruction = {};
f.generateCode = std::move(_generateCode); f.generateCode = std::move(_generateCode);
@ -107,7 +112,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
if (_objectAccess) if (_objectAccess)
{ {
builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, true, []( builtins.emplace(createFunction("datasize", 1, 1, SideEffects{}, {true}, [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _context, BuiltinContext& _context,
@ -128,7 +133,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
_assembly.appendDataSize(_context.subIDs.at(dataName)); _assembly.appendDataSize(_context.subIDs.at(dataName));
} }
})); }));
builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, true, []( builtins.emplace(createFunction("dataoffset", 1, 1, SideEffects{}, {true}, [](
FunctionCall const& _call, FunctionCall const& _call,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext& _context, BuiltinContext& _context,
@ -154,7 +159,7 @@ map<YulString, BuiltinFunctionForEVM> createBuiltins(langutil::EVMVersion _evmVe
3, 3,
0, 0,
SideEffects{false, false, false, false, true}, SideEffects{false, false, false, false, true},
false, {},
[]( [](
FunctionCall const&, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
@ -262,7 +267,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
m_functions["popbool"_yulstring] = m_functions["pop"_yulstring]; m_functions["popbool"_yulstring] = m_functions["pop"_yulstring];
m_functions["popbool"_yulstring].name = "popbool"_yulstring; m_functions["popbool"_yulstring].name = "popbool"_yulstring;
m_functions["popbool"_yulstring].parameters = {"bool"_yulstring}; m_functions["popbool"_yulstring].parameters = {"bool"_yulstring};
m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, false, []( m_functions.insert(createFunction("bool_to_u256", 1, 1, {}, {}, [](
FunctionCall const&, FunctionCall const&,
AbstractAssembly&, AbstractAssembly&,
BuiltinContext&, BuiltinContext&,
@ -272,7 +277,7 @@ EVMDialectTyped::EVMDialectTyped(langutil::EVMVersion _evmVersion, bool _objectA
})); }));
m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring}; m_functions["bool_to_u256"_yulstring].parameters = {"bool"_yulstring};
m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring}; m_functions["bool_to_u256"_yulstring].returns = {"u256"_yulstring};
m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, false, []( m_functions.insert(createFunction("u256_to_bool", 1, 1, {}, {}, [](
FunctionCall const&, FunctionCall const&,
AbstractAssembly& _assembly, AbstractAssembly& _assembly,
BuiltinContext&, BuiltinContext&,

Some files were not shown because too many files have changed in this diff Show More