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

View File

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

View File

@ -12,7 +12,7 @@ Compiler Features:
Bugfixes:
### 0.6.6 (unreleased)
### 0.6.7 (unreleased)
Language Features:
@ -21,7 +21,25 @@ Compiler Features:
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.
* 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
(left) side with zero-bytes such that the length is 32 bytes.
- ``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``
- ``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

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",
"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": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
@ -131,6 +132,7 @@
},
"0.1.7": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
"ZeroFunctionSelector",
@ -151,6 +153,7 @@
},
"0.2.0": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -172,6 +175,7 @@
},
"0.2.1": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -193,6 +197,7 @@
},
"0.2.2": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"ExpExponentCleanup",
"NestedArrayFunctionCallDecoder",
@ -214,6 +219,7 @@
},
"0.3.0": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -237,6 +243,7 @@
},
"0.3.1": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -259,6 +266,7 @@
},
"0.3.2": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -281,6 +289,7 @@
},
"0.3.3": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -302,6 +311,7 @@
},
"0.3.4": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -323,6 +333,7 @@
},
"0.3.5": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -344,6 +355,7 @@
},
"0.3.6": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -363,6 +375,7 @@
},
"0.4.0": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -382,6 +395,7 @@
},
"0.4.1": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -401,6 +415,7 @@
},
"0.4.10": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -418,6 +433,7 @@
},
"0.4.11": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -434,6 +450,7 @@
},
"0.4.12": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -449,6 +466,7 @@
},
"0.4.13": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -464,6 +482,7 @@
},
"0.4.14": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -478,6 +497,7 @@
},
"0.4.15": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -491,6 +511,7 @@
},
"0.4.16": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -506,6 +527,7 @@
},
"0.4.17": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -522,6 +544,7 @@
},
"0.4.18": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -537,6 +560,7 @@
},
"0.4.19": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -553,6 +577,7 @@
},
"0.4.2": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -571,6 +596,7 @@
},
"0.4.20": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -587,6 +613,7 @@
},
"0.4.21": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -603,6 +630,7 @@
},
"0.4.22": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -619,6 +647,7 @@
},
"0.4.23": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -634,6 +663,7 @@
},
"0.4.24": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -649,6 +679,7 @@
},
"0.4.25": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -662,6 +693,7 @@
},
"0.4.26": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -672,6 +704,7 @@
},
"0.4.3": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -689,6 +722,7 @@
},
"0.4.4": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"IncorrectEventSignatureInLibraries_0.4.x",
@ -705,6 +739,7 @@
},
"0.4.5": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x",
@ -723,6 +758,7 @@
},
"0.4.6": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"UninitializedFunctionPointerInConstructor_0.4.x",
@ -740,6 +776,7 @@
},
"0.4.7": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -757,6 +794,7 @@
},
"0.4.8": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -774,6 +812,7 @@
},
"0.4.9": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -791,6 +830,7 @@
},
"0.5.0": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -804,6 +844,7 @@
},
"0.5.1": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -817,6 +858,7 @@
},
"0.5.10": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -826,6 +868,7 @@
},
"0.5.11": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -834,6 +877,7 @@
},
"0.5.12": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -842,6 +886,7 @@
},
"0.5.13": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -850,6 +895,7 @@
},
"0.5.14": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -859,6 +905,7 @@
},
"0.5.15": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5"
@ -867,6 +914,7 @@
},
"0.5.16": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden"
],
@ -874,12 +922,14 @@
},
"0.5.17": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
"released": "2020-03-17"
},
"0.5.2": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -893,6 +943,7 @@
},
"0.5.3": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -906,6 +957,7 @@
},
"0.5.4": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -919,6 +971,7 @@
},
"0.5.5": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"SignedArrayStorageCopy",
@ -934,6 +987,7 @@
},
"0.5.6": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
@ -949,6 +1003,7 @@
},
"0.5.7": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"ABIEncoderV2CalldataStructsWithStaticallySizedAndDynamicallyEncodedMembers",
@ -962,6 +1017,7 @@
},
"0.5.8": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -974,6 +1030,7 @@
},
"0.5.9": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"privateCanBeOverridden",
"YulOptimizerRedundantAssignmentBreakContinue0.5",
@ -985,6 +1042,7 @@
},
"0.6.0": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow",
"YulOptimizerRedundantAssignmentBreakContinue"
],
@ -992,30 +1050,40 @@
},
"0.6.1": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
"released": "2020-01-02"
},
"0.6.2": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
"released": "2020-01-27"
},
"0.6.3": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
"released": "2020-02-18"
},
"0.6.4": {
"bugs": [
"TupleAssignmentMultiStackSlotComponents",
"MemoryArrayCreationOverflow"
],
"released": "2020-03-10"
},
"0.6.5": {
"bugs": [],
"bugs": [
"TupleAssignmentMultiStackSlotComponents"
],
"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".
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
# .htaccess) here, relative to this directory. These files are copied
# 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,
# 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>`.
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 { ... }``
(without the ``function`` keyword).
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
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>`
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
@ -245,7 +245,7 @@ contract cannot receive Ether through regular transactions and throws an
exception.
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
will consume more gas than the 2300 gas stipend:
@ -265,7 +265,7 @@ will consume more gas than the 2300 gas stipend:
.. warning::
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``.
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``.
For state variables, ``external`` is not possible.
``external``:
``external``
External functions are part of the contract interface,
which means they can be called from other contracts and
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
is not copied from calldata to memory.
``public``:
``public``
Public functions are part of the contract interface
and can be either called internally or via
messages. For public state variables, an automatic getter
function (see below) is generated.
``internal``:
``internal``
Those functions and state variables can only be
accessed internally (i.e. from within the current contract
or contracts deriving from it), without using ``this``.
``private``:
``private``
Private functions and state variables are only
visible for the contract they are defined in and not in
derived contracts.

View File

@ -81,26 +81,55 @@ Thank you for your help!
Running the compiler tests
==========================
The ``./scripts/tests.sh`` script executes most Solidity tests automatically,
but for quicker feedback, you might want to run specific tests.
Prerequisites
-------------
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
`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.
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,
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on MacOS. If it is not found, the relevant tests
are skipped. To run all tests, download the library from
`Github <https://github.com/ethereum/evmone/releases/tag/v0.3.0>`_
and either place it in the project root path or inside the ``deps`` folder.
``evmone.dll`` on Windows systems and ``libevmone.dylib`` on macOS. If it is not found, tests that
use it are skipped. These tests are ``libsolididty/semanticTests``, ``libsolidity/GasCosts``,
``libsolidity/SolidityEndToEndTest``, part of the soltest suite. To run all tests, download the library from
`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:
``./scripts/soltest.sh --no-smt``.
If the ``libz3`` library is not installed on your system, you should disable the
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.
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,
@ -109,20 +138,10 @@ See especially:
.. 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``.
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".
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 ..
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.
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.
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 CI runs additional tests (including ``solc-js`` and testing third party Solidity
frameworks) that require compiling the Emscripten target.
Writing and running syntax tests
--------------------------------

View File

@ -433,7 +433,7 @@ The full contract
.. note::
The function ``splitSignature`` does not use all security
checks. A real implementation should use a more rigorously tested library,
such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-solidity/blob/master/contracts/ECRecovery.sol>`_ of this code.
such as openzepplin's `version <https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/cryptography/ECDSA.sol>`_ of this code.
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>`_
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
=============
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
further down this page. The
: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>`_.
Note: The solc-js project is derived from the C++
@ -53,10 +53,10 @@ Please refer to the solc-js repository for instructions.
.. 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`)
expecting the behaviour of `solc` will not work with `solcjs`.
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``.
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**.
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.
.. 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.
.. 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.
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::
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
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
in the same way as if it were tagged with `@notice`.
used then the Solidity compiler will interpret a ``///`` or ``/**`` comment
in the same way as if it were tagged with ``@notice``.
=========== =============================================================================== =============================
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>`_
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.
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 ;
More than one space around an assignment or other operator to align with
another:
More than one space around an assignment or other operator to align with another:
Yes::
@ -996,7 +998,7 @@ Contract and Library Names
* 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.
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::
@ -1132,8 +1134,8 @@ Solidity contracts can have a form of comments that are the basis of the
Ethereum Natural Language Specification Format.
Add comments above functions or contracts following `doxygen <http://www.doxygen.nl>`_ notation
of one or multiple lines starting with `///` or a
multiline comment starting with `/**` and ending with `*/`.
of one or multiple lines starting with ``///`` or a
multiline comment starting with ``/**`` and ending with ``*/``.
For example, the contract from `a simple smart contract <simple-smart-contract>`_ with the comments
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
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
tuple with a second `bool` value denoting success.
tuple with a second ``bool`` value denoting success.
.. include:: types/value-types.rst
@ -26,4 +26,4 @@ tuple with a second `bool` value denoting success.
.. 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
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
same contract type.

View File

@ -140,15 +140,19 @@ Error Handling
See the dedicated section on :ref:`assert and require<assert-and-require>` for
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.
``require(bool condition)``:
``require(bool condition)``
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.
``revert()``:
``revert()``
abort execution and revert state changes
``revert(string memory reason)``:
``revert(string memory reason)``
abort execution and revert state changes, providing an explanatory string
.. 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
----------------------------------------
``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.
``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.
``keccak256(bytes memory) returns (bytes32)``:
``keccak256(bytes memory) returns (bytes32)``
compute the Keccak-256 hash of the input
.. note::
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
``ripemd160(bytes memory) returns (bytes20)``:
``ripemd160(bytes memory) returns (bytes20)``
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.
The function parameters correspond to ECDSA values of the signature:
``r`` = first 32 bytes of signature
``s`` = second 32 bytes of signature
``v`` = final 1 byte of signature
* ``r`` = first 32 bytes of signature
* ``s`` = second 32 bytes of signature
* ``v`` = final 1 byte of signature
``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.
@ -209,17 +213,22 @@ Mathematical and Cryptographic Functions
Members of Address Types
------------------------
``<address>.balance`` (``uint256``):
``<address>.balance`` (``uint256``)
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
``<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
``<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
``<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
``<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
For more information, see the section on :ref:`address`.
@ -258,10 +267,10 @@ For more information, see the section on :ref:`address`.
Contract Related
----------------
``this`` (current contract's type):
``this`` (current contract's type)
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`
and end execution.
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
available for a contract type ``C``:
``type(C).name``:
``type(C).name``
The name of the contract.
``type(C).creationCode``:
``type(C).creationCode``
Memory byte array that contains the creation bytecode of the contract.
This can be used in inline assembly to build custom creation routines,
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
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.
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

View File

@ -106,7 +106,8 @@ Target options
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.
- ``homestead`` (oldest version)
- ``homestead``
- (oldest version)
- ``tangerineWhistle``
- 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.
@ -692,7 +693,7 @@ Review changes
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;

View File

@ -22,15 +22,17 @@
*/
#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/CommonSubexpressionEliminator.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 solidity;
using namespace solidity::evmasm;

View File

@ -28,6 +28,7 @@
#include <ostream>
#include <tuple>
#include <utility>
namespace solidity::evmasm
{
@ -119,7 +120,7 @@ public:
struct GasConsumption
{
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); }
GasConsumption& operator+=(GasConsumption const& _other);
@ -133,8 +134,8 @@ public:
};
/// Constructs a new gas meter given the current state.
GasMeter(std::shared_ptr<KnownState> const& _state, langutil::EVMVersion _evmVersion, u256 const& _largestMemoryAccess = 0):
m_state(_state), m_evmVersion(_evmVersion), m_largestMemoryAccess(_largestMemoryAccess) {}
GasMeter(std::shared_ptr<KnownState> _state, langutil::EVMVersion _evmVersion, u256 _largestMemoryAccess = 0):
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
/// the state.

View File

@ -23,6 +23,7 @@
#pragma once
#include <utility>
#include <vector>
#include <map>
#include <set>
@ -83,7 +84,7 @@ public:
explicit KnownState(
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 <string>
#include <tuple>
#include <utility>
namespace solidity::langutil
{
@ -68,8 +69,8 @@ class CharStream
{
public:
CharStream() = default;
explicit CharStream(std::string const& _source, std::string const& name):
m_source(_source), m_name(name) {}
explicit CharStream(std::string _source, std::string name):
m_source(std::move(_source)), m_name(std::move(name)) {}
int position() const { return m_position; }
bool isPastEndOfInput(size_t _charsForward = 0) const { return (m_position + _charsForward) >= m_source.size(); }

View File

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

View File

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

View File

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

View File

@ -24,6 +24,8 @@
#include <libsolidity/ast/ASTVisitor.h>
#include <utility>
namespace solidity::langutil
{
class ErrorReporter;
@ -47,7 +49,7 @@ public:
):
m_errorReporter(_errorReporter),
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))
m_currentNode->variableOccurrences.emplace_back(
*variableDeclaration,
static_cast<Expression const&>(_identifier).annotation().lValueRequested ?
static_cast<Expression const&>(_identifier).annotation().willBeWrittenTo ?
VariableOccurrence::Kind::Assignment :
VariableOccurrence::Kind::Access,
_identifier.location()

View File

@ -26,6 +26,7 @@
#include <map>
#include <memory>
#include <stack>
#include <utility>
#include <vector>
namespace solidity::frontend
@ -48,8 +49,8 @@ public:
Assignment,
InlineAssembly
};
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> const& _occurrence = {}):
m_declaration(_declaration), m_occurrenceKind(_kind), m_occurrence(_occurrence)
VariableOccurrence(VariableDeclaration const& _declaration, Kind _kind, std::optional<langutil::SourceLocation> _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())
return;
if (_expression.annotation().lValueRequested && _expression.annotation().lValueOfOrdinaryAssignment)
if (_expression.annotation().willBeWrittenTo && _expression.annotation().lValueOfOrdinaryAssignment)
{
if (!m_currentConstructor)
m_errorReporter.typeError(

View File

@ -195,51 +195,6 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
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()
{
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.
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.
void warnVariablesNamedLikeInstructions();

View File

@ -22,9 +22,7 @@
#include <libsolidity/analysis/ReferencesResolver.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
#include <libsolidity/analysis/ConstantEvaluator.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libyul/AsmAnalysis.h>
#include <libyul/AsmAnalysisInfo.h>
@ -37,7 +35,6 @@
#include <libsolutil/StringUtils.h>
#include <boost/algorithm/string.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
using namespace solidity::langutil;
@ -126,40 +123,10 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
else if (declarations.size() == 1)
_identifier.annotation().referencedDeclaration = declarations.front();
else
_identifier.annotation().overloadedDeclarations =
m_resolver.cleanedDeclarations(_identifier, declarations);
_identifier.annotation().candidateDeclarations = declarations;
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)
{
m_returnParameters.push_back(_functionDefinition.returnParameterList().get());
@ -194,113 +161,6 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
}
_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)
@ -321,115 +181,6 @@ bool ReferencesResolver::visit(Return const& _return)
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)
{
bool wasInsideFunction = m_yulInsideFunction;
@ -514,18 +265,6 @@ void ReferencesResolver::operator()(yul::VariableDeclaration const& _varDecl)
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)
{
m_errorOccurred = true;

View File

@ -76,29 +76,18 @@ private:
void endVisit(ForStatement const& _for) override;
void endVisit(VariableDeclarationStatement const& _varDeclStatement) override;
bool visit(Identifier const& _identifier) override;
bool visit(ElementaryTypeName const& _typeName) override;
bool visit(FunctionDefinition const& _functionDefinition) override;
void endVisit(FunctionDefinition const& _functionDefinition) override;
bool visit(ModifierDefinition const& _modifierDefinition) override;
void endVisit(ModifierDefinition const& _modifierDefinition) 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(Return const& _return) override;
void endVisit(VariableDeclaration const& _variable) override;
void operator()(yul::FunctionDefinition const& _function) override;
void operator()(yul::Identifier const& _identifier) 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.
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.");
}
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 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.");
}
switch (varType->category())
if (auto referenceType = dynamic_cast<ReferenceType const*>(varType))
{
case Type::Category::Array:
if (auto arrayType = dynamic_cast<ArrayType const*>(varType))
if (
((arrayType->location() == DataLocation::Memory) ||
(arrayType->location() == DataLocation::CallData)) &&
!arrayType->validForCalldata()
)
m_errorReporter.typeError(_variable.location(), "Array is too large to be encoded.");
break;
default:
break;
auto result = referenceType->validForLocation(referenceType->location());
if (result && _variable.isPublicCallableParameter())
result = referenceType->validForLocation(DataLocation::CallData);
if (!result)
{
solAssert(!result.message().empty(), "Expected detailed error message");
m_errorReporter.typeError(_variable.location(), result.message());
}
}
return false;
@ -633,7 +597,15 @@ void TypeChecker::endVisit(FunctionTypeName const& _funType)
{
FunctionType const& fun = dynamic_cast<FunctionType const&>(*_funType.annotation().type);
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.");
}
}
bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
@ -1305,7 +1277,7 @@ bool TypeChecker::visit(Conditional const& _conditional)
_conditional.trueExpression().annotation().isPure &&
_conditional.falseExpression().annotation().isPure;
if (_conditional.annotation().lValueRequested)
if (_conditional.annotation().willBeWrittenTo)
m_errorReporter.typeError(
_conditional.location(),
"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();
TypePointers types;
if (_tuple.annotation().lValueRequested)
if (_tuple.annotation().willBeWrittenTo)
{
if (_tuple.isInlineArray())
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])
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)
if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
{
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.");
}
components[i]->accept(*this);
types.push_back(type(*components[i]));
// 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())
if (types[i]->category() == Type::Category::Tuple)
if (dynamic_cast<TupleType const&>(*types[i]).components().empty())
{
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]);
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.");
}
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
types.push_back(TypePointer());
if (!components[i]->annotation().isPure)
isPure = false;
}
_tuple.annotation().isPure = isPure;
if (_tuple.isInlineArray())
@ -1802,6 +1770,10 @@ void TypeChecker::typeCheckReceiveFunction(FunctionDefinition const& _function)
void TypeChecker::typeCheckConstructor(FunctionDefinition const& _function)
{
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())
m_errorReporter.typeError(_function.returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
if (_function.stateMutability() != StateMutability::NonPayable && _function.stateMutability() != StateMutability::Payable)
@ -2782,11 +2754,57 @@ bool TypeChecker::visit(IndexRangeAccess const& _access)
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)
{
IdentifierAnnotation& annotation = _identifier.annotation();
if (!annotation.referencedDeclaration)
{
annotation.overloadedDeclarations = cleanOverloadedDeclarations(_identifier, annotation.candidateDeclarations);
if (!annotation.arguments)
{
// 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)
{
_expression.annotation().lValueRequested = true;
_expression.annotation().willBeWrittenTo = true;
_expression.annotation().lValueOfOrdinaryAssignment = _ordinaryAssignment;
_expression.accept(*this);

View File

@ -112,7 +112,6 @@ private:
void endVisit(InheritanceSpecifier const& _inheritance) override;
void endVisit(UsingForDirective const& _usingFor) override;
bool visit(StructDefinition const& _struct) override;
bool visit(FunctionDefinition const& _function) 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
@ -154,6 +153,11 @@ private:
/// @returns the referenced declaration and throws on error.
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
/// convertible to @a _expectedType.
bool expectType(Expression const& _expression, Type const& _expectedType);

View File

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

View File

@ -28,16 +28,18 @@
#include <libsolutil/Keccak256.h>
#include <boost/algorithm/string.hpp>
#include <algorithm>
#include <functional>
#include <utility>
using namespace std;
using namespace solidity;
using namespace solidity::frontend;
ASTNode::ASTNode(int64_t _id, SourceLocation const& _location):
ASTNode::ASTNode(int64_t _id, SourceLocation _location):
m_id(_id),
m_location(_location)
m_location(std::move(_location))
{
}
@ -255,12 +257,13 @@ TypeNameAnnotation& TypeName::annotation() const
TypePointer StructDefinition::type() const
{
solAssert(annotation().recursive.has_value(), "Requested struct type before DeclarationTypeChecker.");
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
@ -556,6 +559,18 @@ bool VariableDeclaration::isExternalCallableParameter() const
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
{
if (!isCallableOrCatchParameter())
@ -614,12 +629,20 @@ set<VariableDeclaration::Location> VariableDeclaration::allowedDataLocations() c
else if (isLocalVariable())
{
solAssert(typeName(), "");
solAssert(typeName()->annotation().type, "Can only be called after reference resolution");
if (typeName()->annotation().type->category() == Type::Category::Mapping)
return set<Location>{ Location::Storage };
else
// TODO: add Location::Calldata once implemented for local variables.
return set<Location>{ Location::Memory, Location::Storage };
auto dataLocations = [](TypePointer _type, auto&& _recursion) -> set<Location> {
solAssert(_type, "Can only be called after reference resolution");
switch (_type->category())
{
case Type::Category::Array:
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
// Struct members etc.
@ -756,3 +779,25 @@ string Literal::getChecksummedAddress() const
address.insert(address.begin(), 40 - address.size(), '0');
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 <optional>
#include <string>
#include <utility>
#include <vector>
namespace solidity::yul
@ -82,7 +83,7 @@ public:
using SourceLocation = langutil::SourceLocation;
explicit ASTNode(int64_t _id, SourceLocation const& _location);
explicit ASTNode(int64_t _id, SourceLocation _location);
virtual ~ASTNode() {}
/// @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
{
public:
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> const& _nodes):
ASTNode(_id, _location), m_nodes(_nodes) {}
SourceUnit(int64_t _id, SourceLocation const& _location, std::vector<ASTPointer<ASTNode>> _nodes):
ASTNode(_id, _location), m_nodes(std::move(_nodes)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -224,10 +225,10 @@ public:
Declaration(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<ASTString> _name,
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.
ASTString const& name() const { return *m_name; }
@ -275,9 +276,9 @@ public:
PragmaDirective(
int64_t _id,
SourceLocation const& _location,
std::vector<Token> const& _tokens,
std::vector<ASTString> const& _literals
): ASTNode(_id, _location), m_tokens(_tokens), m_literals(_literals)
std::vector<Token> _tokens,
std::vector<ASTString> _literals
): ASTNode(_id, _location), m_tokens(std::move(_tokens)), m_literals(std::move(_literals))
{}
void accept(ASTVisitor& _visitor) override;
@ -318,12 +319,12 @@ public:
ImportDirective(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _path,
ASTPointer<ASTString> _path,
ASTPointer<ASTString> const& _unitAlias,
SymbolAliasList _symbolAliases
):
Declaration(_id, _location, _unitAlias),
m_path(_path),
m_path(std::move(_path)),
m_symbolAliases(move(_symbolAliases))
{ }
@ -372,8 +373,8 @@ public:
StructuredDocumentation(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _text
): ASTNode(_id, _location), m_text(_text)
ASTPointer<ASTString> _text
): ASTNode(_id, _location), m_text(std::move(_text))
{}
void accept(ASTVisitor& _visitor) override;
@ -394,7 +395,7 @@ class Documented
{
public:
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.
/// Can contain a nullptr in which case indicates absence of documentation
@ -411,7 +412,7 @@ class StructurallyDocumented
{
public:
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.
/// Can contain a nullptr in which case indicates absence of documentation
@ -453,15 +454,15 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
ASTPointer<StructuredDocumentation> const& _documentation,
std::vector<ASTPointer<InheritanceSpecifier>> const& _baseContracts,
std::vector<ASTPointer<ASTNode>> const& _subNodes,
std::vector<ASTPointer<InheritanceSpecifier>> _baseContracts,
std::vector<ASTPointer<ASTNode>> _subNodes,
ContractKind _contractKind = ContractKind::Contract,
bool _abstract = false
):
Declaration(_id, _location, _name),
StructurallyDocumented(_documentation),
m_baseContracts(_baseContracts),
m_subNodes(_subNodes),
m_baseContracts(std::move(_baseContracts)),
m_subNodes(std::move(_subNodes)),
m_contractKind(_contractKind),
m_abstract(_abstract)
{}
@ -538,10 +539,10 @@ public:
InheritanceSpecifier(
int64_t _id,
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _baseName,
ASTPointer<UserDefinedTypeName> _baseName,
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(ASTConstVisitor& _visitor) const override;
@ -568,10 +569,10 @@ public:
UsingForDirective(
int64_t _id,
SourceLocation const& _location,
ASTPointer<UserDefinedTypeName> const& _libraryName,
ASTPointer<TypeName> const& _typeName
ASTPointer<UserDefinedTypeName> _libraryName,
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(ASTConstVisitor& _visitor) const override;
@ -592,9 +593,9 @@ public:
int64_t _id,
SourceLocation const& _location,
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(ASTConstVisitor& _visitor) const override;
@ -606,7 +607,7 @@ public:
bool isVisibleInDerivedContracts() const override { return true; }
bool isVisibleViaContractTypeAccess() const override { return true; }
TypeDeclarationAnnotation& annotation() const override;
StructDeclarationAnnotation& annotation() const override;
private:
std::vector<ASTPointer<VariableDeclaration>> m_members;
@ -619,9 +620,9 @@ public:
int64_t _id,
SourceLocation const& _location,
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(ASTConstVisitor& _visitor) const override;
@ -664,9 +665,9 @@ public:
ParameterList(
int64_t _id,
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(ASTConstVisitor& _visitor) const override;
@ -688,15 +689,15 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _name,
Visibility _visibility,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<ParameterList> _parameters,
bool _isVirtual = false,
ASTPointer<OverrideSpecifier> const& _overrides = nullptr,
ASTPointer<ParameterList> const& _returnParameters = ASTPointer<ParameterList>()
ASTPointer<OverrideSpecifier> _overrides = nullptr,
ASTPointer<ParameterList> _returnParameters = ASTPointer<ParameterList>()
):
Declaration(_id, _location, _name, _visibility),
m_parameters(_parameters),
m_overrides(_overrides),
m_returnParameters(_returnParameters),
m_parameters(std::move(_parameters)),
m_overrides(std::move(_overrides)),
m_returnParameters(std::move(_returnParameters)),
m_isVirtual(_isVirtual)
{
}
@ -740,10 +741,10 @@ public:
OverrideSpecifier(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<UserDefinedTypeName>> const& _overrides
std::vector<ASTPointer<UserDefinedTypeName>> _overrides
):
ASTNode(_id, _location),
m_overrides(_overrides)
m_overrides(std::move(_overrides))
{
}
@ -771,7 +772,7 @@ public:
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<StructuredDocumentation> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
std::vector<ASTPointer<ModifierInvocation>> _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body
):
@ -780,7 +781,7 @@ public:
ImplementationOptional(_body != nullptr),
m_stateMutability(_stateMutability),
m_kind(_kind),
m_functionModifiers(_modifiers),
m_functionModifiers(std::move(_modifiers)),
m_body(_body)
{
solAssert(_kind == Token::Constructor || _kind == Token::Function || _kind == Token::Fallback || _kind == Token::Receive, "");
@ -854,28 +855,38 @@ class VariableDeclaration: public Declaration
{
public:
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(
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _type,
ASTPointer<TypeName> _type,
ASTPointer<ASTString> const& _name,
ASTPointer<Expression> _value,
Visibility _visibility,
bool _isStateVar = false,
bool _isIndexed = false,
Constantness _constantness = Constantness::Mutable,
ASTPointer<OverrideSpecifier> const& _overrides = nullptr,
Mutability _mutability = Mutability::Mutable,
ASTPointer<OverrideSpecifier> _overrides = nullptr,
Location _referenceLocation = Location::Unspecified
):
Declaration(_id, _location, _name, _visibility),
m_typeName(_type),
m_value(_value),
m_typeName(std::move(_type)),
m_value(std::move(_value)),
m_isStateVariable(_isStateVar),
m_isIndexed(_isIndexed),
m_constantness(_constantness),
m_overrides(_overrides),
m_mutability(_mutability),
m_overrides(std::move(_overrides)),
m_location(_referenceLocation) {}
@ -903,6 +914,8 @@ public:
/// @returns true if this variable is a parameter (not return parameter) of an external function.
/// This excludes parameters of external function type names.
bool isExternalCallableParameter() const;
/// @returns true if this variable is a parameter (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
/// or a function type of internal visibility.
bool isInternalCallableParameter() const;
@ -918,8 +931,9 @@ public:
bool hasReferenceOrMappingType() const;
bool isStateVariable() const { return m_isStateVariable; }
bool isIndexed() const { return m_isIndexed; }
bool isConstant() const { return m_constantness == Constantness::Constant; }
bool immutable() const { return m_constantness == Constantness::Immutable; }
Mutability mutability() const { return m_mutability; }
bool isConstant() const { return m_mutability == Mutability::Constant; }
bool immutable() const { return m_mutability == Mutability::Immutable; }
ASTPointer<OverrideSpecifier> const& overrides() const { return m_overrides; }
Location referenceLocation() const { return m_location; }
/// @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_isIndexed = false; ///< Whether this is an indexed variable (used by events).
/// 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
Location m_location = Location::Unspecified; ///< Location of the variable if it is of reference type.
};
@ -966,11 +980,11 @@ public:
ASTPointer<ParameterList> const& _parameters,
bool _isVirtual,
ASTPointer<OverrideSpecifier> const& _overrides,
ASTPointer<Block> const& _body
ASTPointer<Block> _body
):
CallableDeclaration(_id, _location, _name, Visibility::Internal, _parameters, _isVirtual, _overrides),
StructurallyDocumented(_documentation),
m_body(_body)
m_body(std::move(_body))
{
}
@ -1004,10 +1018,10 @@ public:
ModifierInvocation(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Identifier> const& _name,
ASTPointer<Identifier> _name,
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(ASTConstVisitor& _visitor) const override;
@ -1148,8 +1162,8 @@ private:
class UserDefinedTypeName: public TypeName
{
public:
UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> const& _namePath):
TypeName(_id, _location), m_namePath(_namePath) {}
UserDefinedTypeName(int64_t _id, SourceLocation const& _location, std::vector<ASTString> _namePath):
TypeName(_id, _location), m_namePath(std::move(_namePath)) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1170,12 +1184,12 @@ public:
FunctionTypeName(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ParameterList> const& _parameterTypes,
ASTPointer<ParameterList> const& _returnTypes,
ASTPointer<ParameterList> _parameterTypes,
ASTPointer<ParameterList> _returnTypes,
Visibility _visibility,
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)
{}
void accept(ASTVisitor& _visitor) override;
@ -1209,10 +1223,10 @@ public:
Mapping(
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _keyType,
ASTPointer<TypeName> const& _valueType
ASTPointer<TypeName> _keyType,
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(ASTConstVisitor& _visitor) const override;
@ -1233,10 +1247,10 @@ public:
ArrayTypeName(
int64_t _id,
SourceLocation const& _location,
ASTPointer<TypeName> const& _baseType,
ASTPointer<Expression> const& _length
ASTPointer<TypeName> _baseType,
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(ASTConstVisitor& _visitor) const override;
@ -1280,9 +1294,9 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
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(ASTConstVisitor& _visitor) const override;
@ -1306,9 +1320,9 @@ public:
int64_t _id,
SourceLocation const& _location,
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(ASTConstVisitor& _visitor) const override;
@ -1348,14 +1362,14 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _trueBody,
ASTPointer<Statement> const& _falseBody
ASTPointer<Expression> _condition,
ASTPointer<Statement> _trueBody,
ASTPointer<Statement> _falseBody
):
Statement(_id, _location, _docString),
m_condition(_condition),
m_trueBody(_trueBody),
m_falseBody(_falseBody)
m_condition(std::move(_condition)),
m_trueBody(std::move(_trueBody)),
m_falseBody(std::move(_falseBody))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1382,14 +1396,14 @@ public:
TryCatchClause(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _errorName,
ASTPointer<ParameterList> const& _parameters,
ASTPointer<Block> const& _block
ASTPointer<ASTString> _errorName,
ASTPointer<ParameterList> _parameters,
ASTPointer<Block> _block
):
ASTNode(_id, _location),
m_errorName(_errorName),
m_parameters(_parameters),
m_block(_block)
m_errorName(std::move(_errorName)),
m_parameters(std::move(_parameters)),
m_block(std::move(_block))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1427,12 +1441,12 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _externalCall,
std::vector<ASTPointer<TryCatchClause>> const& _clauses
ASTPointer<Expression> _externalCall,
std::vector<ASTPointer<TryCatchClause>> _clauses
):
Statement(_id, _location, _docString),
m_externalCall(_externalCall),
m_clauses(_clauses)
m_externalCall(std::move(_externalCall)),
m_clauses(std::move(_clauses))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1440,6 +1454,10 @@ public:
Expression const& externalCall() const { return *m_externalCall; }
std::vector<ASTPointer<TryCatchClause>> const& clauses() const { return m_clauses; }
TryCatchClause const* successClause() const;
TryCatchClause const* structuredClause() const;
TryCatchClause const* fallbackClause() const;
private:
ASTPointer<Expression> m_externalCall;
std::vector<ASTPointer<TryCatchClause>> m_clauses;
@ -1465,11 +1483,11 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Expression> const& _condition,
ASTPointer<Statement> const& _body,
ASTPointer<Expression> _condition,
ASTPointer<Statement> _body,
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) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1494,16 +1512,16 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
ASTPointer<Statement> const& _initExpression,
ASTPointer<Expression> const& _conditionExpression,
ASTPointer<ExpressionStatement> const& _loopExpression,
ASTPointer<Statement> const& _body
ASTPointer<Statement> _initExpression,
ASTPointer<Expression> _conditionExpression,
ASTPointer<ExpressionStatement> _loopExpression,
ASTPointer<Statement> _body
):
BreakableStatement(_id, _location, _docString),
m_initExpression(_initExpression),
m_condExpression(_conditionExpression),
m_loopExpression(_loopExpression),
m_body(_body)
m_initExpression(std::move(_initExpression)),
m_condExpression(std::move(_conditionExpression)),
m_loopExpression(std::move(_loopExpression)),
m_body(std::move(_body))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1552,7 +1570,7 @@ public:
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
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(ASTConstVisitor& _visitor) const override;
@ -1586,9 +1604,9 @@ public:
int64_t _id,
SourceLocation const& _location,
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(ASTConstVisitor& _visitor) const override;
@ -1613,10 +1631,10 @@ public:
int64_t _id,
SourceLocation const& _location,
ASTPointer<ASTString> const& _docString,
std::vector<ASTPointer<VariableDeclaration>> const& _variables,
ASTPointer<Expression> const& _initialValue
std::vector<ASTPointer<VariableDeclaration>> _variables,
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(ASTConstVisitor& _visitor) const override;
@ -1645,7 +1663,7 @@ public:
ASTPointer<ASTString> const& _docString,
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(ASTConstVisitor& _visitor) const override;
@ -1679,14 +1697,14 @@ public:
Conditional(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _condition,
ASTPointer<Expression> const& _trueExpression,
ASTPointer<Expression> const& _falseExpression
ASTPointer<Expression> _condition,
ASTPointer<Expression> _trueExpression,
ASTPointer<Expression> _falseExpression
):
Expression(_id, _location),
m_condition(_condition),
m_trueExpression(_trueExpression),
m_falseExpression(_falseExpression)
m_condition(std::move(_condition)),
m_trueExpression(std::move(_trueExpression)),
m_falseExpression(std::move(_falseExpression))
{}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1709,14 +1727,14 @@ public:
Assignment(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _leftHandSide,
ASTPointer<Expression> _leftHandSide,
Token _assignmentOperator,
ASTPointer<Expression> const& _rightHandSide
ASTPointer<Expression> _rightHandSide
):
Expression(_id, _location),
m_leftHandSide(_leftHandSide),
m_leftHandSide(std::move(_leftHandSide)),
m_assigmentOperator(_assignmentOperator),
m_rightHandSide(_rightHandSide)
m_rightHandSide(std::move(_rightHandSide))
{
solAssert(TokenTraits::isAssignmentOp(_assignmentOperator), "");
}
@ -1747,11 +1765,11 @@ public:
TupleExpression(
int64_t _id,
SourceLocation const& _location,
std::vector<ASTPointer<Expression>> const& _components,
std::vector<ASTPointer<Expression>> _components,
bool _isArray
):
Expression(_id, _location),
m_components(_components),
m_components(std::move(_components)),
m_isArray(_isArray) {}
void accept(ASTVisitor& _visitor) override;
void accept(ASTConstVisitor& _visitor) const override;
@ -1775,12 +1793,12 @@ public:
int64_t _id,
SourceLocation const& _location,
Token _operator,
ASTPointer<Expression> const& _subExpression,
ASTPointer<Expression> _subExpression,
bool _isPrefix
):
Expression(_id, _location),
m_operator(_operator),
m_subExpression(_subExpression),
m_subExpression(std::move(_subExpression)),
m_isPrefix(_isPrefix)
{
solAssert(TokenTraits::isUnaryOp(_operator), "");
@ -1808,11 +1826,11 @@ public:
BinaryOperation(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _left,
ASTPointer<Expression> _left,
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), "");
}
@ -1840,11 +1858,11 @@ public:
FunctionCall(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _arguments,
std::vector<ASTPointer<ASTString>> const& _names
ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> _arguments,
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(ASTConstVisitor& _visitor) const override;
@ -1870,11 +1888,11 @@ public:
FunctionCallOptions(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _expression,
std::vector<ASTPointer<Expression>> const& _options,
std::vector<ASTPointer<ASTString>> const& _names
ASTPointer<Expression> _expression,
std::vector<ASTPointer<Expression>> _options,
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(ASTConstVisitor& _visitor) const override;
@ -1899,9 +1917,9 @@ public:
NewExpression(
int64_t _id,
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(ASTConstVisitor& _visitor) const override;
@ -1921,9 +1939,9 @@ public:
int64_t _id,
SourceLocation const& _location,
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(ASTConstVisitor& _visitor) const override;
Expression const& expression() const { return *m_expression; }
@ -1945,10 +1963,10 @@ public:
IndexAccess(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _base,
ASTPointer<Expression> const& _index
ASTPointer<Expression> _base,
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(ASTConstVisitor& _visitor) const override;
@ -1969,11 +1987,11 @@ public:
IndexRangeAccess(
int64_t _id,
SourceLocation const& _location,
ASTPointer<Expression> const& _base,
ASTPointer<Expression> const& _start,
ASTPointer<Expression> const& _end
ASTPointer<Expression> _base,
ASTPointer<Expression> _start,
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(ASTConstVisitor& _visitor) const override;
@ -2006,9 +2024,9 @@ public:
Identifier(
int64_t _id,
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(ASTConstVisitor& _visitor) const override;
@ -2031,10 +2049,10 @@ public:
ElementaryTypeNameExpression(
int64_t _id,
SourceLocation const& _location,
ASTPointer<ElementaryTypeName> const& _type
ASTPointer<ElementaryTypeName> _type
):
PrimaryExpression(_id, _location),
m_type(_type)
m_type(std::move(_type))
{
}
void accept(ASTVisitor& _visitor) override;
@ -2070,10 +2088,10 @@ public:
int64_t _id,
SourceLocation const& _location,
Token _token,
ASTPointer<ASTString> const& _value,
ASTPointer<ASTString> _value,
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(ASTConstVisitor& _visitor) const override;

View File

@ -128,6 +128,16 @@ struct TypeDeclarationAnnotation: DeclarationAnnotation
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
{
/// 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).
bool isLValue = false;
/// 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.
/// Would be false for --, ++, delete, +=, -=, ....
bool lValueOfOrdinaryAssignment = false;
@ -248,6 +258,8 @@ struct IdentifierAnnotation: ExpressionAnnotation
{
/// Referenced declaration, set at latest during overload resolution stage.
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.
std::vector<Declaration const*> overloadedDeclarations;
};

View File

@ -34,6 +34,7 @@
#include <boost/algorithm/string/join.hpp>
#include <boost/range/algorithm/sort.hpp>
#include <utility>
#include <vector>
#include <algorithm>
@ -45,7 +46,7 @@ namespace solidity::frontend
ASTJsonConverter::ASTJsonConverter(bool _legacy, map<string, unsigned> _sourceIndices):
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("isPure", _annotation.isPure),
make_pair("isLValue", _annotation.isLValue),
make_pair("lValueRequested", _annotation.lValueRequested),
make_pair("lValueRequested", _annotation.willBeWrittenTo),
make_pair("argumentTypes", typePointerToJson(_annotation.arguments))
};
_attributes += exprAttributes;
@ -378,6 +379,7 @@ bool ASTJsonConverter::visit(VariableDeclaration const& _node)
make_pair("name", _node.name()),
make_pair("typeName", toJsonOrNull(_node.typeName())),
make_pair("constant", _node.isConstant()),
make_pair("mutability", VariableDeclaration::mutabilityToString(_node.mutability())),
make_pair("stateVariable", _node.isStateVariable()),
make_pair("storageLocation", location(_node.referenceLocation())),
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!");
VariableDeclaration::Constantness constantness{};
if (memberAsBool(_node, "constant"))
constantness = VariableDeclaration::Constantness::Constant;
VariableDeclaration::Mutability mutability{};
astAssert(member(_node, "mutability").isString(), "'mutability' expected to be string.");
string const mutabilityStr = member(_node, "mutability").asString();
if (mutabilityStr == "constant")
{
mutability = VariableDeclaration::Mutability::Constant;
astAssert(memberAsBool(_node, "constant"), "");
}
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>(
_node,
@ -425,7 +438,7 @@ ASTPointer<VariableDeclaration> ASTJsonImporter::createVariableDeclaration(Json:
visibility(_node),
memberAsBool(_node, "stateVariable"),
_node.isMember("indexed") ? memberAsBool(_node, "indexed") : false,
constantness,
mutability,
_node["overrides"].isNull() ? nullptr : createOverrideSpecifier(member(_node, "overrides")),
location(_node)
);
@ -495,7 +508,7 @@ ASTPointer<UserDefinedTypeName> ASTJsonImporter::createUserDefinedTypeName(Json:
string nameString = member(_node, "name").asString();
boost::algorithm::split(strs, nameString, boost::is_any_of("."));
for (string s: strs)
namePath.push_back(ASTString(s));
namePath.emplace_back(s);
return createASTNode<UserDefinedTypeName>(
_node,
namePath

View File

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

View File

@ -43,6 +43,7 @@
#include <boost/range/algorithm/copy.hpp>
#include <limits>
#include <utility>
using namespace std;
using namespace solidity;
@ -1253,8 +1254,8 @@ StringLiteralType::StringLiteralType(Literal const& _literal):
{
}
StringLiteralType::StringLiteralType(string const& _value):
m_value{_value}
StringLiteralType::StringLiteralType(string _value):
m_value{std::move(_value)}
{
}
@ -1648,12 +1649,50 @@ bool ArrayType::operator==(Type const& _other) const
return isDynamicallySized() || length() == other.length();
}
bool ArrayType::validForCalldata() const
BoolResult ArrayType::validForLocation(DataLocation _loc) const
{
if (auto arrayBaseType = dynamic_cast<ArrayType const*>(baseType()))
if (!arrayBaseType->validForCalldata())
return false;
return isDynamicallySized() || unlimitedStaticCalldataSize(true) <= numeric_limits<unsigned>::max();
{
BoolResult result = arrayBaseType->validForLocation(_loc);
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
@ -2174,93 +2213,119 @@ MemberList::MemberMap StructType::nativeMembers(ContractDefinition const*) const
TypeResult StructType::interfaceType(bool _inLibrary) const
{
if (_inLibrary && m_interfaceType_library.has_value())
return *m_interfaceType_library;
if (!_inLibrary && m_interfaceType.has_value())
if (!_inLibrary)
{
if (!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;
}
else if (m_interfaceType_library.has_value())
return *m_interfaceType_library;
TypeResult result{TypePointer{}};
m_recursive = false;
auto visitor = [&](
StructDefinition const& _struct,
util::CycleDetector<StructDefinition>& _cycleDetector,
size_t /*_depth*/
)
{
// 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())
)
util::BreadthFirstSearch<StructDefinition const*> breadthFirstSearch{{&m_struct}};
breadthFirstSearch.run(
[&](StructDefinition const* _struct, auto&& _addChild) {
// 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())
{
m_recursive = true;
if (_inLibrary && location() == DataLocation::Storage)
continue;
else
// 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("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;
}
}
auto iType = memberType->interfaceType(_inLibrary);
if (!iType.get())
{
solAssert(!iType.message().empty(), "Expected detailed error message!");
result = iType;
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->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 (_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;
if (location() == DataLocation::Storage)
m_interfaceType_library = this;
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
@ -2643,21 +2708,11 @@ FunctionType::FunctionType(FunctionTypeName const& _typeName):
for (auto const& t: _typeName.parameterTypes())
{
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);
}
for (auto const& t: _typeName.returnParameterTypes())
{
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);
}
@ -3112,6 +3167,8 @@ MemberList::MemberMap FunctionType::nativeMembers(ContractDefinition const* _sco
TypePointer FunctionType::encodingType() const
{
if (m_gasSet || m_valueSet)
return nullptr;
// Only external functions can be encoded, internal functions cannot leave code boundaries.
if (m_kind == Kind::External)
return this;

View File

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

View File

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

View File

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

View File

@ -949,43 +949,7 @@ void ContractCompiler::handleCatch(vector<ASTPointer<TryCatchClause>> const& _ca
// Try to decode the error message.
// If this fails, leaves 0 on the stack, otherwise the pointer to the data string.
m_context << u256(0);
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.callYulFunction(m_context.utilFunctions().tryDecodeErrorMessageFunction(), 0, 1);
m_context << Instruction::DUP1;
AssemblyItem decodeSuccessTag = m_context.appendConditionalJump();
m_context << Instruction::POP;

View File

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

View File

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

View File

@ -41,10 +41,15 @@ public:
std::string createFunction(std::string const& _name, std::function<std::string()> const& _creator);
/// @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
/// empty return value.
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:
/// Map from function name to code for a multi-use function.
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 +=
suffixedVariableNameList("converted", destStackSize, destStackSize + toComponent->sizeOnStack()) +
" := " +
(toComponent->sizeOnStack() > 0 ? " := " : "") +
conversionFunction(*fromComponent, *toComponent) +
"(" +
suffixedVariableNameList("value", sourceStackSize, sourceStackSize + fromComponent->sizeOnStack()) +
@ -2089,12 +2089,13 @@ string YulUtilFunctions::conversionFunctionSpecial(Type const& _from, Type const
sourceStackSize += fromComponent->sizeOnStack();
}
return Whiskers(R"(
function <functionName>(<values>) -> <converted> {
function <functionName>(<values>) <arrow> <converted> {
<conversions>
}
)")
("functionName", functionName)
("values", suffixedVariableNameList("value", 0, sourceStackSize))
("arrow", destStackSize > 0 ? "->" : "")
("converted", suffixedVariableNameList("converted", 0, destStackSize))
("conversions", conversions)
.render();
@ -2252,3 +2253,84 @@ string YulUtilFunctions::revertReasonIfDebug(string const& _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 <libsolidity/ast/Types.h>
#include <libsolidity/codegen/MultiUseYulFunctionCollector.h>
#include <libsolidity/interface/DebugSettings.h>
@ -322,6 +323,20 @@ public:
static std::string revertReasonIfDebug(RevertStrings revertStrings, 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:
/// Special case of conversionFunction - handles everything that does not
/// use exactly one variable to hold the value.

View File

@ -32,6 +32,25 @@ using namespace solidity;
using namespace solidity::util;
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
{
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());
}
string IRGenerationContext::virtualFunctionName(FunctionDefinition const& _functionDeclaration)
{
return functionName(_functionDeclaration.resolveVirtual(mostDerivedContract()));
}
string IRGenerationContext::newYulVariable()
{
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 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()) },
{ "name", functionName(*function)}
});
enqueueFunctionForCodeGeneration(*function);
}
templ("cases", move(functions));
return templ.render();
@ -141,3 +168,4 @@ std::string IRGenerationContext::revertReasonIfDebug(std::string const& _message
{
return YulUtilFunctions::revertReasonIfDebug(m_revertStrings, _message);
}

View File

@ -30,6 +30,7 @@
#include <libsolutil/Common.h>
#include <set>
#include <string>
#include <memory>
#include <vector>
@ -61,6 +62,15 @@ public:
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)>
void setMostDerivedContract(ContractDefinition const& _mostDerivedContract)
{
@ -82,7 +92,6 @@ public:
std::string functionName(FunctionDefinition const& _function);
std::string functionName(VariableDeclaration const& _varDecl);
std::string virtualFunctionName(FunctionDefinition const& _functionDeclaration);
std::string newYulVariable();
@ -99,6 +108,10 @@ public:
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:
langutil::EVMVersion m_evmVersion;
RevertStrings m_revertStrings;
@ -109,6 +122,15 @@ private:
std::map<VariableDeclaration const*, std::pair<u256, unsigned>> m_stateVariables;
MultiUseYulFunctionCollector m_functions;
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("constructor", constructorCode(_contract));
t("deploy", deployCode(_contract));
// We generate code for all functions and rely on the optimizer to remove them again
// 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);
generateQueuedFunctions();
t("functions", m_context.functionCollector().requestedFunctions());
resetContext(_contract);
m_context.setMostDerivedContract(_contract);
t("RuntimeObject", runtimeObjectName(_contract));
t("dispatch", dispatchRoutine(_contract));
for (auto const* contract: _contract.annotation().linearizedBaseContracts)
for (auto const* fun: contract->definedFunctions())
generateFunction(*fun);
generateQueuedFunctions();
t("runtimeFunctions", m_context.functionCollector().requestedFunctions());
return t.render();
}
@ -127,6 +119,13 @@ string IRGenerator::generate(Block const& _block)
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 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("params", suffixedVariableNameList("param_", 0, paramVars));
t("abiDecode", abiFunctions.tupleDecoder(constructor->functionType(false)->parameterTypes(), true));
t("constructorName", m_context.functionName(*constructor));
t("constructorName", m_context.enqueueFunctionForCodeGeneration(*constructor));
out << t.render();
}
@ -352,7 +351,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
vector<map<string, string>> functions;
for (auto const& function: _contract.interfaceFunctions())
{
functions.push_back({});
functions.emplace_back();
map<string, string>& templ = functions.back();
templ["functionSelector"] = "0x" + function.first.hex();
FunctionTypePointer const& type = function.second;
@ -370,7 +369,7 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
templ["retParams"] = suffixedVariableNameList("ret_", retVars, 0);
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()))
templ["function"] = generateGetter(*varDecl);
else
@ -386,14 +385,14 @@ string IRGenerator::dispatchRoutine(ContractDefinition const& _contract)
string fallbackCode;
if (!fallback->isPayable())
fallbackCode += callValueCheck();
fallbackCode += generateFunction(*fallback) + "() stop()";
fallbackCode += m_context.enqueueFunctionForCodeGeneration(*fallback) + "() stop()";
t("fallback", fallbackCode);
}
else
t("fallback", "revert(0, 0)");
if (FunctionDefinition const* etherReceiver = _contract.receiveFunction())
t("receiveEther", generateFunction(*etherReceiver) + "() stop()");
t("receiveEther", m_context.enqueueFunctionForCodeGeneration(*etherReceiver) + "() stop()");
else
t("receiveEther", "");
return t.render();
@ -413,6 +412,10 @@ string IRGenerator::memoryInit()
void IRGenerator::resetContext(ContractDefinition const& _contract)
{
solAssert(
m_context.functionGenerationQueueEmpty(),
"Reset function generation queue while it still had functions."
);
solAssert(
m_context.functionCollector().requestedFunctions().empty(),
"Reset context while it still had functions."

View File

@ -56,6 +56,9 @@ private:
std::string generate(ContractDefinition const& _contract);
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.
std::string generateFunction(FunctionDefinition const& _function);
/// 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/ABIFunctions.h>
#include <libsolidity/codegen/CompilerUtils.h>
#include <libsolidity/codegen/ReturnInfo.h>
#include <libsolidity/ast/TypeProvider.h>
#include <libevmasm/GasMeter.h>
@ -41,6 +42,7 @@
#include <libsolutil/Keccak256.h>
#include <libsolutil/Visitor.h>
#include <boost/range/adaptor/reversed.hpp>
#include <boost/range/adaptor/transformed.hpp>
using namespace std;
@ -255,14 +257,14 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
solUnimplementedAssert(false, "");
else
{
bool lValueRequested = _tuple.annotation().lValueRequested;
if (lValueRequested)
bool willBeWrittenTo = _tuple.annotation().willBeWrittenTo;
if (willBeWrittenTo)
solAssert(!m_currentLValue, "");
if (_tuple.components().size() == 1)
{
solAssert(_tuple.components().front(), "");
_tuple.components().front()->accept(*this);
if (lValueRequested)
if (willBeWrittenTo)
solAssert(!!m_currentLValue, "");
else
define(_tuple, *_tuple.components().front());
@ -274,7 +276,7 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
if (auto const& component = _tuple.components()[i])
{
component->accept(*this);
if (lValueRequested)
if (willBeWrittenTo)
{
solAssert(!!m_currentLValue, "");
lvalues.emplace_back(std::move(m_currentLValue));
@ -283,10 +285,10 @@ bool IRGeneratorForStatements::visit(TupleExpression const& _tuple)
else
define(IRVariable(_tuple).tupleComponent(i), *component);
}
else if (lValueRequested)
else if (willBeWrittenTo)
lvalues.emplace_back();
if (_tuple.annotation().lValueRequested)
if (_tuple.annotation().willBeWrittenTo)
m_currentLValue.emplace(IRLValue{
*_tuple.annotation().type,
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))
{
define(_functionCall) <<
m_context.virtualFunctionName(*functionDef) <<
m_context.enqueueFunctionForCodeGeneration(
functionDef->resolveVirtual(m_context.mostDerivedContract())
) <<
"(" <<
joinHumanReadable(args) <<
")\n";
@ -584,6 +588,7 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall)
}
define(_functionCall) <<
// NOTE: internalDispatch() takes care of adding the function to function generation queue
m_context.internalDispatch(
TupleType(functionType->parameterTypes()).sizeOnStack(),
TupleType(functionType->returnParameterTypes()).sizeOnStack()
@ -840,11 +845,18 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
case Type::Category::Function:
if (member == "selector")
{
solUnimplementedAssert(
dynamic_cast<FunctionType const&>(*_memberAccess.expression().annotation().type).kind() ==
FunctionType::Kind::External, ""
FunctionType const& functionType = dynamic_cast<FunctionType const&>(
*_memberAccess.expression().annotation().type
);
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")
{
@ -971,6 +983,78 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess)
solAssert(false, "Illegal fixed bytes member.");
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:
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))
define(_identifier) << to_string(functionDef->resolveVirtual(m_context.mostDerivedContract()).id()) << "\n";
else if (VariableDeclaration const* varDecl = dynamic_cast<VariableDeclaration const*>(declaration))
{
// 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.");
}
handleVariableReference(*varDecl, _identifier);
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
{
solUnimplementedAssert(!contract->isLibrary(), "Libraries not yet supported.");
@ -1249,6 +1312,33 @@ bool IRGeneratorForStatements::visit(Literal const& _literal)
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(
FunctionCall const& _functionCall,
vector<ASTPointer<Expression const>> const& _arguments
@ -1260,39 +1350,15 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
_arguments.size() == funType.parameterTypes().size(), ""
);
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::BareCallCode, "Callcode has been removed.");
bool returnSuccessConditionAndReturndata = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::BareStaticCall;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
bool useStaticCall = funKind == FunctionType::Kind::BareStaticCall || (funType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall());
bool const 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 haveReturndatacopy = m_context.evmVersion().supportsReturndata();
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();
}
ReturnInfo const returnInfo{m_context.evmVersion(), funType};
TypePointers argumentTypes;
vector<string> argumentStrings;
@ -1311,8 +1377,8 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
// (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
// would remove that, so we use MSTORE here.
if (!funType.gasSet() && estimatedReturnSize > 0)
m_code << "mstore(add(" << freeMemory() << ", " << to_string(estimatedReturnSize) << "), 0)\n";
if (!funType.gasSet() && returnInfo.estimatedReturnSize > 0)
m_code << "mstore(add(" << freeMemory() << ", " << to_string(returnInfo.estimatedReturnSize) << "), 0)\n";
}
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) }
</checkExistence>
// storage for arguments and returned data
let <pos> := <freeMemory>
mstore(<pos>, <shl28>(<funId>))
let <end> := <encodeArgs>(add(<pos>, 4) <argumentString>)
let <result> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
if iszero(<result>) { <forwardingRevert>() }
let <success> := <call>(<gas>, <address>, <?hasValue> <value>, </hasValue> <pos>, sub(<end>, <pos>), <pos>, <reservedReturnSize>)
<?noTryCall>
if iszero(<success>) { <forwardingRevert>() }
</noTryCall>
<?hasRetVars> let <retVars> </hasRetVars>
if <success> {
<?dynamicReturnSize>
// copy dynamic return data out
returndatacopy(<pos>, 0, returndatasize())
</dynamicReturnSize>
<?dynamicReturnSize>
returndatacopy(<pos>, 0, returndatasize())
</dynamicReturnSize>
// update freeMemoryPointer according to dynamic return size
mstore(<freeMemoryPointer>, add(<pos>, <roundUp>(<returnSize>)))
mstore(<freeMemoryPointer>, add(<pos>, and(add(<returnSize>, 0x1f), not(0x1f))))
<?returns> let <retVars> := </returns> <abiDecode>(<pos>, add(<pos>, <returnSize>))
// decode return parameters from external try-call into retVars
<?hasRetVars> <retVars> := </hasRetVars> <abiDecode>(<pos>, add(<pos>, <returnSize>))
}
)");
templ("pos", 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("freeMemoryPointer", to_string(CompilerUtils::freeMemoryPointer));
templ("shl28", m_utils.shiftLeftFunction(8 * (32 - 4)));
templ("funId", IRVariable(_functionCall.expression()).part("functionIdentifier").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.
// Move arguments to memory, will not update the free memory pointer (but will update the memory
// pointer on the stack).
@ -1401,24 +1498,9 @@ void IRGeneratorForStatements::appendExternalFunctionCall(
templ("forwardingRevert", m_utils.forwardingRevertFunction());
solUnimplementedAssert(!returnSuccessConditionAndReturndata, "");
solUnimplementedAssert(funKind != FunctionType::Kind::RIPEMD160, "");
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();
}
@ -1471,14 +1553,17 @@ void IRGeneratorForStatements::declareAssign(IRVariable const& _lhs, IRVariable
else
m_code << (_declare ? "let ": "") << _lhs.part(stackItemName).name() << " := " << _rhs.part(stackItemName).name() << "\n";
else
m_code <<
(_declare ? "let ": "") <<
_lhs.commaSeparatedList() <<
" := " <<
m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
{
if (_lhs.type().sizeOnStack() > 0)
m_code <<
(_declare ? "let ": "") <<
_lhs.commaSeparatedList() <<
" := ";
m_code << m_context.utils().conversionFunction(_rhs.type(), _lhs.type()) <<
"(" <<
_rhs.commaSeparatedList() <<
")\n";
}
}
IRVariable IRGeneratorForStatements::zeroValue(Type const& _type, bool _splitFunctionTypes)
@ -1540,6 +1625,15 @@ string IRGeneratorForStatements::binaryOperation(
case Token::Mod:
fun = m_utils.checkedIntModFunction(*type);
break;
case Token::BitOr:
fun = "or";
break;
case Token::BitXor:
fun = "xor";
break;
case Token::BitAnd:
fun = "and";
break;
default:
break;
}
@ -1688,7 +1782,7 @@ void IRGeneratorForStatements::setLValue(Expression const& _expression, IRLValue
{
solAssert(!m_currentLValue, "");
if (_expression.annotation().lValueRequested)
if (_expression.annotation().willBeWrittenTo)
{
m_currentLValue.emplace(std::move(_lvalue));
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.");
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/IRVariable.h>
#include <functional>
namespace solidity::frontend
{
@ -70,7 +72,26 @@ public:
void endVisit(Identifier const& _identifier) override;
bool visit(Literal const& _literal) override;
bool visit(TryStatement const& _tryStatement) override;
bool visit(TryCatchClause const& _tryCatchClause) override;
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.
/// All involved expressions have already been visited.
void appendExternalFunctionCall(
@ -123,7 +144,7 @@ private:
/// @returns a fresh IR variable containing the value of the lvalue @a _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.
void setLValue(Expression const& _expression, IRLValue _lvalue);
void generateLoop(

View File

@ -31,6 +31,7 @@
using namespace std;
using namespace solidity;
using namespace solidity::util;
using namespace solidity::langutil;
using namespace solidity::frontend;
@ -130,15 +131,10 @@ bool CHC::visit(ContractDefinition const& _contract)
clearIndices(&_contract);
auto errorFunctionSort = make_shared<smt::FunctionSort>(
vector<smt::SortPointer>(),
smt::SortProvider::boolSort
);
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_implicitConstructorPredicate = createSymbolicBlock(interfaceSort(), "implicit_constructor_" + suffix);
m_implicitConstructorPredicate = createSymbolicBlock(arity0FunctionSort(), "implicit_constructor_" + suffix);
auto stateExprs = currentStateVariables();
setCurrentBlock(*m_interfaces.at(m_currentContract), &stateExprs);
@ -148,15 +144,7 @@ bool CHC::visit(ContractDefinition const& _contract)
void CHC::endVisit(ContractDefinition const& _contract)
{
for (auto const& var: m_stateVariables)
{
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());
auto implicitConstructor = (*m_implicitConstructorPredicate)({});
connectBlocks(genesis(), implicitConstructor);
m_currentBlock = implicitConstructor;
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*> stateVars;
for (auto const& contract: _contract.annotation().linearizedBaseContracts)
for (auto var: contract->stateVariables())
stateVars.push_back(var);
return stateVars;
return fold(
_contract.annotation().linearizedBaseContracts,
vector<VariableDeclaration const*>{},
[](auto&& _acc, auto _contract) { return _acc + _contract->stateVariables(); }
);
}
vector<smt::SortPointer> CHC::stateSorts(ContractDefinition const& _contract)
{
vector<smt::SortPointer> stateSorts;
for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract))
stateSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
return stateSorts;
return applyMap(
stateVariablesIncludingInheritedAndPrivate(_contract),
[](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); }
);
}
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:
/// - Index of failed assertion. 0 means no assertion failed.
/// - 2 sets of state variables:
@ -695,12 +691,9 @@ smt::SortPointer CHC::interfaceSort(ContractDefinition const& _contract)
/// - 1 set of output variables
smt::SortPointer CHC::sort(FunctionDefinition const& _function)
{
vector<smt::SortPointer> inputSorts;
for (auto const& var: _function.parameters())
inputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
vector<smt::SortPointer> outputSorts;
for (auto const& var: _function.returnParameters())
outputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
auto inputSorts = applyMap(_function.parameters(), smtSort);
auto outputSorts = applyMap(_function.returnParameters(), smtSort);
return make_shared<smt::FunctionSort>(
vector<smt::SortPointer>{smt::SortProvider::intSort} + m_stateSorts + inputSorts + m_stateSorts + inputSorts + outputSorts,
smt::SortProvider::boolSort
@ -715,11 +708,9 @@ smt::SortPointer CHC::sort(ASTNode const* _node)
auto fSort = dynamic_pointer_cast<smt::FunctionSort>(sort(*m_currentFunction));
solAssert(fSort, "");
vector<smt::SortPointer> varSorts;
for (auto const& var: m_currentFunction->localVariables())
varSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
return make_shared<smt::FunctionSort>(
fSort->domain + varSorts,
fSort->domain + applyMap(m_currentFunction->localVariables(), smtSort),
smt::SortProvider::boolSort
);
}
@ -729,11 +720,9 @@ smt::SortPointer CHC::summarySort(FunctionDefinition const& _function, ContractD
auto stateVariables = stateVariablesIncludingInheritedAndPrivate(_contract);
auto sorts = stateSorts(_contract);
vector<smt::SortPointer> inputSorts, outputSorts;
for (auto const& var: _function.parameters())
inputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
for (auto const& var: _function.returnParameters())
outputSorts.push_back(smt::smtSortAbstractFunction(*var->type()));
auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
auto inputSorts = applyMap(_function.parameters(), smtSort);
auto outputSorts = applyMap(_function.returnParameters(), smtSort);
return make_shared<smt::FunctionSort>(
vector<smt::SortPointer>{smt::SortProvider::intSort} + sorts + inputSorts + sorts + outputSorts,
smt::SortProvider::boolSort
@ -769,9 +758,10 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
smt::Expression CHC::interface()
{
vector<smt::Expression> paramExprs;
for (auto const& var: m_stateVariables)
paramExprs.push_back(m_context.variable(*var)->currentValue());
auto paramExprs = applyMap(
m_stateVariables,
[this](auto _var) { return m_context.variable(*_var)->currentValue(); }
);
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()};
auto contract = _function.annotation().contract;
args += contract->isLibrary() ? stateVariablesAtIndex(0, *contract) : initialStateVariables();
for (auto const& var: _function.parameters())
args.push_back(m_context.variable(*var)->valueAtIndex(0));
args += applyMap(_function.parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); });
args += contract->isLibrary() ? stateVariablesAtIndex(1, *contract) : currentStateVariables();
for (auto const& var: _function.returnParameters())
args.push_back(m_context.variable(*var)->currentValue());
args += applyMap(_function.returnParameters(), [this](auto _var) { return currentValue(*_var); });
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)
{
solAssert(m_currentContract, "");
vector<smt::Expression> exprs;
for (auto const& var: m_stateVariables)
exprs.push_back(m_context.variable(*var)->valueAtIndex(_index));
return exprs;
return applyMap(m_stateVariables, [&](auto _var) { return valueAtIndex(*_var, _index); });
}
vector<smt::Expression> CHC::stateVariablesAtIndex(int _index, ContractDefinition const& _contract)
{
vector<smt::Expression> exprs;
for (auto const& var: stateVariablesIncludingInheritedAndPrivate(_contract))
exprs.push_back(m_context.variable(*var)->valueAtIndex(_index));
return exprs;
return applyMap(
stateVariablesIncludingInheritedAndPrivate(_contract),
[&](auto _var) { return valueAtIndex(*_var, _index); }
);
}
vector<smt::Expression> CHC::currentStateVariables()
{
solAssert(m_currentContract, "");
vector<smt::Expression> exprs;
for (auto const& var: m_stateVariables)
exprs.push_back(m_context.variable(*var)->currentValue());
return exprs;
return applyMap(m_stateVariables, [this](auto _var) { return currentValue(*_var); });
}
vector<smt::Expression> CHC::currentFunctionVariables()
@ -886,9 +868,7 @@ vector<smt::Expression> CHC::currentFunctionVariables()
initInputExprs.push_back(m_context.variable(*var)->valueAtIndex(0));
mutableInputExprs.push_back(m_context.variable(*var)->currentValue());
}
vector<smt::Expression> returnExprs;
for (auto const& var: m_currentFunction->returnParameters())
returnExprs.push_back(m_context.variable(*var)->currentValue());
auto returnExprs = applyMap(m_currentFunction->returnParameters(), [this](auto _var) { return currentValue(*_var); });
return vector<smt::Expression>{m_error.currentValue()} +
initialStateVariables() +
initInputExprs +
@ -899,11 +879,10 @@ vector<smt::Expression> CHC::currentFunctionVariables()
vector<smt::Expression> CHC::currentBlockVariables()
{
vector<smt::Expression> paramExprs;
if (m_currentFunction)
for (auto const& var: m_currentFunction->localVariables())
paramExprs.push_back(m_context.variable(*var)->currentValue());
return currentFunctionVariables() + paramExprs;
return currentFunctionVariables() + applyMap(m_currentFunction->localVariables(), [this](auto _var) { return currentValue(*_var); });
return currentFunctionVariables();
}
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();
else
createVariable(*param);
for (auto const& var: function->returnParameters())
args.push_back(m_context.variable(*var)->currentValue());
args += applyMap(function->returnParameters(), [this](auto _var) { return currentValue(*_var); });
if (contract->isLibrary())
return (*m_summaries.at(contract).at(function))(args);

View File

@ -106,6 +106,7 @@ private:
smt::SortPointer constructorSort();
smt::SortPointer interfaceSort();
static smt::SortPointer interfaceSort(ContractDefinition const& _const);
smt::SortPointer arity0FunctionSort();
smt::SortPointer sort(FunctionDefinition const& _function);
smt::SortPointer sort(ASTNode const* _block);
/// @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, "");
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, "");
}
@ -229,6 +239,11 @@ CVC4::Type CVC4Interface::cvc4Sort(Sort const& _sort)
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
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:
break;
}

View File

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

View File

@ -107,6 +107,9 @@ void SMTEncoder::endVisit(ContractDefinition const& _contract)
solAssert(m_currentContract == &_contract, "");
m_currentContract = nullptr;
if (m_callStack.empty())
m_context.popSolver();
}
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));
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();
solAssert(components.size() == declarations.size(), "");
solAssert(symbComponents.size() == declarations.size(), "");
for (unsigned i = 0; i < declarations.size(); ++i)
if (
components.at(i) &&
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()))
@ -354,7 +363,7 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
{
auto const& type = _assignment.annotation().type;
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()));
solAssert(symbTupleLeft, "");
@ -365,17 +374,16 @@ void SMTEncoder::endVisit(Assignment const& _assignment)
auto const& rightComponents = symbTupleRight->components();
solAssert(leftComponents.size() == rightComponents.size(), "");
for (unsigned i = 0; i < leftComponents.size(); ++i)
{
auto const& left = leftComponents.at(i);
auto const& right = rightComponents.at(i);
/// Right hand side tuple component cannot be empty.
solAssert(right, "");
if (left)
rightArguments.push_back(right->currentValue(left->originalType()));
else
rightArguments.push_back(right->currentValue());
}
auto tupleTypeLeft = dynamic_cast<TupleType const*>(_assignment.leftHandSide().annotation().type);
solAssert(tupleTypeLeft, "");
solAssert(tupleTypeLeft->components().size() == leftComponents.size(), "");
auto const& typesLeft = tupleTypeLeft->components();
solAssert(tupleTypeRight->components().size() == rightComponents.size(), "");
auto const& typesRight = tupleTypeRight->components();
for (unsigned i = 0; i < rightComponents.size(); ++i)
rightArguments.push_back(symbTupleRight->component(i, typesRight.at(i), typesLeft.at(i)));
}
else
{
@ -418,17 +426,16 @@ void SMTEncoder::endVisit(TupleExpression const& _tuple)
solAssert(symbComponents.size() == tupleComponents->size(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i)
{
auto sComponent = symbComponents.at(i);
auto tComponent = tupleComponents->at(i);
if (sComponent && tComponent)
if (tComponent)
{
if (auto varDecl = identifierToVariable(*tComponent))
m_context.addAssertion(sComponent->currentValue() == currentValue(*varDecl));
m_context.addAssertion(symbTuple->component(i) == currentValue(*varDecl));
else
{
if (!m_context.knownExpression(*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(_op.subExpression().annotation().lValueRequested, "");
solAssert(_op.subExpression().annotation().willBeWrittenTo, "");
if (auto identifier = dynamic_cast<Identifier const*>(&_op.subExpression()))
{
auto decl = identifierToVariable(*identifier);
@ -658,7 +665,6 @@ void SMTEncoder::initFunction(FunctionDefinition const& _function)
{
solAssert(m_callStack.empty(), "");
solAssert(m_currentContract, "");
m_context.reset();
m_context.pushSolver();
m_pathConditions.clear();
pushCallStack({&_function, nullptr});
@ -700,7 +706,7 @@ void SMTEncoder::visitGasLeft(FunctionCall const& _funCall)
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.
}
@ -807,13 +813,15 @@ void SMTEncoder::endVisit(Return const& _return)
{
auto const& symbTuple = dynamic_pointer_cast<smt::SymbolicTupleVariable>(m_context.expression(*_return.expression()));
solAssert(symbTuple, "");
auto const& components = symbTuple->components();
solAssert(components.size() == returnParams.size(), "");
solAssert(symbTuple->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)
{
solAssert(components.at(i), "");
m_context.addAssertion(components.at(i)->currentValue(returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
}
m_context.addAssertion(symbTuple->component(i, types.at(i), returnParams.at(i)->type()) == m_context.newValue(*returnParams.at(i)));
}
else if (returnParams.size() == 1)
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(), "");
for (unsigned i = 0; i < symbComponents.size(); ++i)
{
auto sComponent = symbComponents.at(i);
auto param = returnParams.at(i);
solAssert(param, "");
if (sComponent)
{
solAssert(m_context.knownVariable(*param), "");
m_context.addAssertion(sComponent->currentValue() == currentValue(*param));
}
solAssert(m_context.knownVariable(*param), "");
m_context.addAssertion(symbTuple->component(i) == currentValue(*param));
}
}
else if (returnParams.size() == 1)

View File

@ -28,6 +28,8 @@
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
using namespace std;
using namespace solidity;
@ -37,10 +39,10 @@ using namespace solidity::frontend::smt;
SMTLib2Interface::SMTLib2Interface(
map<h256, string> const& _queryResponses,
ReadCallback::Callback const& _smtCallback
ReadCallback::Callback _smtCallback
):
m_queryResponses(_queryResponses),
m_smtCallback(_smtCallback)
m_smtCallback(std::move(_smtCallback))
{
reset();
}
@ -50,6 +52,7 @@ void SMTLib2Interface::reset()
m_accumulatedOutput.clear();
m_accumulatedOutput.emplace_back();
m_variables.clear();
m_userSorts.clear();
write("(set-option :produce-models true)");
write("(set-logic ALL)");
}
@ -145,6 +148,14 @@ string SMTLib2Interface::toSExpr(smt::Expression const& _expr)
sexpr += "(as const " + toSmtLibSort(*arraySort) + ") ";
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
{
sexpr += _expr.name;
@ -169,6 +180,22 @@ string SMTLib2Interface::toSmtLibSort(Sort const& _sort)
solAssert(arraySort.domain && 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:
solAssert(false, "Invalid SMT sort");
}

View File

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

View File

@ -94,7 +94,8 @@ public:
{"mod", 2},
{"select", 2},
{"store", 3},
{"const_array", 2}
{"const_array", 2},
{"tuple_get", 2}
};
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)
{
return Expression("not", std::move(_a), Kind::Bool);

View File

@ -33,7 +33,8 @@ enum class Kind
Bool,
Function,
Array,
Sort
Sort,
Tuple
};
struct Sort
@ -115,6 +116,46 @@ struct SortSort: public Sort
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.*/
struct SortProvider
{

View File

@ -75,6 +75,21 @@ SortPointer smtSort(frontend::Type const& _type)
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:
// Abstract case.
return SortProvider::intSort;
@ -96,6 +111,17 @@ SortPointer smtSortAbstractFunction(frontend::Type const& _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)
{
if (isNumber(_category))
@ -106,6 +132,8 @@ Kind smtKind(frontend::Type::Category _category)
return Kind::Function;
else if (isMapping(_category) || isArray(_category))
return Kind::Array;
else if (isTuple(_category))
return Kind::Tuple;
// Abstract case.
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.
/// Otherwise return smtSort(_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.
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(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
{
if (_targetType)
{
solAssert(m_originalType, "");
// 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)));
}
optional<smt::Expression> conversion = symbolicTypeConversion(m_originalType, _targetType);
if (conversion)
return *conversion;
return SymbolicVariable::currentValue(_targetType);
}
@ -262,16 +255,34 @@ SymbolicTupleVariable::SymbolicTupleVariable(
SymbolicVariable(_type, _type, move(_uniqueName), _context)
{
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)
if (componentsTypes.at(i))
{
string componentName = m_uniqueName + "_component_" + to_string(i);
auto result = smt::newSymbolicVariable(*componentsTypes.at(i), componentName, m_context);
solAssert(result.second, "");
m_components.emplace_back(move(result.second));
}
else
m_components.emplace_back(nullptr);
}
SymbolicTupleVariable::SymbolicTupleVariable(
SortPointer _sort,
string _uniqueName,
EncodingContext& _context
):
SymbolicVariable(move(_sort), move(_uniqueName), _context)
{
solAssert(m_sort->kind == Kind::Tuple, "");
}
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,
EncodingContext& _context
);
SymbolicTupleVariable(
SortPointer _sort,
std::string _uniqueName,
EncodingContext& _context
);
std::vector<std::shared_ptr<SymbolicVariable>> const& components()
{
return m_components;
}
private:
std::vector<std::shared_ptr<SymbolicVariable>> m_components;
std::vector<SortPointer> const& components();
Expression component(
size_t _index,
TypePointer _fromType = nullptr,
TypePointer _toType = nullptr
);
};
}

View File

@ -40,15 +40,15 @@ set<VariableDeclaration const*> VariableUsage::touchedVariables(ASTNode const& _
void VariableUsage::endVisit(Identifier const& _identifier)
{
if (_identifier.annotation().lValueRequested)
if (_identifier.annotation().willBeWrittenTo)
checkIdentifier(_identifier);
}
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.
auto identifier = dynamic_cast<Identifier const*>(SMTEncoder::leftmostBase(_indexAccess));
if (identifier)

View File

@ -193,6 +193,11 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
solAssert(arraySort && arraySort->domain, "");
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, "");
}
@ -217,6 +222,28 @@ z3::sort Z3Interface::z3Sort(Sort const& _sort)
auto const& arraySort = dynamic_cast<ArraySort const&>(_sort);
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:
break;
}

View File

@ -27,6 +27,7 @@
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/DeclarationTypeChecker.h>
#include <libsolidity/analysis/DocStringAnalyser.h>
#include <libsolidity/analysis/GlobalContext.h>
#include <libsolidity/analysis/NameAndTypeResolver.h>
@ -67,6 +68,7 @@
#include <json/json.h>
#include <boost/algorithm/string.hpp>
#include <utility>
using namespace std;
using namespace solidity;
@ -78,8 +80,8 @@ using solidity::util::toHex;
static int g_compilerStackCounts = 0;
CompilerStack::CompilerStack(ReadCallback::Callback const& _readFile):
m_readFile{_readFile},
CompilerStack::CompilerStack(ReadCallback::Callback _readFile):
m_readFile{std::move(_readFile)},
m_enabledSMTSolvers{smt::SMTSolverChoice::All()},
m_generateIR{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
// contract or function level.
// 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.
/// @param _readFile callback used to read files for import statements. Must return
/// and must not emit exceptions.
explicit CompilerStack(ReadCallback::Callback const& _readFile = ReadCallback::Callback());
explicit CompilerStack(ReadCallback::Callback _readFile = ReadCallback::Callback());
~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
vector<string> contracts{ _contract };
if (!_contract.empty())
contracts.push_back("*");
contracts.emplace_back("*");
for (auto const& contract: contracts)
if (
_outputSelection[file].isMember(contract) &&
@ -591,7 +591,7 @@ boost::variant<StandardCompiler::InputsAndSettings, Json::Value> StandardCompile
));
else
{
ret.sources[sourceName] = result.responseOrErrorMessage;
ret.sources[sourceName] = result.responseOrErrorMessage;
found = true;
break;
}

View File

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

View File

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

View File

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

View File

@ -47,6 +47,7 @@
#include <boost/multiprecision/cpp_int.hpp>
#include <map>
#include <utility>
#include <vector>
#include <functional>
#include <string>
@ -120,7 +121,7 @@ inline std::ostream& operator<<(std::ostream& os, bytes const& _bytes)
class ScopeGuard
{
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(); }
private:

View File

@ -34,11 +34,12 @@
#include <set>
#include <functional>
#include <utility>
#include <type_traits>
/// Operators need to stay in the global namespace.
/// 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)
_a.push_back(i);
@ -51,7 +52,7 @@ template <class T, class U> std::vector<T>& operator+=(std::vector<T>& _a, U&& _
return _a;
}
/// 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());
return _a;
@ -64,7 +65,7 @@ template <class U, class... T> std::multiset<T...>& operator+=(std::multiset<T..
return _a;
}
/// 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());
return _a;
@ -141,6 +142,36 @@ inline std::multiset<T...>& operator-=(std::multiset<T...>& _a, C const& _b)
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>
T convertContainer(U const& _from)
{

View File

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

View File

@ -36,7 +36,7 @@ namespace solidity::util
///
template <class ResultType>
class Result
class [[nodiscard]] Result
{
public:
/// 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(), "");
vector<YulString> const* parameterTypes = nullptr;
vector<YulString> const* returnTypes = nullptr;
bool needsLiteralArguments = false;
vector<bool> const* needsLiteralArguments = nullptr;
if (BuiltinFunction const* f = m_dialect.builtin(_funCall.functionName.name))
{
parameterTypes = &f->parameters;
returnTypes = &f->returns;
if (f->literalArguments)
needsLiteralArguments = true;
needsLiteralArguments = &f->literalArguments.value();
}
else if (!m_currentScope->lookup(_funCall.functionName.name, GenericVisitor{
[&](Scope::Variable const&)
@ -293,11 +293,13 @@ vector<YulString> AsmAnalyzer::operator()(FunctionCall const& _funCall)
);
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));
if (needsLiteralArguments)
if (needsLiteralArguments && (*needsLiteralArguments)[i - 1])
{
if (!holds_alternative<Literal>(arg))
typeError(

View File

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

View File

@ -28,6 +28,7 @@
#include <vector>
#include <set>
#include <optional>
namespace solidity::yul
{
@ -46,8 +47,8 @@ struct BuiltinFunction
ControlFlowSideEffects controlFlowSideEffects;
/// If true, this is the msize instruction.
bool isMSize = false;
/// If true, can only accept literals as arguments and they cannot be moved to variables.
bool literalArguments = false;
/// 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.
std::optional<std::vector<bool>> literalArguments;
};
struct Dialect: boost::noncopyable

View File

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

View File

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

View File

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

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