mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #10010 from ethereum/develop
Merge develop into breaking.
This commit is contained in:
commit
8a1bf41ac0
@ -15,7 +15,10 @@ Compiler Features:
|
||||
|
||||
|
||||
Bugfixes:
|
||||
* Code generator: Fix internal compiler error when referencing members via module name but not using the reference.
|
||||
* Code generator: Fix ``ABIEncoderV2`` pragma from the current module affecting inherited functions and applied modifiers.
|
||||
* Type Checker: Fix internal compiler error caused by storage parameters with nested mappings in libraries.
|
||||
* Name Resolver: Fix shadowing/same-name warnings for later declarations.
|
||||
|
||||
|
||||
### 0.7.3 (2020-10-07)
|
||||
@ -71,8 +74,8 @@ Bugfixes:
|
||||
* Code generator: Fix internal error on stripping dynamic types from return parameters on EVM versions without ``RETURNDATACOPY``.
|
||||
* Type Checker: Add missing check against nested dynamic arrays in ABI encoding functions when ABIEncoderV2 is disabled.
|
||||
* Type Checker: Correct the error message for invalid named parameter in a call to refer to the right argument.
|
||||
* Type Checker: Correct the warning for homonymous, but not shadowing declarations.
|
||||
* Type Checker: Disallow ``virtual`` for modifiers in libraries.
|
||||
* Name Resolver: Correct the warning for homonymous, but not shadowing declarations.
|
||||
* Type system: Fix internal error on implicit conversion of contract instance to the type of its ``super``.
|
||||
* Type system: Fix internal error on implicit conversion of string literal to a calldata string.
|
||||
* Type system: Fix named parameters in overloaded function and event calls being matched incorrectly if the order differs from the declaration.
|
||||
|
@ -20,50 +20,52 @@
|
||||
|
||||
### Create the Release
|
||||
- [ ] Create Github release page: https://github.com/ethereum/solidity/releases/new
|
||||
- [ ] On the release page, select the ``release`` branch as new target and set tag to the new version (e.g. `v0.5.4`) (make sure you only `SAVE DRAFT` instead of `PUBLISH RELEASE` before the actual release)
|
||||
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e origin/release..origin/develop``).
|
||||
- [ ] Create a pull request from ``develop`` to ``release``, wait for the tests, then merge it.
|
||||
- [ ] On the release page, select the ``develop`` branch as new target and set tag to the new version (e.g. `v0.5.4`) (make sure you only `SAVE DRAFT` instead of `PUBLISH RELEASE` before the actual release)
|
||||
- [ ] Thank voluntary contributors in the Github release page (use ``git shortlog -s -n -e v0.5.3..origin/develop``).
|
||||
- [ ] Make a final check that there are no platform-dependency issues in the ``solidity-test-bytecode`` repository.
|
||||
- [ ] Wait for the tests for the commit on ``release``, create a release in Github, creating the tag (click the `PUBLISH RELEASE` button on the release page.)
|
||||
- [ ] Wait for the CI runs on the tag itself (travis should push artifacts onto the Github release page).
|
||||
- [ ] Check that all tests on the latest commit in ``develop`` are green.
|
||||
- [ ] Click the `PUBLISH RELEASE` button on the release page, creating the tag.
|
||||
- [ ] Wait for the CI runs on the tag itself (travis will push the source archive and the static linux binary onto the Github release page).
|
||||
|
||||
### Download Binaries
|
||||
- [ ] Take the ``solc.exe`` binary from the ``b_win_release`` run of the released commit in circle-ci and add it to the release page as ``solc-windows.exe``.
|
||||
- [ ] Run ``scripts/create_source_tarball.sh`` while being on the tag to create the source tarball. Make sure to create ``prerelease.txt`` before: (``echo -n > prerelease.txt``). This will create the tarball in a directory called ``upload``.
|
||||
- [ ] Take the tarball from the upload directory (its name should be ``solidity_x.x.x.tar.gz``, otherwise ``prerelease.txt`` was missing in the step before) and upload the source tarball to the release page.
|
||||
- [ ] Take the ``solc`` binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
|
||||
- [ ] If not done by travis: Take the ``soljson.js`` binary from the ``b_ems`` run of the released commit in circle-ci and add it to the release page as ``soljson.js``.
|
||||
|
||||
### Update [solc-bin](https://github.com/ethereum/solc-bin/)
|
||||
- [ ] Copy ``soljson.js`` to ``solc-bin/bin/soljson-v$VERSION+commit.$COMMIT.js``
|
||||
- [ ] Copy ``solc-static-linux`` from the release page to ``solc-bin/linux-amd64/solc-linux-amd64-v$VERSION+commit.$COMMIT``
|
||||
- [ ] Make it executable.
|
||||
- [ ] Copy ``solc-macos`` from the release page to ``solc-bin/macosx-amd64/solc-macosx-amd64-v$VERSION+commit.$COMMIT``
|
||||
- [ ] Make it executable.
|
||||
- [ ] Copy ``solc-windows.exe`` from the release page to ``solc-bin/windows-amd64/solc-windows-amd64-v$VERSION+commit.$COMMIT.exe``
|
||||
- [ ] Run ``./update --reuse-hashes`` in ``solc-bin`` and verify that the script has updated ``list.js``, ``list.txt`` and ``list.json`` files correctly and that symlinks to the new release have been added in ``solc-bin/wasm/`` and ``solc-bin/emscripten-wasm32/``.
|
||||
- [ ] Create a pull request and merge.
|
||||
|
||||
### Homebrew and MacOS
|
||||
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/Homebrew/homebrew-core/blob/master/Formula/solidity.rb
|
||||
- [ ] Update the version and the hash (``sha256sum solidity_x.x.x.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
|
||||
- [ ] Take the binary from the ``b_osx`` run of the released commit in circle-ci and add it to the release page as ``solc-macos``.
|
||||
- [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in https://github.com/Homebrew/homebrew-core/blob/master/Formula/solidity.rb
|
||||
- [ ] Update the version and the hash (``sha256sum solidity_$VERSION.tar.gz``) in https://github.com/ethereum/homebrew-ethereum/blob/master/solidity.rb
|
||||
|
||||
### Update solc-bin
|
||||
- [ ] Copy ``soljson.js`` from the release page to ``solc-bin/bin/soljson-v<version>+commit.<commit>.js``
|
||||
- [ ] Copy ``solc-static-linux`` from the release page to ``solc-bin/linux-amd64/solc-linux-amd64-v<version>+commit.<commit>``
|
||||
- [ ] Copy ``solc-macos`` from the release page to ``solc-bin/macos-amd64/solc-macos-amd64-v<version>+commit.<commit>``
|
||||
- [ ] Copy ``solc-windows.zip`` from the release page to ``solc-bin/windows-amd64/solc-windows-amd64-v<version>+commit.<commit>.zip``
|
||||
- [ ] Make the linux and the macos binaries executable.
|
||||
- [ ] Run ``./update`` in ``solc-bin`` and verify that the script has updated ``list.js``, ``list.txt`` and ``list.json`` files correctly and that symlinks to the new release have been added in ``solc-bin/wasm/`` and ``solc-bin/emscripten-wasm32/``.
|
||||
- [ ] Create a pull request and merge.
|
||||
### Docker
|
||||
- [ ] Run ``./scripts/docker_deploy_manual.sh v$VERSION``).
|
||||
|
||||
### PPA
|
||||
- [ ] Change ``scripts/release_ppa.sh`` to match your key's email and key id.
|
||||
- [ ] Run ``scripts/release_ppa.sh release`` to create the PPA release (you need the relevant openssl key).
|
||||
- [ ] Run ``scripts/release_ppa.sh v$VERSION`` to create the PPA release (you need the relevant openssl key).
|
||||
- [ ] Wait for the ``~ethereum/ubuntu/ethereum-static`` PPA build to be finished and published for *all platforms*. SERIOUSLY: DO NOT PROCEED EARLIER!!! *After* the static builds are *published*, copy the static package to the ``~ethereum/ubuntu/ethereum`` PPA for the destination series ``Trusty`` and ``Xenial`` while selecting ``Copy existing binaries``.
|
||||
|
||||
### Docker
|
||||
- [ ] Check that the Docker release was pushed to Docker Hub (this still seems to have problems, run ``./scripts/docker_deploy_manual.sh v0.x.x``).
|
||||
|
||||
### Documentation
|
||||
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` on the bottom of the page and click `BUILD`)
|
||||
- [ ] Build the new version on https://readthedocs.org/projects/solidity/ (select `latest` at the bottom of the page and click `BUILD`)
|
||||
- [ ] In the admin panel, select `Versions` in the menu and set the default version to the released one.
|
||||
|
||||
### Release solc-js
|
||||
- [ ] Wait until solc-bin was properly deployed. You can test this via remix - a test run through remix is advisable anyway.
|
||||
- [ ] Increment the version number, create a pull request for that, merge it after tests succeeded.
|
||||
- [ ] Run ``npm publish`` in the updated ``solc-js`` repository.
|
||||
- [ ] Make sure to push the tag ``npm publish`` created with ``git push --tags``.
|
||||
- [ ] Create a tag using ``git tag --annotate v$VERSION`` and push it with ``git push --tags``.
|
||||
|
||||
### Post-release
|
||||
- [ ] Publish the blog post.
|
||||
- [ ] Create a commit to increase the version number on ``develop`` in ``CMakeLists.txt`` and add a new skeleton changelog entry.
|
||||
- [ ] Merge ``release`` back into ``develop``.
|
||||
- [ ] Announce on Twitter and Reddit.
|
||||
- [ ] Lean back, wait for bug reports and repeat from step 1 :)
|
||||
|
@ -519,7 +519,6 @@ An ``assert``-style exception is generated in the following situations:
|
||||
#. If you access an array or an array slice at a too large or negative index (i.e. ``x[i]`` where ``i >= x.length`` or ``i < 0``).
|
||||
#. If you access a fixed-length ``bytesN`` at a too large or negative index.
|
||||
#. If you divide or modulo by zero (e.g. ``5 / 0`` or ``23 % 0``).
|
||||
#. If you shift by a negative amount.
|
||||
#. If you convert a value too big or negative into an enum type.
|
||||
#. If you call a zero-initialized variable of internal function type.
|
||||
#. If you call ``assert`` with an argument that evaluates to false.
|
||||
|
@ -26,42 +26,58 @@ a 0.x version number `to indicate this fast pace of change <https://semver.org/#
|
||||
|
||||
.. warning::
|
||||
|
||||
Solidity recently released the 0.6.x version that introduced a lot of breaking
|
||||
changes. Make sure you read :doc:`the full list <060-breaking-changes>`.
|
||||
Solidity recently released the 0.7.x version that introduced a lot of breaking
|
||||
changes. Make sure you read :doc:`the full list <070-breaking-changes>`.
|
||||
|
||||
Language Documentation
|
||||
----------------------
|
||||
Ideas for improving Solidity or this documentation are always welcome,
|
||||
read our :doc:`contributors guide <contributing>` for more details.
|
||||
|
||||
If you are new to the concept of smart contracts we recommend you start with
|
||||
:ref:`an example smart contract <simple-smart-contract>` written
|
||||
in Solidity. When you are ready for more detail, we recommend you read the
|
||||
:doc:`"Solidity by Example" <solidity-by-example>` and
|
||||
"Language Description" sections to learn the core concepts of the language.
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
For further reading, try :ref:`the basics of blockchains <blockchain-basics>`
|
||||
and details of the :ref:`Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
|
||||
**1. Understand the Smart Contract Basics**
|
||||
|
||||
If you are new to the concept of smart contracts we recommend you to get started by digging
|
||||
into the "Introduction to Smart Contracts" section, which covers:
|
||||
* :ref:`A simple example smart contract <simple-smart-contract>` written in Solidity.
|
||||
* :ref:`Blockchain Basics <blockchain-basics>`.
|
||||
* :ref:`The Ethereum Virtual Machine <the-ethereum-virtual-machine>`.
|
||||
|
||||
**2. Get to Know Solidity**
|
||||
|
||||
Once you are accustomed to the basics, we recommend you read the :doc:`"Solidity by Example" <solidity-by-example>`
|
||||
and “Language Description” sections to understand the core concepts of the language.
|
||||
|
||||
**3. Install the Solidity Compiler**
|
||||
|
||||
There are various ways to install the Solidity compiler,
|
||||
simply choose your preferred option and follow the steps outlined on the :ref:`installation page <installing-solidity>`.
|
||||
|
||||
.. hint::
|
||||
You can always try out code examples in your browser with the
|
||||
You can try out code examples directly in your browser with the
|
||||
`Remix IDE <https://remix.ethereum.org>`_. Remix is a web browser based IDE
|
||||
that allows you to write Solidity smart contracts, then deploy and run the
|
||||
smart contracts. It can take a while to load, so please be patient.
|
||||
that allows you to write, deploy and administer Solidity smart contracts, without
|
||||
the need to install Solidity locally.
|
||||
|
||||
.. warning::
|
||||
As humans write software, it can have bugs. You should follow established
|
||||
software development best-practices when writing your smart contracts, this
|
||||
software development best-practices when writing your smart contracts. This
|
||||
includes code review, testing, audits, and correctness proofs. Smart contract
|
||||
users are sometimes more confident with code than their authors, and
|
||||
blockchains and smart contracts have their own unique issues to
|
||||
watch out for, so before working on production code, make sure you read the
|
||||
:ref:`security_considerations` section.
|
||||
|
||||
If you have any questions, you can try searching for answers or asking on the
|
||||
`Ethereum Stackexchange <https://ethereum.stackexchange.com/>`_, or
|
||||
our `gitter channel <https://gitter.im/ethereum/solidity/>`_.
|
||||
**4. Learn More**
|
||||
|
||||
Ideas for improving Solidity or this documentation are always welcome,
|
||||
read our :doc:`contributors guide <contributing>` for more details.
|
||||
If you want to learn more about building decentralized applications on Ethereum, the
|
||||
`Ethereum Developer Resources <https://ethereum.org/en/developers/>`_
|
||||
can help you with further general documentation around Ethereum, and a wide selection of tutorials,
|
||||
tools and development frameworks.
|
||||
|
||||
If you have any questions, you can try searching for answers or asking on the
|
||||
`Ethereum StackExchange <https://ethereum.stackexchange.com/>`_, or
|
||||
our `Gitter channel <https://gitter.im/ethereum/solidity/>`_.
|
||||
|
||||
.. _translations:
|
||||
|
||||
|
@ -111,6 +111,14 @@ activate it using ``pragma experimental ABIEncoderV2;`` - we kept
|
||||
the same pragma, even though it is not considered experimental
|
||||
anymore.
|
||||
|
||||
The set of types supported by the new encoder is a strict superset of
|
||||
the ones supported by the old one. Contracts that use it can interact with ones
|
||||
that do not without limitations. The reverse is possible only as long as the
|
||||
non-``ABIEncoderV2`` contract does not try to make calls that would require
|
||||
decoding types only supported by the new encoder. The compiler can detect this
|
||||
and will issue an error. Simply enabling ``ABIEncoderV2`` for your contract is
|
||||
enough to make the error go away.
|
||||
|
||||
.. _smt_checker:
|
||||
|
||||
SMTChecker
|
||||
|
@ -100,6 +100,7 @@ bool DeclarationContainer::isInvisible(ASTString const& _name) const
|
||||
bool DeclarationContainer::registerDeclaration(
|
||||
Declaration const& _declaration,
|
||||
ASTString const* _name,
|
||||
langutil::SourceLocation const* _location,
|
||||
bool _invisible,
|
||||
bool _update
|
||||
)
|
||||
@ -115,15 +116,34 @@ bool DeclarationContainer::registerDeclaration(
|
||||
m_declarations.erase(*_name);
|
||||
m_invisibleDeclarations.erase(*_name);
|
||||
}
|
||||
else if (conflictingDeclaration(_declaration, _name))
|
||||
else
|
||||
{
|
||||
if (conflictingDeclaration(_declaration, _name))
|
||||
return false;
|
||||
|
||||
// Do not warn about shadowing for structs and enums because their members are
|
||||
// not accessible without prefixes. Also do not warn about event parameters
|
||||
// because they do not participate in any proper scope.
|
||||
bool special = _declaration.scope() && (_declaration.isStructMember() || _declaration.isEnumValue() || _declaration.isEventParameter());
|
||||
if (m_enclosingContainer && !special)
|
||||
m_homonymCandidates.emplace_back(*_name, _location ? _location : &_declaration.location());
|
||||
}
|
||||
|
||||
vector<Declaration const*>& decls = _invisible ? m_invisibleDeclarations[*_name] : m_declarations[*_name];
|
||||
if (!util::contains(decls, &_declaration))
|
||||
decls.push_back(&_declaration);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DeclarationContainer::registerDeclaration(
|
||||
Declaration const& _declaration,
|
||||
bool _invisible,
|
||||
bool _update
|
||||
)
|
||||
{
|
||||
return registerDeclaration(_declaration, nullptr, nullptr, _invisible, _update);
|
||||
}
|
||||
|
||||
vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive, bool _alsoInvisible) const
|
||||
{
|
||||
solAssert(!_name.empty(), "Attempt to resolve empty name.");
|
||||
@ -164,3 +184,16 @@ vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) con
|
||||
|
||||
return similar;
|
||||
}
|
||||
|
||||
void DeclarationContainer::populateHomonyms(back_insert_iterator<Homonyms> _it) const
|
||||
{
|
||||
for (DeclarationContainer const* innerContainer: m_innerContainers)
|
||||
innerContainer->populateHomonyms(_it);
|
||||
|
||||
for (auto [name, location]: m_homonymCandidates)
|
||||
{
|
||||
vector<Declaration const*> const& declarations = m_enclosingContainer->resolveName(name, true, true);
|
||||
if (!declarations.empty())
|
||||
_it = make_pair(location, declarations);
|
||||
}
|
||||
}
|
||||
|
@ -24,9 +24,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ast/ASTForward.h>
|
||||
#include <liblangutil/Exceptions.h>
|
||||
#include <liblangutil/SourceLocation.h>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
namespace solidity::frontend
|
||||
{
|
||||
@ -38,17 +38,26 @@ namespace solidity::frontend
|
||||
class DeclarationContainer
|
||||
{
|
||||
public:
|
||||
using Homonyms = std::vector<std::pair<langutil::SourceLocation const*, std::vector<Declaration const*>>>;
|
||||
|
||||
explicit DeclarationContainer(
|
||||
ASTNode const* _enclosingNode = nullptr,
|
||||
DeclarationContainer const* _enclosingContainer = nullptr
|
||||
DeclarationContainer* _enclosingContainer = nullptr
|
||||
):
|
||||
m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer) {}
|
||||
m_enclosingNode(_enclosingNode), m_enclosingContainer(_enclosingContainer)
|
||||
{
|
||||
if (_enclosingContainer)
|
||||
_enclosingContainer->m_innerContainers.emplace_back(this);
|
||||
}
|
||||
/// Registers the declaration in the scope unless its name is already declared or the name is empty.
|
||||
/// @param _name the name to register, if nullptr the intrinsic name of @a _declaration is used.
|
||||
/// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName
|
||||
/// @param _update if true, replaces a potential declaration that is already present
|
||||
/// @param _location alternative location, used to point at homonymous declarations.
|
||||
/// @param _invisible if true, registers the declaration, reports name clashes but does not return it in @a resolveName.
|
||||
/// @param _update if true, replaces a potential declaration that is already present.
|
||||
/// @returns false if the name was already declared.
|
||||
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr, bool _invisible = false, bool _update = false);
|
||||
bool registerDeclaration(Declaration const& _declaration, ASTString const* _name, langutil::SourceLocation const* _location, bool _invisible, bool _update);
|
||||
bool registerDeclaration(Declaration const& _declaration, bool _invisible, bool _update);
|
||||
|
||||
std::vector<Declaration const*> resolveName(ASTString const& _name, bool _recursive = false, bool _alsoInvisible = false) const;
|
||||
ASTNode const* enclosingNode() const { return m_enclosingNode; }
|
||||
DeclarationContainer const* enclosingContainer() const { return m_enclosingContainer; }
|
||||
@ -67,11 +76,18 @@ public:
|
||||
/// Searches this and all parent containers.
|
||||
std::vector<ASTString> similarNames(ASTString const& _name) const;
|
||||
|
||||
/// Populates a vector of (location, declaration) pairs, where location is a location of an inner-scope declaration,
|
||||
/// and declaration is the corresponding homonymous outer-scope declaration.
|
||||
void populateHomonyms(std::back_insert_iterator<Homonyms> _it) const;
|
||||
|
||||
private:
|
||||
ASTNode const* m_enclosingNode;
|
||||
DeclarationContainer const* m_enclosingContainer;
|
||||
std::vector<DeclarationContainer const*> m_innerContainers;
|
||||
std::map<ASTString, std::vector<Declaration const*>> m_declarations;
|
||||
std::map<ASTString, std::vector<Declaration const*>> m_invisibleDeclarations;
|
||||
/// List of declarations (name and location) to check later for homonymity.
|
||||
std::vector<std::pair<std::string, langutil::SourceLocation const*>> m_homonymCandidates;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include <liblangutil/ErrorReporter.h>
|
||||
#include <libsolutil/StringUtils.h>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <unordered_set>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity::langutil;
|
||||
@ -47,7 +48,7 @@ NameAndTypeResolver::NameAndTypeResolver(
|
||||
m_scopes[nullptr] = make_shared<DeclarationContainer>();
|
||||
for (Declaration const* declaration: _globalContext.declarations())
|
||||
{
|
||||
solAssert(m_scopes[nullptr]->registerDeclaration(*declaration), "Unable to register global declaration.");
|
||||
solAssert(m_scopes[nullptr]->registerDeclaration(*declaration, false, false), "Unable to register global declaration.");
|
||||
}
|
||||
}
|
||||
|
||||
@ -149,7 +150,7 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
{
|
||||
try
|
||||
{
|
||||
m_scopes[nullptr]->registerDeclaration(_declaration, nullptr, false, true);
|
||||
m_scopes[nullptr]->registerDeclaration(_declaration, false, true);
|
||||
solAssert(_declaration.scope() == nullptr, "Updated declaration outside global scope.");
|
||||
}
|
||||
catch (langutil::FatalError const&)
|
||||
@ -202,7 +203,7 @@ Declaration const* NameAndTypeResolver::pathFromCurrentScope(vector<ASTString> c
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
void NameAndTypeResolver::warnVariablesNamedLikeInstructions() const
|
||||
{
|
||||
for (auto const& instruction: evmasm::c_instructions)
|
||||
{
|
||||
@ -223,6 +224,52 @@ void NameAndTypeResolver::warnVariablesNamedLikeInstructions()
|
||||
}
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::warnHomonymDeclarations() const
|
||||
{
|
||||
DeclarationContainer::Homonyms homonyms;
|
||||
m_scopes.at(nullptr)->populateHomonyms(back_inserter(homonyms));
|
||||
|
||||
for (auto [innerLocation, outerDeclarations]: homonyms)
|
||||
{
|
||||
solAssert(innerLocation && !outerDeclarations.empty(), "");
|
||||
|
||||
bool magicShadowed = false;
|
||||
SecondarySourceLocation homonymousLocations;
|
||||
SecondarySourceLocation shadowedLocations;
|
||||
for (Declaration const* outerDeclaration: outerDeclarations)
|
||||
{
|
||||
solAssert(outerDeclaration, "");
|
||||
if (dynamic_cast<MagicVariableDeclaration const*>(outerDeclaration))
|
||||
magicShadowed = true;
|
||||
else if (!outerDeclaration->isVisibleInContract())
|
||||
homonymousLocations.append("The other declaration is here:", outerDeclaration->location());
|
||||
else
|
||||
shadowedLocations.append("The shadowed declaration is here:", outerDeclaration->location());
|
||||
}
|
||||
|
||||
if (magicShadowed)
|
||||
m_errorReporter.warning(
|
||||
2319_error,
|
||||
*innerLocation,
|
||||
"This declaration shadows a builtin symbol."
|
||||
);
|
||||
if (!homonymousLocations.infos.empty())
|
||||
m_errorReporter.warning(
|
||||
8760_error,
|
||||
*innerLocation,
|
||||
"This declaration has the same name as another declaration.",
|
||||
homonymousLocations
|
||||
);
|
||||
if (!shadowedLocations.infos.empty())
|
||||
m_errorReporter.warning(
|
||||
2519_error,
|
||||
*innerLocation,
|
||||
"This declaration shadows an existing declaration.",
|
||||
shadowedLocations
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::setScope(ASTNode const* _node)
|
||||
{
|
||||
m_currentScope = m_scopes[_node].get();
|
||||
@ -281,8 +328,8 @@ bool NameAndTypeResolver::resolveNamesAndTypesInternal(ASTNode& _node, bool _res
|
||||
}
|
||||
|
||||
// make "this" and "super" invisible.
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), true, true);
|
||||
m_globalContext.resetCurrentContract();
|
||||
|
||||
return success;
|
||||
@ -303,7 +350,7 @@ void NameAndTypeResolver::importInheritedScope(ContractDefinition const& _base)
|
||||
for (auto const& declaration: nameAndDeclaration.second)
|
||||
// Import if it was declared in the base, is not the constructor and is visible in derived classes
|
||||
if (declaration->scope() == &_base && declaration->isVisibleInDerivedContracts())
|
||||
if (!m_currentScope->registerDeclaration(*declaration))
|
||||
if (!m_currentScope->registerDeclaration(*declaration, false, false))
|
||||
{
|
||||
SourceLocation firstDeclarationLocation;
|
||||
SourceLocation secondDeclarationLocation;
|
||||
@ -458,19 +505,11 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
_errorLocation = &_declaration.location();
|
||||
|
||||
string name = _name ? *_name : _declaration.name();
|
||||
Declaration const* shadowedDeclaration = nullptr;
|
||||
// Do not warn about shadowing for structs and enums because their members are
|
||||
// not accessible without prefixes. Also do not warn about event parameters
|
||||
// because they do not participate in any proper scope.
|
||||
bool warnOnShadow = !_declaration.isStructMember() && !_declaration.isEnumValue() && !_declaration.isEventParameter();
|
||||
if (warnOnShadow && !name.empty() && _container.enclosingContainer())
|
||||
for (auto const* decl: _container.enclosingContainer()->resolveName(name, true, true))
|
||||
shadowedDeclaration = decl;
|
||||
|
||||
// We use "invisible" for both inactive variables in blocks and for members invisible in contracts.
|
||||
// They cannot both be true at the same time.
|
||||
solAssert(!(_inactive && !_declaration.isVisibleInContract()), "");
|
||||
if (!_container.registerDeclaration(_declaration, _name, !_declaration.isVisibleInContract() || _inactive))
|
||||
if (!_container.registerDeclaration(_declaration, _name, _errorLocation, !_declaration.isVisibleInContract() || _inactive, false))
|
||||
{
|
||||
SourceLocation firstDeclarationLocation;
|
||||
SourceLocation secondDeclarationLocation;
|
||||
@ -499,34 +538,7 @@ bool DeclarationRegistrationHelper::registerDeclaration(
|
||||
);
|
||||
return false;
|
||||
}
|
||||
else if (shadowedDeclaration)
|
||||
{
|
||||
if (dynamic_cast<MagicVariableDeclaration const*>(shadowedDeclaration))
|
||||
_errorReporter.warning(
|
||||
2319_error,
|
||||
*_errorLocation,
|
||||
"This declaration shadows a builtin symbol."
|
||||
);
|
||||
else
|
||||
{
|
||||
auto shadowedLocation = shadowedDeclaration->location();
|
||||
|
||||
if (!shadowedDeclaration->isVisibleInContract())
|
||||
_errorReporter.warning(
|
||||
8760_error,
|
||||
_declaration.location(),
|
||||
"This declaration has the same name as another declaration.",
|
||||
SecondarySourceLocation().append("The other declaration is here:", shadowedLocation)
|
||||
);
|
||||
else
|
||||
_errorReporter.warning(
|
||||
2519_error,
|
||||
_declaration.location(),
|
||||
"This declaration shadows an existing declaration.",
|
||||
SecondarySourceLocation().append("The shadowed declaration is here:", shadowedLocation)
|
||||
);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -556,8 +568,8 @@ bool DeclarationRegistrationHelper::visit(ImportDirective& _import)
|
||||
bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
||||
{
|
||||
m_globalContext.setCurrentContract(_contract);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, false, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, false, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), false, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), false, true);
|
||||
m_currentContract = &_contract;
|
||||
|
||||
return ASTVisitor::visit(_contract);
|
||||
@ -566,8 +578,8 @@ bool DeclarationRegistrationHelper::visit(ContractDefinition& _contract)
|
||||
void DeclarationRegistrationHelper::endVisit(ContractDefinition& _contract)
|
||||
{
|
||||
// make "this" and "super" invisible.
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), nullptr, true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentThis(), true, true);
|
||||
m_scopes[nullptr]->registerDeclaration(*m_globalContext.currentSuper(), true, true);
|
||||
m_globalContext.resetCurrentContract();
|
||||
m_currentContract = nullptr;
|
||||
ASTVisitor::endVisit(_contract);
|
||||
@ -612,11 +624,14 @@ void DeclarationRegistrationHelper::endVisitNode(ASTNode& _node)
|
||||
|
||||
void DeclarationRegistrationHelper::enterNewSubScope(ASTNode& _subScope)
|
||||
{
|
||||
shared_ptr<DeclarationContainer> container{make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())};
|
||||
bool newlyAdded = m_scopes.emplace(&_subScope, move(container)).second;
|
||||
// Source units are the only AST nodes for which containers can be created from multiple places
|
||||
// due to imports.
|
||||
solAssert(newlyAdded || dynamic_cast<SourceUnit const*>(&_subScope), "Unable to add new scope.");
|
||||
if (m_scopes.count(&_subScope))
|
||||
// Source units are the only AST nodes for which containers can be created from multiple places due to imports.
|
||||
solAssert(dynamic_cast<SourceUnit const*>(&_subScope), "Unexpected scope type.");
|
||||
else
|
||||
{
|
||||
bool newlyAdded = m_scopes.emplace(&_subScope, make_shared<DeclarationContainer>(m_currentScope, m_scopes[m_currentScope].get())).second;
|
||||
solAssert(newlyAdded, "Unable to add new scope.");
|
||||
}
|
||||
m_currentScope = &_subScope;
|
||||
}
|
||||
|
||||
|
@ -93,7 +93,10 @@ public:
|
||||
Declaration const* pathFromCurrentScope(std::vector<ASTString> const& _path) const;
|
||||
|
||||
/// Generate and store warnings about variables that are named like instructions.
|
||||
void warnVariablesNamedLikeInstructions();
|
||||
void warnVariablesNamedLikeInstructions() const;
|
||||
|
||||
/// Generate and store warnings about declarations with the same name.
|
||||
void warnHomonymDeclarations() const;
|
||||
|
||||
/// @returns a list of similar identifiers in the current and enclosing scopes. May return empty string if no suggestions.
|
||||
std::string similarNameSuggestions(ASTString const& _name) const;
|
||||
|
@ -78,6 +78,7 @@ public:
|
||||
|
||||
/// Update currently enabled set of experimental features.
|
||||
void setExperimentalFeatures(std::set<ExperimentalFeature> const& _features) { m_experimentalFeatures = _features; }
|
||||
std::set<ExperimentalFeature> const& experimentalFeaturesActive() const { return m_experimentalFeatures; }
|
||||
/// @returns true if the given feature is enabled.
|
||||
bool experimentalFeatureActive(ExperimentalFeature _feature) const { return m_experimentalFeatures.count(_feature); }
|
||||
|
||||
|
@ -1326,9 +1326,14 @@ void ContractCompiler::appendModifierOrFunctionCode()
|
||||
|
||||
if (codeBlock)
|
||||
{
|
||||
std::set<ExperimentalFeature> experimentalFeaturesOutside = m_context.experimentalFeaturesActive();
|
||||
m_context.setExperimentalFeatures(codeBlock->sourceUnit().annotation().experimentalFeatures);
|
||||
|
||||
m_returnTags.emplace_back(m_context.newTag(), m_context.stackHeight());
|
||||
codeBlock->accept(*this);
|
||||
|
||||
m_context.setExperimentalFeatures(experimentalFeaturesOutside);
|
||||
|
||||
solAssert(!m_returnTags.empty(), "");
|
||||
m_context << m_returnTags.back().first;
|
||||
m_returnTags.pop_back();
|
||||
|
@ -1772,6 +1772,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
|
||||
solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, "");
|
||||
utils().pushCombinedFunctionEntryLabel(*function);
|
||||
}
|
||||
else if (auto const* contract = dynamic_cast<ContractDefinition const*>(_memberAccess.annotation().referencedDeclaration))
|
||||
{
|
||||
if (contract->isLibrary())
|
||||
m_context.appendLibraryAddress(contract->fullyQualifiedName());
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
|
@ -86,7 +86,6 @@ public:
|
||||
|
||||
/// @returns the name of a function that performs a left shift and subsequent cleanup
|
||||
/// and, if needed, prior cleanup.
|
||||
/// If the amount to shift by is signed, a check for negativeness is performed.
|
||||
/// signature: (value, amountToShift) -> result
|
||||
std::string typedShiftLeftFunction(Type const& _type, Type const& _amountType);
|
||||
std::string typedShiftRightFunction(Type const& _type, Type const& _amountType);
|
||||
|
@ -396,7 +396,6 @@ void BMC::endVisit(FunctionCall const& _funCall)
|
||||
case FunctionType::Kind::Send:
|
||||
case FunctionType::Kind::Transfer:
|
||||
{
|
||||
SMTEncoder::endVisit(_funCall);
|
||||
auto value = _funCall.arguments().front();
|
||||
solAssert(value, "");
|
||||
smtutil::Expression thisBalance = m_context.state().balance();
|
||||
@ -406,6 +405,8 @@ void BMC::endVisit(FunctionCall const& _funCall)
|
||||
thisBalance < expr(*value),
|
||||
&_funCall
|
||||
);
|
||||
|
||||
SMTEncoder::endVisit(_funCall);
|
||||
break;
|
||||
}
|
||||
case FunctionType::Kind::AddMod:
|
||||
|
@ -101,7 +101,7 @@ bool CHC::visit(ContractDefinition const& _contract)
|
||||
|
||||
solAssert(m_currentContract, "");
|
||||
m_constructorSummaryPredicate = createSymbolicBlock(
|
||||
constructorSort(*m_currentContract),
|
||||
constructorSort(*m_currentContract, state()),
|
||||
"summary_constructor_" + contractSuffix(_contract),
|
||||
PredicateType::ConstructorSummary,
|
||||
&_contract
|
||||
@ -114,15 +114,16 @@ bool CHC::visit(ContractDefinition const& _contract)
|
||||
void CHC::endVisit(ContractDefinition const& _contract)
|
||||
{
|
||||
auto implicitConstructorPredicate = createSymbolicBlock(
|
||||
implicitConstructorSort(),
|
||||
implicitConstructorSort(state()),
|
||||
"implicit_constructor_" + contractSuffix(_contract),
|
||||
PredicateType::ImplicitConstructor,
|
||||
&_contract
|
||||
);
|
||||
auto implicitConstructor = (*implicitConstructorPredicate)({});
|
||||
addRule(implicitConstructor, implicitConstructor.name);
|
||||
m_currentBlock = implicitConstructor;
|
||||
m_context.addAssertion(errorFlag().currentValue() == 0);
|
||||
addRule(
|
||||
(*implicitConstructorPredicate)({0, state().thisAddress(), state().state()}),
|
||||
implicitConstructorPredicate->functor().name
|
||||
);
|
||||
setCurrentBlock(*implicitConstructorPredicate);
|
||||
|
||||
if (auto constructor = _contract.constructor())
|
||||
constructor->accept(*this);
|
||||
@ -178,6 +179,7 @@ bool CHC::visit(FunctionDefinition const& _function)
|
||||
m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var));
|
||||
for (auto const& var: _function.parameters())
|
||||
m_context.addAssertion(m_context.variable(*var)->valueAtIndex(0) == currentValue(*var));
|
||||
m_context.addAssertion(state().state(0) == state().state());
|
||||
|
||||
connectBlocks(functionPred, bodyPred);
|
||||
|
||||
@ -215,7 +217,7 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
||||
string suffix = m_currentContract->name() + "_" + to_string(m_currentContract->id());
|
||||
solAssert(m_currentContract, "");
|
||||
auto constructorExit = createSymbolicBlock(
|
||||
constructorSort(*m_currentContract),
|
||||
constructorSort(*m_currentContract, state()),
|
||||
"constructor_exit_" + suffix,
|
||||
PredicateType::ConstructorSummary,
|
||||
m_currentContract
|
||||
@ -234,7 +236,7 @@ void CHC::endVisit(FunctionDefinition const& _function)
|
||||
|
||||
setCurrentBlock(*m_interfaces.at(m_currentContract));
|
||||
|
||||
auto ifacePre = (*m_interfaces.at(m_currentContract))(initialStateVariables());
|
||||
auto ifacePre = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
||||
if (_function.isPublic())
|
||||
{
|
||||
addAssertVerificationTarget(&_function, ifacePre, sum, assertionError);
|
||||
@ -598,15 +600,21 @@ void CHC::externalFunctionCall(FunctionCall const& _funCall)
|
||||
for (auto var: function->returnParameters())
|
||||
m_context.variable(*var)->increaseIndex();
|
||||
|
||||
auto preCallState = currentStateVariables();
|
||||
auto preCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
|
||||
bool usesStaticCall = kind == FunctionType::Kind::BareStaticCall ||
|
||||
function->stateMutability() == StateMutability::Pure ||
|
||||
function->stateMutability() == StateMutability::View;
|
||||
if (!usesStaticCall)
|
||||
{
|
||||
state().newState();
|
||||
for (auto const* var: m_stateVariables)
|
||||
m_context.variable(*var)->increaseIndex();
|
||||
}
|
||||
|
||||
auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + currentStateVariables());
|
||||
auto postCallState = vector<smtutil::Expression>{state().state()} + currentStateVariables();
|
||||
auto nondet = (*m_nondetInterfaces.at(m_currentContract))(preCallState + postCallState);
|
||||
// TODO this could instead add the summary of the called function, where that summary
|
||||
// basically has the nondet interface of this summary as a constraint.
|
||||
m_context.addAssertion(nondet);
|
||||
|
||||
m_context.addAssertion(errorFlag().currentValue() == 0);
|
||||
@ -775,6 +783,8 @@ void CHC::clearIndices(ContractDefinition const* _contract, FunctionDefinition c
|
||||
for (auto const& var: _function->localVariables())
|
||||
m_context.variable(*var)->increaseIndex();
|
||||
}
|
||||
|
||||
state().newState();
|
||||
}
|
||||
|
||||
void CHC::setCurrentBlock(Predicate const& _block)
|
||||
@ -800,7 +810,7 @@ set<frontend::Expression const*, CHC::IdCompare> CHC::transactionAssertions(ASTN
|
||||
|
||||
SortPointer CHC::sort(FunctionDefinition const& _function)
|
||||
{
|
||||
return functionSort(_function, m_currentContract);
|
||||
return functionSort(_function, m_currentContract, state());
|
||||
}
|
||||
|
||||
SortPointer CHC::sort(ASTNode const* _node)
|
||||
@ -809,7 +819,7 @@ SortPointer CHC::sort(ASTNode const* _node)
|
||||
return sort(*funDef);
|
||||
|
||||
solAssert(m_currentFunction, "");
|
||||
return functionBodySort(*m_currentFunction, m_currentContract);
|
||||
return functionBodySort(*m_currentFunction, m_currentContract, state());
|
||||
}
|
||||
|
||||
Predicate const* CHC::createSymbolicBlock(SortPointer _sort, string const& _name, PredicateType _predType, ASTNode const* _node)
|
||||
@ -825,8 +835,8 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
|
||||
if (auto const* contract = dynamic_cast<ContractDefinition const*>(node.get()))
|
||||
{
|
||||
string suffix = contract->name() + "_" + to_string(contract->id());
|
||||
m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract), "interface_" + suffix, PredicateType::Interface, contract);
|
||||
m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract), "nondet_interface_" + suffix, PredicateType::NondetInterface, contract);
|
||||
m_interfaces[contract] = createSymbolicBlock(interfaceSort(*contract, state()), "interface_" + suffix, PredicateType::Interface, contract);
|
||||
m_nondetInterfaces[contract] = createSymbolicBlock(nondetInterfaceSort(*contract, state()), "nondet_interface_" + suffix, PredicateType::NondetInterface, contract);
|
||||
|
||||
for (auto const* var: stateVariablesIncludingInheritedAndPrivate(*contract))
|
||||
if (!m_context.knownVariable(*var))
|
||||
@ -836,8 +846,7 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
|
||||
/// 0 steps to be taken, used as base for the inductive
|
||||
/// rule for each function.
|
||||
auto const& iface = *m_nondetInterfaces.at(contract);
|
||||
auto state0 = stateVariablesAtIndex(0, *contract);
|
||||
addRule(iface(state0 + state0), "base_nondet");
|
||||
addRule(smt::nondetInterface(iface, *contract, m_context, 0, 0), "base_nondet");
|
||||
|
||||
for (auto const* base: contract->annotation().linearizedBaseContracts)
|
||||
for (auto const* function: base->definedFunctions())
|
||||
@ -861,12 +870,13 @@ void CHC::defineInterfacesAndSummaries(SourceUnit const& _source)
|
||||
auto state1 = stateVariablesAtIndex(1, *contract);
|
||||
auto state2 = stateVariablesAtIndex(2, *contract);
|
||||
|
||||
auto nondetPre = iface(state0 + state1);
|
||||
auto nondetPost = iface(state0 + state2);
|
||||
auto nondetPre = smt::nondetInterface(iface, *contract, m_context, 0, 1);
|
||||
auto nondetPost = smt::nondetInterface(iface, *contract, m_context, 0, 2);
|
||||
|
||||
vector<smtutil::Expression> args{errorFlag().currentValue()};
|
||||
vector<smtutil::Expression> args{errorFlag().currentValue(), state().thisAddress(), state().state(1)};
|
||||
args += state1 +
|
||||
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 0); }) +
|
||||
vector<smtutil::Expression>{state().state(2)} +
|
||||
state2 +
|
||||
applyMap(function->parameters(), [this](auto _var) { return valueAtIndex(*_var, 1); }) +
|
||||
applyMap(function->returnParameters(), [this](auto _var) { return valueAtIndex(*_var, 1); });
|
||||
@ -930,7 +940,7 @@ Predicate const* CHC::createBlock(ASTNode const* _node, PredicateType _predType,
|
||||
Predicate const* CHC::createSummaryBlock(FunctionDefinition const& _function, ContractDefinition const& _contract)
|
||||
{
|
||||
auto block = createSymbolicBlock(
|
||||
functionSort(_function, &_contract),
|
||||
functionSort(_function, &_contract, state()),
|
||||
"summary_" + uniquePrefix() + "_" + predicateName(&_function, &_contract),
|
||||
PredicateType::FunctionSummary,
|
||||
&_function
|
||||
@ -1042,7 +1052,7 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
|
||||
return smtutil::Expression(true);
|
||||
|
||||
errorFlag().increaseIndex();
|
||||
vector<smtutil::Expression> args{errorFlag().currentValue()};
|
||||
vector<smtutil::Expression> args{errorFlag().currentValue(), state().thisAddress(), state().state()};
|
||||
|
||||
FunctionType const& funType = dynamic_cast<FunctionType const&>(*_funCall.expression().annotation().type);
|
||||
solAssert(funType.kind() == FunctionType::Kind::Internal, "");
|
||||
@ -1058,11 +1068,17 @@ smtutil::Expression CHC::predicate(FunctionCall const& _funCall)
|
||||
auto const* calledContract = contract->isLibrary() ? contract : m_currentContract;
|
||||
solAssert(calledContract, "");
|
||||
|
||||
bool usesStaticCall = function->stateMutability() == StateMutability::Pure || function->stateMutability() == StateMutability::View;
|
||||
|
||||
args += currentStateVariables(*calledContract);
|
||||
args += symbolicArguments(_funCall);
|
||||
if (!calledContract->isLibrary())
|
||||
if (!calledContract->isLibrary() && !usesStaticCall)
|
||||
{
|
||||
state().newState();
|
||||
for (auto const& var: m_stateVariables)
|
||||
m_context.variable(*var)->increaseIndex();
|
||||
}
|
||||
args += vector<smtutil::Expression>{state().state()};
|
||||
args += currentStateVariables(*calledContract);
|
||||
|
||||
for (auto var: function->parameters() + function->returnParameters())
|
||||
@ -1152,7 +1168,7 @@ void CHC::addVerificationTarget(ASTNode const* _scope, VerificationTarget::Type
|
||||
addVerificationTarget(_scope, _type, summary(*m_currentContract), smtutil::Expression(true), _errorId);
|
||||
else
|
||||
{
|
||||
auto iface = (*m_interfaces.at(m_currentContract))(initialStateVariables());
|
||||
auto iface = smt::interfacePre(*m_interfaces.at(m_currentContract), *m_currentContract, m_context);
|
||||
auto sum = summary(*m_currentFunction);
|
||||
addVerificationTarget(_scope, _type, iface, sum, _errorId);
|
||||
}
|
||||
@ -1444,7 +1460,12 @@ unsigned CHC::newErrorId(frontend::Expression const& _expr)
|
||||
return errorId;
|
||||
}
|
||||
|
||||
SymbolicState& CHC::state()
|
||||
{
|
||||
return m_context.state();
|
||||
}
|
||||
|
||||
SymbolicIntVariable& CHC::errorFlag()
|
||||
{
|
||||
return m_context.state().errorFlag();
|
||||
return state().errorFlag();
|
||||
}
|
||||
|
@ -235,6 +235,7 @@ private:
|
||||
/// it into m_errorIds.
|
||||
unsigned newErrorId(Expression const& _expr);
|
||||
|
||||
smt::SymbolicState& state();
|
||||
smt::SymbolicIntVariable& errorFlag();
|
||||
//@}
|
||||
|
||||
|
@ -161,9 +161,9 @@ string Predicate::formatSummaryCall(vector<string> const& _args) const
|
||||
auto const* fun = programFunction();
|
||||
solAssert(fun, "");
|
||||
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
|
||||
/// The signature of a function summary predicate is: summary(error, this, preBlockChainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
|
||||
/// Here we are interested in preInputVars.
|
||||
vector<string>::const_iterator first = _args.begin() + static_cast<int>(stateVars->size()) + 1;
|
||||
vector<string>::const_iterator first = _args.begin() + 3 + static_cast<int>(stateVars->size());
|
||||
vector<string>::const_iterator last = first + static_cast<int>(fun->parameters().size());
|
||||
solAssert(first >= _args.begin() && first <= _args.end(), "");
|
||||
solAssert(last >= _args.begin() && last <= _args.end(), "");
|
||||
@ -188,8 +188,8 @@ string Predicate::formatSummaryCall(vector<string> const& _args) const
|
||||
|
||||
vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
|
||||
{
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
|
||||
/// The signature of an implicit constructor summary predicate is: summary(error, postStateVars).
|
||||
/// The signature of a function summary predicate is: summary(error, this, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
|
||||
/// The signature of an implicit constructor summary predicate is: summary(error, this, postBlockchainState, postStateVars).
|
||||
/// Here we are interested in postStateVars.
|
||||
|
||||
auto stateVars = stateVariables();
|
||||
@ -199,12 +199,12 @@ vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
|
||||
vector<string>::const_iterator stateLast;
|
||||
if (auto const* function = programFunction())
|
||||
{
|
||||
stateFirst = _args.begin() + 1 + static_cast<int>(stateVars->size()) + static_cast<int>(function->parameters().size());
|
||||
stateFirst = _args.begin() + 3 + static_cast<int>(stateVars->size()) + static_cast<int>(function->parameters().size()) + 1;
|
||||
stateLast = stateFirst + static_cast<int>(stateVars->size());
|
||||
}
|
||||
else if (programContract())
|
||||
{
|
||||
stateFirst = _args.begin() + 1;
|
||||
stateFirst = _args.begin() + 3;
|
||||
stateLast = stateFirst + static_cast<int>(stateVars->size());
|
||||
}
|
||||
else
|
||||
@ -220,7 +220,7 @@ vector<string> Predicate::summaryStateValues(vector<string> const& _args) const
|
||||
|
||||
vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) const
|
||||
{
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
|
||||
/// The signature of a function summary predicate is: summary(error, this, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
|
||||
/// Here we are interested in postInputVars.
|
||||
auto const* function = programFunction();
|
||||
solAssert(function, "");
|
||||
@ -230,7 +230,7 @@ vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) co
|
||||
|
||||
auto const& inParams = function->parameters();
|
||||
|
||||
vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size());
|
||||
vector<string>::const_iterator first = _args.begin() + 3 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) + 1;
|
||||
vector<string>::const_iterator last = first + static_cast<int>(inParams.size());
|
||||
|
||||
solAssert(first >= _args.begin() && first <= _args.end(), "");
|
||||
@ -243,7 +243,7 @@ vector<string> Predicate::summaryPostInputValues(vector<string> const& _args) co
|
||||
|
||||
vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) const
|
||||
{
|
||||
/// The signature of a function summary predicate is: summary(error, preStateVars, preInputVars, postStateVars, postInputVars, outputVars).
|
||||
/// The signature of a function summary predicate is: summary(error, this, preBlockchainState, preStateVars, preInputVars, postBlockchainState, postStateVars, postInputVars, outputVars).
|
||||
/// Here we are interested in outputVars.
|
||||
auto const* function = programFunction();
|
||||
solAssert(function, "");
|
||||
@ -253,7 +253,7 @@ vector<string> Predicate::summaryPostOutputValues(vector<string> const& _args) c
|
||||
|
||||
auto const& inParams = function->parameters();
|
||||
|
||||
vector<string>::const_iterator first = _args.begin() + 1 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2;
|
||||
vector<string>::const_iterator first = _args.begin() + 3 + static_cast<int>(stateVars->size()) * 2 + static_cast<int>(inParams.size()) * 2 + 1;
|
||||
|
||||
solAssert(first >= _args.begin() && first <= _args.end(), "");
|
||||
|
||||
|
@ -27,10 +27,35 @@ using namespace solidity::smtutil;
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
smtutil::Expression interfacePre(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context)
|
||||
{
|
||||
auto& state = _context.state();
|
||||
vector<smtutil::Expression> stateExprs{state.thisAddress(0), state.state(0)};
|
||||
return _pred(stateExprs + initialStateVariables(_contract, _context));
|
||||
}
|
||||
|
||||
smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context)
|
||||
{
|
||||
return _pred(currentStateVariables(_contract, _context));
|
||||
auto& state = _context.state();
|
||||
vector<smtutil::Expression> stateExprs{state.thisAddress(0), state.state()};
|
||||
return _pred(stateExprs + currentStateVariables(_contract, _context));
|
||||
}
|
||||
|
||||
smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx)
|
||||
{
|
||||
return _pred(
|
||||
vector<smtutil::Expression>{_context.state().state(_preIdx)} +
|
||||
stateVariablesAtIndex(_preIdx, _contract, _context) +
|
||||
vector<smtutil::Expression>{_context.state().state(_postIdx)} +
|
||||
stateVariablesAtIndex(_postIdx, _contract, _context)
|
||||
);
|
||||
}
|
||||
|
||||
smtutil::Expression implicitConstructor(Predicate const& _pred, ContractDefinition const&, EncodingContext& _context)
|
||||
{
|
||||
auto& state = _context.state();
|
||||
vector<smtutil::Expression> stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.state(0)};
|
||||
return _pred(stateExprs);
|
||||
}
|
||||
|
||||
smtutil::Expression constructor(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context)
|
||||
@ -38,16 +63,9 @@ smtutil::Expression constructor(Predicate const& _pred, ContractDefinition const
|
||||
if (auto const* constructor = _contract.constructor())
|
||||
return _pred(currentFunctionVariables(*constructor, &_contract, _context));
|
||||
|
||||
return _pred(
|
||||
vector<smtutil::Expression>{_context.state().errorFlag().currentValue()} +
|
||||
currentStateVariables(_contract, _context)
|
||||
);
|
||||
}
|
||||
|
||||
/// Currently it does not have arguments but it will have tx data in the future.
|
||||
smtutil::Expression implicitConstructor(Predicate const& _pred, ContractDefinition const&, EncodingContext&)
|
||||
{
|
||||
return _pred({});
|
||||
auto& state = _context.state();
|
||||
vector<smtutil::Expression> stateExprs{state.errorFlag().currentValue(), state.thisAddress(0), state.state()};
|
||||
return _pred(stateExprs + currentStateVariables(_contract, _context));
|
||||
}
|
||||
|
||||
smtutil::Expression function(
|
||||
@ -99,9 +117,11 @@ vector<smtutil::Expression> currentFunctionVariables(
|
||||
EncodingContext& _context
|
||||
)
|
||||
{
|
||||
vector<smtutil::Expression> exprs{_context.state().errorFlag().currentValue()};
|
||||
auto& state = _context.state();
|
||||
vector<smtutil::Expression> exprs{_context.state().errorFlag().currentValue(), state.thisAddress(0), state.state(0)};
|
||||
exprs += _contract ? initialStateVariables(*_contract, _context) : vector<smtutil::Expression>{};
|
||||
exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->valueAtIndex(0); });
|
||||
exprs += vector<smtutil::Expression>{state.state()};
|
||||
exprs += _contract ? currentStateVariables(*_contract, _context) : vector<smtutil::Expression>{};
|
||||
exprs += applyMap(_function.parameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); });
|
||||
exprs += applyMap(_function.returnParameters(), [&](auto _var) { return _context.variable(*_var)->currentValue(); });
|
||||
|
@ -30,8 +30,12 @@ class EncodingContext;
|
||||
* The predicates follow the specification in PredicateSort.h.
|
||||
* */
|
||||
|
||||
smtutil::Expression interfacePre(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context);
|
||||
|
||||
smtutil::Expression interface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context);
|
||||
|
||||
smtutil::Expression nondetInterface(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context, unsigned _preIdx, unsigned _postIdx);
|
||||
|
||||
smtutil::Expression constructor(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context);
|
||||
|
||||
smtutil::Expression implicitConstructor(Predicate const& _pred, ContractDefinition const& _contract, EncodingContext& _context);
|
||||
|
@ -28,49 +28,54 @@ using namespace solidity::smtutil;
|
||||
namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
SortPointer interfaceSort(ContractDefinition const& _contract)
|
||||
SortPointer interfaceSort(ContractDefinition const& _contract, SymbolicState& _state)
|
||||
{
|
||||
return make_shared<FunctionSort>(
|
||||
stateSorts(_contract),
|
||||
vector<SortPointer>{_state.thisAddressSort(), _state.stateSort()} + stateSorts(_contract),
|
||||
SortProvider::boolSort
|
||||
);
|
||||
}
|
||||
|
||||
SortPointer nondetInterfaceSort(ContractDefinition const& _contract)
|
||||
SortPointer nondetInterfaceSort(ContractDefinition const& _contract, SymbolicState& _state)
|
||||
{
|
||||
auto varSorts = stateSorts(_contract);
|
||||
vector<SortPointer> stateSort{_state.stateSort()};
|
||||
return make_shared<FunctionSort>(
|
||||
varSorts + varSorts,
|
||||
stateSort + varSorts + stateSort + varSorts,
|
||||
SortProvider::boolSort
|
||||
);
|
||||
}
|
||||
|
||||
SortPointer implicitConstructorSort()
|
||||
SortPointer implicitConstructorSort(SymbolicState& _state)
|
||||
{
|
||||
return arity0FunctionSort();
|
||||
return make_shared<FunctionSort>(
|
||||
vector<SortPointer>{_state.errorFlagSort(), _state.thisAddressSort(), _state.stateSort()},
|
||||
SortProvider::boolSort
|
||||
);
|
||||
}
|
||||
|
||||
SortPointer constructorSort(ContractDefinition const& _contract)
|
||||
SortPointer constructorSort(ContractDefinition const& _contract, SymbolicState& _state)
|
||||
{
|
||||
if (auto const* constructor = _contract.constructor())
|
||||
return functionSort(*constructor, &_contract);
|
||||
return functionSort(*constructor, &_contract, _state);
|
||||
|
||||
return make_shared<FunctionSort>(
|
||||
vector<SortPointer>{SortProvider::uintSort} + stateSorts(_contract),
|
||||
vector<SortPointer>{_state.errorFlagSort(), _state.thisAddressSort(), _state.stateSort()} + stateSorts(_contract),
|
||||
SortProvider::boolSort
|
||||
);
|
||||
}
|
||||
|
||||
SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract)
|
||||
SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state)
|
||||
{
|
||||
auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
|
||||
auto varSorts = _contract ? stateSorts(*_contract) : vector<SortPointer>{};
|
||||
auto inputSorts = applyMap(_function.parameters(), smtSort);
|
||||
auto outputSorts = applyMap(_function.returnParameters(), smtSort);
|
||||
return make_shared<FunctionSort>(
|
||||
vector<SortPointer>{SortProvider::uintSort} +
|
||||
vector<SortPointer>{_state.errorFlagSort(), _state.thisAddressSort(), _state.stateSort()} +
|
||||
varSorts +
|
||||
inputSorts +
|
||||
vector<SortPointer>{_state.stateSort()} +
|
||||
varSorts +
|
||||
inputSorts +
|
||||
outputSorts,
|
||||
@ -78,9 +83,9 @@ SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition
|
||||
);
|
||||
}
|
||||
|
||||
SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract)
|
||||
SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state)
|
||||
{
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(functionSort(_function, _contract));
|
||||
auto fSort = dynamic_pointer_cast<FunctionSort>(functionSort(_function, _contract, _state));
|
||||
solAssert(fSort, "");
|
||||
|
||||
auto smtSort = [](auto _var) { return smt::smtSortAbstractFunction(*_var->type()); };
|
||||
|
@ -20,6 +20,8 @@
|
||||
|
||||
#include <libsolidity/formal/Predicate.h>
|
||||
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
|
||||
#include <libsmtutil/Sorts.h>
|
||||
|
||||
namespace solidity::frontend::smt
|
||||
@ -31,46 +33,46 @@ namespace solidity::frontend::smt
|
||||
*
|
||||
* 1. Interface
|
||||
* The idle state of a contract. Signature:
|
||||
* interface(stateVariables).
|
||||
* interface(this, blockchainState, stateVariables).
|
||||
*
|
||||
* 2. Nondet interface
|
||||
* The nondeterminism behavior of a contract. Signature:
|
||||
* nondet_interface(stateVariables, stateVariables').
|
||||
* nondet_interface(blockchainState, stateVariables, blockchainState', stateVariables').
|
||||
*
|
||||
* 3. Implicit constructor
|
||||
* The implicit constructor of a contract, that is, without input parameters. Signature:
|
||||
* implicit_constructor().
|
||||
* implicit_constructor(error, this, blockchainState).
|
||||
*
|
||||
* 4. Constructor entry/summary
|
||||
* The summary of an implicit constructor. Signature:
|
||||
* constructor_summary(error, stateVariables').
|
||||
* constructor_summary(error, this, blockchainState, blockchainState', stateVariables').
|
||||
*
|
||||
* 5. Function entry/summary
|
||||
* The entry point of a function definition. Signature:
|
||||
* function_entry(error, stateVariables, inputVariables, stateVariables', inputVariables', outputVariables').
|
||||
* function_entry(error, this, blockchainState, stateVariables, inputVariables, blockchainState', stateVariables', inputVariables', outputVariables').
|
||||
*
|
||||
* 6. Function body
|
||||
* Use for any predicate within a function. Signature:
|
||||
* function_body(error, stateVariables, inputVariables, stateVariables', inputVariables', outputVariables', localVariables).
|
||||
* function_body(error, this, blockchainState, stateVariables, inputVariables, blockchainState' ,stateVariables', inputVariables', outputVariables', localVariables).
|
||||
*/
|
||||
|
||||
/// @returns the interface predicate sort for _contract.
|
||||
smtutil::SortPointer interfaceSort(ContractDefinition const& _contract);
|
||||
smtutil::SortPointer interfaceSort(ContractDefinition const& _contract, SymbolicState& _state);
|
||||
|
||||
/// @returns the nondeterminisc interface predicate sort for _contract.
|
||||
smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _contract);
|
||||
smtutil::SortPointer nondetInterfaceSort(ContractDefinition const& _contract, SymbolicState& _state);
|
||||
|
||||
/// @returns the implicit constructor predicate sort.
|
||||
smtutil::SortPointer implicitConstructorSort();
|
||||
smtutil::SortPointer implicitConstructorSort(SymbolicState& _state);
|
||||
|
||||
/// @returns the constructor entry/summary predicate sort for _contract.
|
||||
smtutil::SortPointer constructorSort(ContractDefinition const& _contract);
|
||||
smtutil::SortPointer constructorSort(ContractDefinition const& _contract, SymbolicState& _state);
|
||||
|
||||
/// @returns the function entry/summary predicate sort for _function contained in _contract.
|
||||
smtutil::SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract);
|
||||
smtutil::SortPointer functionSort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state);
|
||||
|
||||
/// @returns the function body predicate sort for _function contained in _contract.
|
||||
smtutil::SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract);
|
||||
smtutil::SortPointer functionBodySort(FunctionDefinition const& _function, ContractDefinition const* _contract, SymbolicState& _state);
|
||||
|
||||
/// @returns the sort of a predicate without parameters.
|
||||
smtutil::SortPointer arity0FunctionSort();
|
||||
|
@ -2162,6 +2162,7 @@ void SMTEncoder::clearIndices(ContractDefinition const* _contract, FunctionDefin
|
||||
for (auto const& var: _function->localVariables())
|
||||
m_context.variable(*var)->resetIndex();
|
||||
}
|
||||
m_context.state().reset();
|
||||
}
|
||||
|
||||
Expression const* SMTEncoder::leftmostBase(IndexAccess const& _indexAccess)
|
||||
|
@ -18,61 +18,118 @@
|
||||
|
||||
#include <libsolidity/formal/SymbolicState.h>
|
||||
|
||||
#include <libsolidity/formal/SymbolicTypes.h>
|
||||
#include <libsolidity/formal/EncodingContext.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace solidity;
|
||||
using namespace solidity::smtutil;
|
||||
using namespace solidity::frontend::smt;
|
||||
|
||||
SymbolicState::SymbolicState(EncodingContext& _context):
|
||||
m_context(_context)
|
||||
{
|
||||
m_stateMembers.emplace("balances", make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort));
|
||||
|
||||
vector<string> members;
|
||||
vector<SortPointer> sorts;
|
||||
for (auto const& [component, sort]: m_stateMembers)
|
||||
{
|
||||
members.emplace_back(component);
|
||||
sorts.emplace_back(sort);
|
||||
m_componentIndices[component] = members.size() - 1;
|
||||
}
|
||||
m_stateTuple = make_unique<SymbolicTupleVariable>(
|
||||
make_shared<smtutil::TupleSort>("state_type", members, sorts),
|
||||
"state",
|
||||
m_context
|
||||
);
|
||||
}
|
||||
|
||||
void SymbolicState::reset()
|
||||
{
|
||||
m_thisAddress.resetIndex();
|
||||
m_balances.resetIndex();
|
||||
m_error.resetIndex();
|
||||
m_thisAddress.resetIndex();
|
||||
m_stateTuple->resetIndex();
|
||||
}
|
||||
|
||||
// Blockchain
|
||||
|
||||
smtutil::Expression SymbolicState::thisAddress()
|
||||
{
|
||||
return m_thisAddress.currentValue();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::balance()
|
||||
{
|
||||
return balance(m_thisAddress.currentValue());
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::balance(smtutil::Expression _address)
|
||||
{
|
||||
return smtutil::Expression::select(m_balances.elements(), move(_address));
|
||||
}
|
||||
|
||||
SymbolicIntVariable& SymbolicState::errorFlag()
|
||||
{
|
||||
return m_error;
|
||||
}
|
||||
|
||||
SortPointer SymbolicState::errorFlagSort()
|
||||
{
|
||||
return m_error.sort();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::thisAddress()
|
||||
{
|
||||
return m_thisAddress.currentValue();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::thisAddress(unsigned _idx)
|
||||
{
|
||||
return m_thisAddress.valueAtIndex(_idx);
|
||||
}
|
||||
|
||||
SortPointer SymbolicState::thisAddressSort()
|
||||
{
|
||||
return m_thisAddress.sort();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::state()
|
||||
{
|
||||
return m_stateTuple->currentValue();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::state(unsigned _idx)
|
||||
{
|
||||
return m_stateTuple->valueAtIndex(_idx);
|
||||
}
|
||||
|
||||
SortPointer SymbolicState::stateSort()
|
||||
{
|
||||
return m_stateTuple->sort();
|
||||
}
|
||||
|
||||
void SymbolicState::newState()
|
||||
{
|
||||
m_stateTuple->increaseIndex();
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::balances()
|
||||
{
|
||||
return m_stateTuple->component(m_componentIndices.at("balances"));
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::balance()
|
||||
{
|
||||
return balance(thisAddress());
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::balance(smtutil::Expression _address)
|
||||
{
|
||||
return smtutil::Expression::select(balances(), move(_address));
|
||||
}
|
||||
|
||||
void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value)
|
||||
{
|
||||
unsigned indexBefore = m_balances.index();
|
||||
unsigned indexBefore = m_stateTuple->index();
|
||||
addBalance(_from, 0 - _value);
|
||||
addBalance(_to, move(_value));
|
||||
unsigned indexAfter = m_balances.index();
|
||||
unsigned indexAfter = m_stateTuple->index();
|
||||
solAssert(indexAfter > indexBefore, "");
|
||||
m_balances.increaseIndex();
|
||||
m_stateTuple->increaseIndex();
|
||||
/// Do not apply the transfer operation if _from == _to.
|
||||
auto newBalances = smtutil::Expression::ite(
|
||||
auto newState = smtutil::Expression::ite(
|
||||
move(_from) == move(_to),
|
||||
m_balances.valueAtIndex(indexBefore),
|
||||
m_balances.valueAtIndex(indexAfter)
|
||||
m_stateTuple->valueAtIndex(indexBefore),
|
||||
m_stateTuple->valueAtIndex(indexAfter)
|
||||
);
|
||||
m_context.addAssertion(m_balances.currentValue() == newBalances);
|
||||
m_context.addAssertion(m_stateTuple->currentValue() == newState);
|
||||
}
|
||||
|
||||
/// Private helpers.
|
||||
@ -80,13 +137,24 @@ void SymbolicState::transfer(smtutil::Expression _from, smtutil::Expression _to,
|
||||
void SymbolicState::addBalance(smtutil::Expression _address, smtutil::Expression _value)
|
||||
{
|
||||
auto newBalances = smtutil::Expression::store(
|
||||
m_balances.elements(),
|
||||
balances(),
|
||||
_address,
|
||||
balance(_address) + move(_value)
|
||||
);
|
||||
auto oldLength = m_balances.length();
|
||||
m_balances.increaseIndex();
|
||||
m_context.addAssertion(m_balances.elements() == newBalances);
|
||||
m_context.addAssertion(m_balances.length() == oldLength);
|
||||
assignStateMember("balances", newBalances);
|
||||
}
|
||||
|
||||
smtutil::Expression SymbolicState::assignStateMember(string const& _member, smtutil::Expression const& _value)
|
||||
{
|
||||
vector<smtutil::Expression> args;
|
||||
for (auto const& member: m_stateMembers)
|
||||
if (member.first == _member)
|
||||
args.emplace_back(_value);
|
||||
else
|
||||
args.emplace_back(m_stateTuple->component(m_componentIndices.at(member.first)));
|
||||
m_stateTuple->increaseIndex();
|
||||
auto tuple = m_stateTuple->currentValue();
|
||||
auto sortExpr = smtutil::Expression(make_shared<smtutil::SortSort>(tuple.sort), tuple.name);
|
||||
m_context.addAssertion(tuple == smtutil::Expression::tuple_constructor(sortExpr, args));
|
||||
return m_stateTuple->currentValue();
|
||||
}
|
||||
|
@ -27,9 +27,17 @@ namespace solidity::frontend::smt
|
||||
{
|
||||
|
||||
class EncodingContext;
|
||||
class SymbolicAddressVariable;
|
||||
class SymbolicArrayVariable;
|
||||
|
||||
/**
|
||||
* Symbolic representation of the blockchain state.
|
||||
* Symbolic representation of the blockchain context:
|
||||
* - error flag
|
||||
* - this (the address of the currently executing contract)
|
||||
* - state, represented as a tuple of:
|
||||
* - balances
|
||||
* - TODO: potentially storage of contracts
|
||||
* - TODO transaction variables
|
||||
*/
|
||||
class SymbolicState
|
||||
{
|
||||
@ -40,15 +48,27 @@ public:
|
||||
|
||||
/// Blockchain.
|
||||
//@{
|
||||
/// Value of `this` address.
|
||||
SymbolicIntVariable& errorFlag();
|
||||
smtutil::SortPointer errorFlagSort();
|
||||
|
||||
/// @returns the symbolic value of the currently executing contract's address.
|
||||
smtutil::Expression thisAddress();
|
||||
smtutil::Expression thisAddress(unsigned _idx);
|
||||
smtutil::SortPointer thisAddressSort();
|
||||
|
||||
/// @returns the state as a tuple.
|
||||
smtutil::Expression state();
|
||||
smtutil::Expression state(unsigned _idx);
|
||||
smtutil::SortPointer stateSort();
|
||||
void newState();
|
||||
|
||||
/// @returns the symbolic balances.
|
||||
smtutil::Expression balances();
|
||||
/// @returns the symbolic balance of address `this`.
|
||||
smtutil::Expression balance();
|
||||
/// @returns the symbolic balance of an address.
|
||||
smtutil::Expression balance(smtutil::Expression _address);
|
||||
|
||||
SymbolicIntVariable& errorFlag();
|
||||
|
||||
/// Transfer _value from _from to _to.
|
||||
void transfer(smtutil::Expression _from, smtutil::Expression _to, smtutil::Expression _value);
|
||||
//@}
|
||||
@ -57,27 +77,27 @@ private:
|
||||
/// Adds _value to _account's balance.
|
||||
void addBalance(smtutil::Expression _account, smtutil::Expression _value);
|
||||
|
||||
/// Generates a new tuple where _member is assigned _value.
|
||||
smtutil::Expression assignStateMember(std::string const& _member, smtutil::Expression const& _value);
|
||||
|
||||
EncodingContext& m_context;
|
||||
|
||||
/// Symbolic `this` address.
|
||||
SymbolicAddressVariable m_thisAddress{
|
||||
"this",
|
||||
m_context
|
||||
};
|
||||
|
||||
/// Symbolic balances.
|
||||
SymbolicArrayVariable m_balances{
|
||||
std::make_shared<smtutil::ArraySort>(smtutil::SortProvider::uintSort, smtutil::SortProvider::uintSort),
|
||||
"balances",
|
||||
m_context
|
||||
};
|
||||
|
||||
smt::SymbolicIntVariable m_error{
|
||||
SymbolicIntVariable m_error{
|
||||
TypeProvider::uint256(),
|
||||
TypeProvider::uint256(),
|
||||
"error",
|
||||
m_context
|
||||
};
|
||||
|
||||
SymbolicAddressVariable m_thisAddress{
|
||||
"this",
|
||||
m_context
|
||||
};
|
||||
|
||||
std::map<std::string, unsigned> m_componentIndices;
|
||||
/// balances, TODO storage of other contracts
|
||||
std::map<std::string, smtutil::SortPointer> m_stateMembers;
|
||||
std::unique_ptr<SymbolicTupleVariable> m_stateTuple;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -343,6 +343,8 @@ bool CompilerStack::analyze()
|
||||
if (source->ast && !resolver.performImports(*source->ast, sourceUnitsByName))
|
||||
return false;
|
||||
|
||||
resolver.warnHomonymDeclarations();
|
||||
|
||||
for (Source const* source: m_sourceOrder)
|
||||
if (source->ast && !resolver.resolveNamesAndTypes(*source->ast))
|
||||
return false;
|
||||
|
@ -0,0 +1,32 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
uint a;
|
||||
uint[2] b;
|
||||
uint c;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {
|
||||
return Data(5, [uint(66), 77], 8);
|
||||
}
|
||||
}
|
||||
|
||||
contract B {
|
||||
function foo(A _a) public returns (uint) {
|
||||
return _a.get().b[1];
|
||||
}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {
|
||||
function test() public returns (uint) {
|
||||
return foo(new A());
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// test() -> 77
|
@ -0,0 +1,38 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
uint value;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {
|
||||
return Data(5);
|
||||
}
|
||||
}
|
||||
|
||||
contract B {
|
||||
uint x = 10;
|
||||
uint y = 10;
|
||||
|
||||
modifier updateStorage() {
|
||||
A a = new A();
|
||||
x = a.get().value;
|
||||
_;
|
||||
y = a.get().value;
|
||||
}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {
|
||||
function test()
|
||||
public
|
||||
updateStorage
|
||||
returns (uint, uint)
|
||||
{
|
||||
return (x, y);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// test() -> 5, 10
|
@ -14,5 +14,7 @@ contract C {
|
||||
two = data.x;
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// f(uint256): 7 -> 7, 8
|
||||
|
@ -0,0 +1,15 @@
|
||||
contract Base {
|
||||
function f(uint n) public returns (uint) {
|
||||
return 2 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Child is Base {
|
||||
function g(uint n) public returns (uint) {
|
||||
return f(n);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g(uint256): 4 -> 8
|
@ -0,0 +1,29 @@
|
||||
contract BaseBase {
|
||||
function f(uint n) public virtual returns (uint) {
|
||||
return 2 * n;
|
||||
}
|
||||
function s(uint n) public returns (uint) {
|
||||
return 4 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Base is BaseBase {
|
||||
function f(uint n) public virtual override returns (uint) {
|
||||
return 3 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Child is Base {
|
||||
function g(uint n) public returns (uint) {
|
||||
return f(n);
|
||||
}
|
||||
|
||||
function h(uint n) public returns (uint) {
|
||||
return s(n);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g(uint256): 4 -> 12
|
||||
// h(uint256): 4 -> 16
|
@ -0,0 +1,32 @@
|
||||
contract BaseBase {
|
||||
function f(uint n) public virtual returns (uint) {
|
||||
return 2 * n;
|
||||
}
|
||||
|
||||
function s(uint n) public returns (uint) {
|
||||
return 4 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Base is BaseBase {
|
||||
function f(uint n) public virtual override returns (uint) {
|
||||
return 3 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Child is Base {
|
||||
function g(uint n) public returns (uint) {
|
||||
// calling base-bse function of a virtual overridden function.
|
||||
return BaseBase.f(n);
|
||||
}
|
||||
|
||||
function k(uint n) public returns (uint) {
|
||||
// Calling base-base function of a non-virtual function.
|
||||
return BaseBase.s(n);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g(uint256): 4 -> 8
|
||||
// k(uint256): 4 -> 16
|
@ -0,0 +1,15 @@
|
||||
contract Base {
|
||||
function f(uint n) public returns (uint) {
|
||||
return 2 * n;
|
||||
}
|
||||
}
|
||||
|
||||
contract Child is Base {
|
||||
function g(uint n) public returns (uint) {
|
||||
return Base.f(n);
|
||||
}
|
||||
}
|
||||
// ====
|
||||
// compileViaYul: also
|
||||
// ----
|
||||
// g(uint256): 4 -> 8
|
@ -0,0 +1,20 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
uint t;
|
||||
constructor() {
|
||||
t = address(this).balance;
|
||||
}
|
||||
function f(address payable a, uint x) public {
|
||||
require(address(this).balance >= x);
|
||||
a.transfer(x);
|
||||
}
|
||||
function inv() public view {
|
||||
// If only looking at `f`, it looks like this.balance always decreases.
|
||||
// However, the edge case of a contract `selfdestruct` sending its remaining balance
|
||||
// to this contract should make the claim false (since there's no fallback/receive here).
|
||||
assert(address(this).balance == t);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (496-530): CHC: Assertion violation happens here.
|
@ -0,0 +1,12 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
address t;
|
||||
constructor() {
|
||||
t = address(this);
|
||||
}
|
||||
function inv() public view {
|
||||
assert(address(this) == t);
|
||||
}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,18 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
abstract contract D {
|
||||
function d() external virtual;
|
||||
}
|
||||
|
||||
contract C {
|
||||
address t;
|
||||
constructor() {
|
||||
t = address(this);
|
||||
}
|
||||
function f(D d) public {
|
||||
address a = address(this);
|
||||
d.d();
|
||||
assert(address(this) == t);
|
||||
assert(a == t);
|
||||
}
|
||||
}
|
@ -0,0 +1,15 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
address t;
|
||||
constructor() {
|
||||
t = address(this);
|
||||
}
|
||||
function f() public view {
|
||||
g(address(this));
|
||||
}
|
||||
function g(address a) internal view {
|
||||
assert(a == t);
|
||||
assert(a == address(this));
|
||||
}
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
pragma experimental SMTChecker;
|
||||
|
||||
contract C {
|
||||
function f(address payable a) public {
|
||||
require(address(this).balance > 1000);
|
||||
a.transfer(666);
|
||||
assert(address(this).balance > 100);
|
||||
// Fails.
|
||||
assert(address(this).balance > 500);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6328: (199-234): CHC: Assertion violation happens here.
|
@ -17,4 +17,3 @@ contract C
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 4661: (297-321): BMC: Assertion violation happens here.
|
||||
|
@ -15,3 +15,4 @@ contract C
|
||||
}
|
||||
// ----
|
||||
// Warning 2661: (176-181): BMC: Overflow (resulting value larger than 2**256 - 1) happens here.
|
||||
// Warning 4661: (296-309): BMC: Assertion violation happens here.
|
||||
|
@ -11,6 +11,7 @@ contract C {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (128-159): This declaration shadows an existing declaration.
|
||||
// Warning 6328: (207-227): CHC: Assertion violation happens here.
|
||||
// Warning 6328: (231-245): CHC: Assertion violation happens here.
|
||||
// Warning 5729: (214-218): Assertion checker does not yet implement this type of function call.
|
||||
|
@ -23,8 +23,6 @@ contract C {
|
||||
assert(s1[2].a[2] == s2.a[2]);
|
||||
s1[0].ts[3].y = 5;
|
||||
assert(s1[0].ts[3].y == s2.ts[3].y);
|
||||
s1[1].ts[4].a[5] = 6;
|
||||
assert(s1[1].ts[4].a[5] == s2.ts[4].a[5]);
|
||||
}
|
||||
}
|
||||
// ----
|
||||
@ -32,4 +30,3 @@ contract C {
|
||||
// Warning 6328: (327-354): CHC: Assertion violation happens here.
|
||||
// Warning 6328: (376-405): CHC: Assertion violation happens here.
|
||||
// Warning 6328: (430-465): CHC: Assertion violation happens here.
|
||||
// Warning 6328: (493-534): CHC: Assertion violation happens here.
|
||||
|
@ -0,0 +1,15 @@
|
||||
struct Item {
|
||||
uint x;
|
||||
uint y;
|
||||
}
|
||||
|
||||
contract D {
|
||||
Item[][][] public items;
|
||||
|
||||
function test() public view returns (uint) {
|
||||
// The autogenerated getters to not use ABI encoder.
|
||||
(uint a, uint b) = this.items(1, 2, 3);
|
||||
return a + b;
|
||||
}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,17 @@
|
||||
struct Item {
|
||||
uint x;
|
||||
uint y;
|
||||
}
|
||||
|
||||
contract D {
|
||||
Item[][][] public items;
|
||||
|
||||
function test() public view returns (uint) {
|
||||
// The autogenerated getters to not use ABI encoder.
|
||||
Item memory item = this.items(1, 2, 3);
|
||||
return item.x + item.y;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 7364: (202-240): Different number of components on the left hand side (1) than on the right hand side (2).
|
||||
// TypeError 9574: (202-240): Type uint256 is not implicitly convertible to expected type struct Item memory.
|
@ -0,0 +1,22 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
library L {
|
||||
struct Item {
|
||||
uint x;
|
||||
}
|
||||
|
||||
function f(uint) external view returns (Item memory) {}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract D {
|
||||
using L for uint;
|
||||
|
||||
function test() public {
|
||||
uint(1).f();
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// TypeError 2428: (B:86-97): The type of return parameter 1, struct L.Item, is only supported in ABIEncoderV2. Use "pragma experimental ABIEncoderV2;" to enable the feature.
|
@ -0,0 +1,27 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
bool flag;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {}
|
||||
}
|
||||
|
||||
contract B {
|
||||
modifier validate() {
|
||||
A(0x00).get();
|
||||
_;
|
||||
}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {
|
||||
function foo()
|
||||
public
|
||||
validate()
|
||||
{}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,33 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
bool flag;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {}
|
||||
}
|
||||
|
||||
contract B {
|
||||
constructor() validate {
|
||||
A(0x00).get();
|
||||
}
|
||||
|
||||
modifier validate() {
|
||||
A(0x00).get();
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {}
|
||||
==== Source: C ====
|
||||
import "B";
|
||||
|
||||
contract D is C {
|
||||
constructor() validate B() validate C() validate {}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,25 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
bool flag;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {}
|
||||
}
|
||||
|
||||
contract B {
|
||||
constructor() {
|
||||
A(0x00).get();
|
||||
}
|
||||
|
||||
function foo() public view {
|
||||
A(0x00).get();
|
||||
}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {}
|
||||
// ----
|
@ -0,0 +1,21 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Item {
|
||||
uint x;
|
||||
}
|
||||
|
||||
library L {
|
||||
event Ev(Item);
|
||||
}
|
||||
|
||||
contract C {
|
||||
function foo() public {
|
||||
emit L.Ev(Item(1));
|
||||
}
|
||||
}
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract D is C {}
|
||||
// ----
|
@ -0,0 +1,29 @@
|
||||
==== Source: A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
bool flag;
|
||||
}
|
||||
|
||||
contract A {
|
||||
function get() public view returns (Data memory) {}
|
||||
}
|
||||
|
||||
contract B {
|
||||
modifier validate() virtual {
|
||||
A(0x00).get();
|
||||
_;
|
||||
}
|
||||
}
|
||||
|
||||
==== Source: B ====
|
||||
import "A";
|
||||
|
||||
contract C is B {
|
||||
function foo() public pure validate {}
|
||||
|
||||
modifier validate() override {
|
||||
_;
|
||||
}
|
||||
}
|
||||
// ----
|
@ -0,0 +1,52 @@
|
||||
==== Source: C ====
|
||||
import "X";
|
||||
import "V1A";
|
||||
import "V2A";
|
||||
import "V1B";
|
||||
|
||||
contract C is V1A, V2A, V1B {
|
||||
function foo()
|
||||
public
|
||||
modV1A
|
||||
modV2A // There should be no error for modV2A (it uses ABIEncoderV2)
|
||||
modV1B
|
||||
{
|
||||
}
|
||||
}
|
||||
==== Source: V1A ====
|
||||
import "X";
|
||||
|
||||
contract V1A {
|
||||
modifier modV1A() {
|
||||
_;
|
||||
}
|
||||
}
|
||||
==== Source: V1B ====
|
||||
import "X";
|
||||
|
||||
contract V1B {
|
||||
modifier modV1B() {
|
||||
_;
|
||||
}
|
||||
}
|
||||
==== Source: V2A ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
import "X";
|
||||
|
||||
contract V2A {
|
||||
modifier modV2A() {
|
||||
X(0x00).get();
|
||||
_;
|
||||
}
|
||||
}
|
||||
==== Source: X ====
|
||||
pragma experimental ABIEncoderV2;
|
||||
|
||||
struct Data {
|
||||
bool flag;
|
||||
}
|
||||
|
||||
contract X {
|
||||
function get() public view returns (Data memory) {}
|
||||
}
|
||||
// ----
|
@ -8,4 +8,5 @@ contract test {
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (31-37): This declaration shadows an existing declaration.
|
||||
// Warning 2519: (39-45): This declaration shadows an existing declaration.
|
||||
// TypeError 6995: (159-160): Duplicate named argument "a".
|
||||
|
@ -8,4 +8,5 @@ contract test {
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (31-37): This declaration shadows an existing declaration.
|
||||
// Warning 2519: (39-45): This declaration shadows an existing declaration.
|
||||
// TypeError 6160: (153-158): Wrong argument count for function call: 0 arguments given but expected 2.
|
||||
|
@ -8,4 +8,5 @@ contract test {
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (31-37): This declaration shadows an existing declaration.
|
||||
// Warning 2519: (39-45): This declaration shadows an existing declaration.
|
||||
// TypeError 6160: (153-162): Wrong argument count for function call: 1 arguments given but expected 2.
|
||||
|
@ -0,0 +1,15 @@
|
||||
==== Source: s1.sol ====
|
||||
import "s1.sol" as A;
|
||||
|
||||
library L {
|
||||
function f() internal pure {}
|
||||
}
|
||||
|
||||
contract C
|
||||
{
|
||||
function test() public pure {
|
||||
A.L;
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 6133: (s1.sol:127-130): Statement has no effect.
|
@ -5,5 +5,6 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (57-63): This declaration shadows an existing declaration.
|
||||
// Warning 2072: (57-63): Unused local variable.
|
||||
// Warning 2072: (75-81): Unused local variable.
|
||||
|
@ -6,5 +6,5 @@ contract test {
|
||||
}
|
||||
}
|
||||
// ----
|
||||
// Warning 2519: (73-79): This declaration shadows an existing declaration.
|
||||
// DeclarationError 2333: (91-97): Identifier already declared.
|
||||
// Warning 2519: (73-79): This declaration shadows an existing declaration.
|
||||
|
@ -0,0 +1,7 @@
|
||||
contract test {
|
||||
function e() external { }
|
||||
function f() public pure { uint e; e = 0; }
|
||||
function e(int) external { }
|
||||
}
|
||||
// ----
|
||||
// Warning 8760: (77-83): This declaration has the same name as another declaration.
|
10
test/libsolidity/syntaxTests/scoping/name_shadowing3.sol
Normal file
10
test/libsolidity/syntaxTests/scoping/name_shadowing3.sol
Normal file
@ -0,0 +1,10 @@
|
||||
function e() {}
|
||||
contract test {
|
||||
function f() pure public { uint e; uint g; uint h; e = g = h = 0; }
|
||||
function g() pure public {}
|
||||
}
|
||||
function h() {}
|
||||
// ----
|
||||
// Warning 2519: (63-69): This declaration shadows an existing declaration.
|
||||
// Warning 2519: (71-77): This declaration shadows an existing declaration.
|
||||
// Warning 2519: (79-85): This declaration shadows an existing declaration.
|
Loading…
Reference in New Issue
Block a user