aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.circleci/config.yml3
-rw-r--r--Changelog.md5
-rw-r--r--libsolidity/CMakeLists.txt1
-rw-r--r--libsolidity/analysis/ContractLevelChecker.cpp452
-rw-r--r--libsolidity/analysis/ContractLevelChecker.h87
-rw-r--r--libsolidity/analysis/TypeChecker.cpp446
-rw-r--r--libsolidity/analysis/TypeChecker.h24
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp6
-rw-r--r--libsolidity/formal/SolverInterface.h3
-rw-r--r--libsolidity/interface/CompilerStack.cpp18
-rwxr-xr-xscripts/install_deps.sh2
-rw-r--r--test/libsolidity/SolidityEndToEndTest.cpp20
-rw-r--r--test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol15
-rw-r--r--test/libsolidity/syntaxTests/deprecated_functions.sol4
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol8
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol11
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol9
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol8
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol11
-rw-r--r--test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol9
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/dynamic_inline_array.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion_strings.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_int_conversion.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_string_conversion.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type_strings.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_fixed_types.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol8
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/inline_array_rationals.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol)0
-rw-r--r--test/libsolidity/syntaxTests/inline_arrays/lvalues_as_inline_array.sol (renamed from test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol)0
-rw-r--r--test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol7
-rw-r--r--test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol11
34 files changed, 709 insertions, 459 deletions
diff --git a/.circleci/config.yml b/.circleci/config.yml
index 975d1c7a..6c5f3e85 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -14,6 +14,7 @@ defaults:
command: |
mkdir -p build
cd build
+ [ -n "$COVERAGE" -a "$CIRCLE_BRANCH" != release -a -z "$CIRCLE_TAG" ] && CMAKE_OPTIONS="$CMAKE_OPTIONS -DCOVERAGE=ON"
cmake .. -DCMAKE_BUILD_TYPE=RelWithDebInfo $CMAKE_OPTIONS
make -j4
- run_tests: &run_tests
@@ -122,7 +123,7 @@ jobs:
- image: buildpack-deps:artful
environment:
TERM: xterm
- CMAKE_OPTIONS: -DCOVERAGE=ON
+ COVERAGE: "ON"
steps:
- checkout
- run:
diff --git a/Changelog.md b/Changelog.md
index f64ae184..630801e4 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -18,10 +18,15 @@ Bugfixes:
* Assembly output: Do not mix in/out jump annotations with arguments.
* Commandline interface: Fix crash when using ``--ast`` on empty runtime code.
* Code Generator: Annotate jump from calldata decoder to function as "jump in".
+ * Code Generator: Fix internal error related to state variables of function type access via base contract name.
* Optimizer: Fix nondeterminism bug related to the boost version and constants representation. The bug only resulted in less optimal but still correct code because the generated routine is always verified to be correct.
* Type Checker: Properly detect different return types when overriding an external interface function with a public contract function.
* Type Checker: Disallow struct return types for getters of public state variables unless the new ABI encoder is active.
* Type Checker: Fix internal compiler error when a field of a struct used as a parameter in a function type has a non-existent type.
+ * Type Checker: Disallow functions ``sha3`` and ``suicide`` also without a function call.
+ * Type Checker: Fixed internal error when trying to create abstract contract in some cases.
+ * Type Checker: Fixed internal error related to double declaration of events.
+ * Type Checker: Disallow inline arrays of mapping type.
Build System:
* Emscripten: Upgrade to Emscripten SDK 1.37.21 and boost 1.67.
diff --git a/libsolidity/CMakeLists.txt b/libsolidity/CMakeLists.txt
index d2e0c854..dc4c6d15 100644
--- a/libsolidity/CMakeLists.txt
+++ b/libsolidity/CMakeLists.txt
@@ -1,6 +1,7 @@
# Until we have a clear separation, libyul has to be included here
set(sources
analysis/ConstantEvaluator.cpp
+ analysis/ContractLevelChecker.cpp
analysis/ControlFlowAnalyzer.cpp
analysis/ControlFlowBuilder.cpp
analysis/ControlFlowGraph.cpp
diff --git a/libsolidity/analysis/ContractLevelChecker.cpp b/libsolidity/analysis/ContractLevelChecker.cpp
new file mode 100644
index 00000000..58dcfe4d
--- /dev/null
+++ b/libsolidity/analysis/ContractLevelChecker.cpp
@@ -0,0 +1,452 @@
+/*
+ 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 verifies overloads, abstract contracts, function clashes and others
+ * checks at contract or function level.
+ */
+
+#include <libsolidity/analysis/ContractLevelChecker.h>
+#include <libsolidity/ast/AST.h>
+
+#include <liblangutil/ErrorReporter.h>
+
+#include <boost/range/adaptor/reversed.hpp>
+
+
+using namespace std;
+using namespace dev;
+using namespace langutil;
+using namespace dev::solidity;
+
+
+bool ContractLevelChecker::check(ContractDefinition const& _contract)
+{
+ checkDuplicateFunctions(_contract);
+ checkDuplicateEvents(_contract);
+ checkIllegalOverrides(_contract);
+ checkAbstractFunctions(_contract);
+ checkBaseConstructorArguments(_contract);
+ checkConstructor(_contract);
+ checkFallbackFunction(_contract);
+ checkExternalTypeClashes(_contract);
+ checkHashCollisions(_contract);
+ checkLibraryRequirements(_contract);
+
+ return Error::containsOnlyWarnings(m_errorReporter.errors());
+}
+
+void ContractLevelChecker::checkDuplicateFunctions(ContractDefinition const& _contract)
+{
+ /// Checks that two functions with the same name defined in this contract have different
+ /// argument types and that there is at most one constructor.
+ map<string, vector<FunctionDefinition const*>> functions;
+ FunctionDefinition const* constructor = nullptr;
+ FunctionDefinition const* fallback = nullptr;
+ for (FunctionDefinition const* function: _contract.definedFunctions())
+ if (function->isConstructor())
+ {
+ if (constructor)
+ m_errorReporter.declarationError(
+ function->location(),
+ SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
+ "More than one constructor defined."
+ );
+ constructor = function;
+ }
+ else if (function->isFallback())
+ {
+ if (fallback)
+ m_errorReporter.declarationError(
+ function->location(),
+ SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
+ "Only one fallback function is allowed."
+ );
+ fallback = function;
+ }
+ else
+ {
+ solAssert(!function->name().empty(), "");
+ functions[function->name()].push_back(function);
+ }
+
+ findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
+}
+
+void ContractLevelChecker::checkDuplicateEvents(ContractDefinition const& _contract)
+{
+ /// Checks that two events with the same name defined in this contract have different
+ /// argument types
+ map<string, vector<EventDefinition const*>> events;
+ for (EventDefinition const* event: _contract.events())
+ events[event->name()].push_back(event);
+
+ findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
+}
+
+template <class T>
+void ContractLevelChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
+{
+ for (auto const& it: _definitions)
+ {
+ vector<T> const& overloads = it.second;
+ set<size_t> reported;
+ for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
+ {
+ SecondarySourceLocation ssl;
+
+ for (size_t j = i + 1; j < overloads.size(); ++j)
+ if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes(
+ *FunctionType(*overloads[j]).asCallableFunction(false))
+ )
+ {
+ ssl.append("Other declaration is here:", overloads[j]->location());
+ reported.insert(j);
+ }
+
+ if (ssl.infos.size() > 0)
+ {
+ ssl.limitSize(_message);
+
+ m_errorReporter.declarationError(
+ overloads[i]->location(),
+ ssl,
+ _message
+ );
+ }
+ }
+ }
+}
+
+void ContractLevelChecker::checkIllegalOverrides(ContractDefinition const& _contract)
+{
+ // TODO unify this at a later point. for this we need to put the constness and the access specifier
+ // into the types
+ map<string, vector<FunctionDefinition const*>> functions;
+ map<string, ModifierDefinition const*> modifiers;
+
+ // We search from derived to base, so the stored item causes the error.
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (FunctionDefinition const* function: contract->definedFunctions())
+ {
+ if (function->isConstructor())
+ continue; // constructors can neither be overridden nor override anything
+ string const& name = function->name();
+ if (modifiers.count(name))
+ m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
+
+ for (FunctionDefinition const* overriding: functions[name])
+ checkFunctionOverride(*overriding, *function);
+
+ functions[name].push_back(function);
+ }
+ for (ModifierDefinition const* modifier: contract->functionModifiers())
+ {
+ string const& name = modifier->name();
+ ModifierDefinition const*& override = modifiers[name];
+ if (!override)
+ override = modifier;
+ else if (ModifierType(*override) != ModifierType(*modifier))
+ m_errorReporter.typeError(override->location(), "Override changes modifier signature.");
+ if (!functions[name].empty())
+ m_errorReporter.typeError(override->location(), "Override changes modifier to function.");
+ }
+ }
+}
+
+void ContractLevelChecker::checkFunctionOverride(FunctionDefinition const& _function, FunctionDefinition const& _super)
+{
+ FunctionTypePointer functionType = FunctionType(_function).asCallableFunction(false);
+ FunctionTypePointer superType = FunctionType(_super).asCallableFunction(false);
+
+ if (!functionType->hasEqualParameterTypes(*superType))
+ return;
+ if (!functionType->hasEqualReturnTypes(*superType))
+ overrideError(_function, _super, "Overriding function return types differ.");
+
+ if (!_function.annotation().superFunction)
+ _function.annotation().superFunction = &_super;
+
+ if (_function.visibility() != _super.visibility())
+ {
+ // Visibility change from external to public is fine.
+ // Any other change is disallowed.
+ if (!(
+ _super.visibility() == FunctionDefinition::Visibility::External &&
+ _function.visibility() == FunctionDefinition::Visibility::Public
+ ))
+ overrideError(_function, _super, "Overriding function visibility differs.");
+ }
+ if (_function.stateMutability() != _super.stateMutability())
+ overrideError(
+ _function,
+ _super,
+ "Overriding function changes state mutability from \"" +
+ stateMutabilityToString(_super.stateMutability()) +
+ "\" to \"" +
+ stateMutabilityToString(_function.stateMutability()) +
+ "\"."
+ );
+}
+
+void ContractLevelChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
+{
+ m_errorReporter.typeError(
+ function.location(),
+ SecondarySourceLocation().append("Overridden function is here:", super.location()),
+ message
+ );
+}
+
+void ContractLevelChecker::checkAbstractFunctions(ContractDefinition const& _contract)
+{
+ // Mapping from name to function definition (exactly one per argument type equality class) and
+ // flag to indicate whether it is fully implemented.
+ using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
+ map<string, vector<FunTypeAndFlag>> functions;
+
+ // Search from base to derived
+ for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
+ for (FunctionDefinition const* function: contract->definedFunctions())
+ {
+ // Take constructors out of overload hierarchy
+ if (function->isConstructor())
+ continue;
+ auto& overloads = functions[function->name()];
+ FunctionTypePointer funType = make_shared<FunctionType>(*function)->asCallableFunction(false);
+ auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
+ {
+ return funType->hasEqualParameterTypes(*_funAndFlag.first);
+ });
+ if (it == overloads.end())
+ overloads.push_back(make_pair(funType, function->isImplemented()));
+ else if (it->second)
+ {
+ if (!function->isImplemented())
+ m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract");
+ }
+ else if (function->isImplemented())
+ it->second = true;
+ }
+
+ // Set to not fully implemented if at least one flag is false.
+ for (auto const& it: functions)
+ for (auto const& funAndFlag: it.second)
+ if (!funAndFlag.second)
+ {
+ FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
+ solAssert(function, "");
+ _contract.annotation().unimplementedFunctions.push_back(function);
+ break;
+ }
+}
+
+
+void ContractLevelChecker::checkBaseConstructorArguments(ContractDefinition const& _contract)
+{
+ vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
+
+ // Determine the arguments that are used for the base constructors.
+ for (ContractDefinition const* contract: bases)
+ {
+ if (FunctionDefinition const* constructor = contract->constructor())
+ for (auto const& modifier: constructor->modifiers())
+ if (auto baseContract = dynamic_cast<ContractDefinition const*>(
+ modifier->name()->annotation().referencedDeclaration
+ ))
+ {
+ if (modifier->arguments())
+ {
+ if (baseContract->constructor())
+ annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
+ }
+ else
+ m_errorReporter.declarationError(
+ modifier->location(),
+ "Modifier-style base constructor call without arguments."
+ );
+ }
+
+ for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
+ {
+ ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
+ base->name().annotation().referencedDeclaration
+ );
+ solAssert(baseContract, "");
+
+ if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
+ annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
+ }
+ }
+
+ // check that we get arguments for all base constructors that need it.
+ // If not mark the contract as abstract (not fully implemented)
+ for (ContractDefinition const* contract: bases)
+ if (FunctionDefinition const* constructor = contract->constructor())
+ if (contract != &_contract && !constructor->parameters().empty())
+ if (!_contract.annotation().baseConstructorArguments.count(constructor))
+ _contract.annotation().unimplementedFunctions.push_back(constructor);
+}
+
+void ContractLevelChecker::annotateBaseConstructorArguments(
+ ContractDefinition const& _currentContract,
+ FunctionDefinition const* _baseConstructor,
+ ASTNode const* _argumentNode
+)
+{
+ solAssert(_baseConstructor, "");
+ solAssert(_argumentNode, "");
+
+ auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
+ std::make_pair(_baseConstructor, _argumentNode)
+ );
+ if (!insertionResult.second)
+ {
+ ASTNode const* previousNode = insertionResult.first->second;
+
+ SourceLocation const* mainLocation = nullptr;
+ SecondarySourceLocation ssl;
+
+ if (
+ _currentContract.location().contains(previousNode->location()) ||
+ _currentContract.location().contains(_argumentNode->location())
+ )
+ {
+ mainLocation = &previousNode->location();
+ ssl.append("Second constructor call is here:", _argumentNode->location());
+ }
+ else
+ {
+ mainLocation = &_currentContract.location();
+ ssl.append("First constructor call is here: ", _argumentNode->location());
+ ssl.append("Second constructor call is here: ", previousNode->location());
+ }
+
+ m_errorReporter.declarationError(
+ *mainLocation,
+ ssl,
+ "Base constructor arguments given twice."
+ );
+ }
+
+}
+
+void ContractLevelChecker::checkConstructor(ContractDefinition const& _contract)
+{
+ FunctionDefinition const* constructor = _contract.constructor();
+ if (!constructor)
+ return;
+
+ if (!constructor->returnParameters().empty())
+ m_errorReporter.typeError(constructor->returnParameterList()->location(), "Non-empty \"returns\" directive for constructor.");
+ if (constructor->stateMutability() != StateMutability::NonPayable && constructor->stateMutability() != StateMutability::Payable)
+ m_errorReporter.typeError(
+ constructor->location(),
+ "Constructor must be payable or non-payable, but is \"" +
+ stateMutabilityToString(constructor->stateMutability()) +
+ "\"."
+ );
+ if (constructor->visibility() != FunctionDefinition::Visibility::Public && constructor->visibility() != FunctionDefinition::Visibility::Internal)
+ m_errorReporter.typeError(constructor->location(), "Constructor must be public or internal.");
+}
+
+void ContractLevelChecker::checkFallbackFunction(ContractDefinition const& _contract)
+{
+ FunctionDefinition const* fallback = _contract.fallbackFunction();
+ if (!fallback)
+ return;
+
+ if (_contract.isLibrary())
+ m_errorReporter.typeError(fallback->location(), "Libraries cannot have fallback functions.");
+ if (fallback->stateMutability() != StateMutability::NonPayable && fallback->stateMutability() != StateMutability::Payable)
+ m_errorReporter.typeError(
+ fallback->location(),
+ "Fallback function must be payable or non-payable, but is \"" +
+ stateMutabilityToString(fallback->stateMutability()) +
+ "\"."
+ );
+ if (!fallback->parameters().empty())
+ m_errorReporter.typeError(fallback->parameterList().location(), "Fallback function cannot take parameters.");
+ if (!fallback->returnParameters().empty())
+ m_errorReporter.typeError(fallback->returnParameterList()->location(), "Fallback function cannot return values.");
+ if (fallback->visibility() != FunctionDefinition::Visibility::External)
+ m_errorReporter.typeError(fallback->location(), "Fallback function must be defined as \"external\".");
+}
+
+void ContractLevelChecker::checkExternalTypeClashes(ContractDefinition const& _contract)
+{
+ map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
+ for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
+ {
+ for (FunctionDefinition const* f: contract->definedFunctions())
+ if (f->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*f);
+ // under non error circumstances this should be true
+ if (functionType->interfaceFunctionType())
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(f, functionType->asCallableFunction(false))
+ );
+ }
+ for (VariableDeclaration const* v: contract->stateVariables())
+ if (v->isPartOfExternalInterface())
+ {
+ auto functionType = make_shared<FunctionType>(*v);
+ // under non error circumstances this should be true
+ if (functionType->interfaceFunctionType())
+ externalDeclarations[functionType->externalSignature()].push_back(
+ make_pair(v, functionType->asCallableFunction(false))
+ );
+ }
+ }
+ for (auto const& it: externalDeclarations)
+ for (size_t i = 0; i < it.second.size(); ++i)
+ for (size_t j = i + 1; j < it.second.size(); ++j)
+ if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
+ m_errorReporter.typeError(
+ it.second[j].first->location(),
+ "Function overload clash during conversion to external types for arguments."
+ );
+}
+
+void ContractLevelChecker::checkHashCollisions(ContractDefinition const& _contract)
+{
+ set<FixedHash<4>> hashes;
+ for (auto const& it: _contract.interfaceFunctionList())
+ {
+ FixedHash<4> const& hash = it.first;
+ if (hashes.count(hash))
+ m_errorReporter.typeError(
+ _contract.location(),
+ string("Function signature hash collision for ") + it.second->externalSignature()
+ );
+ hashes.insert(hash);
+ }
+}
+
+void ContractLevelChecker::checkLibraryRequirements(ContractDefinition const& _contract)
+{
+ if (!_contract.isLibrary())
+ return;
+
+ if (!_contract.baseContracts().empty())
+ m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
+
+ for (auto const& var: _contract.stateVariables())
+ if (!var->isConstant())
+ m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
+}
diff --git a/libsolidity/analysis/ContractLevelChecker.h b/libsolidity/analysis/ContractLevelChecker.h
new file mode 100644
index 00000000..15cbf45d
--- /dev/null
+++ b/libsolidity/analysis/ContractLevelChecker.h
@@ -0,0 +1,87 @@
+/*
+ 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 verifies overloads, abstract contracts, function clashes and others
+ * checks at contract or function level.
+ */
+
+#pragma once
+
+#include <libsolidity/ast/ASTForward.h>
+
+#include <map>
+
+namespace langutil
+{
+class ErrorReporter;
+}
+
+namespace dev
+{
+namespace solidity
+{
+
+/**
+ * Component that verifies overloads, abstract contracts, function clashes and others
+ * checks at contract or function level.
+ */
+class ContractLevelChecker
+{
+public:
+ /// @param _errorReporter provides the error logging functionality.
+ explicit ContractLevelChecker(langutil::ErrorReporter& _errorReporter):
+ m_errorReporter(_errorReporter)
+ {}
+
+ /// Performs checks on the given contract.
+ /// @returns true iff all checks passed. Note even if all checks passed, errors() can still contain warnings
+ bool check(ContractDefinition const& _contract);
+
+private:
+ /// Checks that two functions defined in this contract with the same name have different
+ /// arguments and that there is at most one constructor.
+ void checkDuplicateFunctions(ContractDefinition const& _contract);
+ void checkDuplicateEvents(ContractDefinition const& _contract);
+ template <class T>
+ void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
+ void checkIllegalOverrides(ContractDefinition const& _contract);
+ /// Reports a type error with an appropriate message if overridden function signature differs.
+ /// Also stores the direct super function in the AST annotations.
+ void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
+ void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
+ void checkAbstractFunctions(ContractDefinition const& _contract);
+ void checkBaseConstructorArguments(ContractDefinition const& _contract);
+ void annotateBaseConstructorArguments(
+ ContractDefinition const& _currentContract,
+ FunctionDefinition const* _baseConstructor,
+ ASTNode const* _argumentNode
+ );
+ void checkConstructor(ContractDefinition const& _contract);
+ void checkFallbackFunction(ContractDefinition const& _contract);
+ /// Checks that different functions with external visibility end up having different
+ /// external argument types (i.e. different signature).
+ void checkExternalTypeClashes(ContractDefinition const& _contract);
+ /// Checks for hash collisions in external function signatures.
+ void checkHashCollisions(ContractDefinition const& _contract);
+ /// Checks that all requirements for a library are fulfilled if this is a library.
+ void checkLibraryRequirements(ContractDefinition const& _contract);
+
+ langutil::ErrorReporter& m_errorReporter;
+};
+
+}
+}
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index fcc6746f..9350df05 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -35,7 +35,6 @@
#include <boost/algorithm/cxx11/all_of.hpp>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string/join.hpp>
-#include <boost/range/adaptor/reversed.hpp>
#include <memory>
#include <vector>
@@ -89,417 +88,14 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
{
m_scope = &_contract;
- // We force our own visiting order here. The structs have to be excluded below.
- set<ASTNode const*> visited;
- for (auto const& s: _contract.definedStructs())
- visited.insert(s);
- ASTNode::listAccept(_contract.definedStructs(), *this);
ASTNode::listAccept(_contract.baseContracts(), *this);
- checkContractDuplicateFunctions(_contract);
- checkContractDuplicateEvents(_contract);
- checkContractIllegalOverrides(_contract);
- checkContractAbstractFunctions(_contract);
- checkContractBaseConstructorArguments(_contract);
-
- FunctionDefinition const* function = _contract.constructor();
- if (function)
- {
- 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)
- m_errorReporter.typeError(
- function->location(),
- "Constructor must be payable or non-payable, but is \"" +
- stateMutabilityToString(function->stateMutability()) +
- "\"."
- );
- if (function->visibility() != FunctionDefinition::Visibility::Public && function->visibility() != FunctionDefinition::Visibility::Internal)
- m_errorReporter.typeError(function->location(), "Constructor must be public or internal.");
- }
-
- for (FunctionDefinition const* function: _contract.definedFunctions())
- if (function->isFallback())
- {
- if (_contract.isLibrary())
- m_errorReporter.typeError(function->location(), "Libraries cannot have fallback functions.");
- if (function->stateMutability() != StateMutability::NonPayable && function->stateMutability() != StateMutability::Payable)
- m_errorReporter.typeError(
- function->location(),
- "Fallback function must be payable or non-payable, but is \"" +
- stateMutabilityToString(function->stateMutability()) +
- "\"."
- );
- if (!function->parameters().empty())
- m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters.");
- if (!function->returnParameters().empty())
- m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values.");
- if (function->visibility() != FunctionDefinition::Visibility::External)
- m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\".");
- }
-
for (auto const& n: _contract.subNodes())
- if (!visited.count(n.get()))
- n->accept(*this);
-
- checkContractExternalTypeClashes(_contract);
- // check for hash collisions in function signatures
- set<FixedHash<4>> hashes;
- for (auto const& it: _contract.interfaceFunctionList())
- {
- FixedHash<4> const& hash = it.first;
- if (hashes.count(hash))
- m_errorReporter.typeError(
- _contract.location(),
- string("Function signature hash collision for ") + it.second->externalSignature()
- );
- hashes.insert(hash);
- }
-
- if (_contract.isLibrary())
- checkLibraryRequirements(_contract);
+ n->accept(*this);
return false;
}
-void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _contract)
-{
- /// Checks that two functions with the same name defined in this contract have different
- /// argument types and that there is at most one constructor.
- map<string, vector<FunctionDefinition const*>> functions;
- FunctionDefinition const* constructor = nullptr;
- FunctionDefinition const* fallback = nullptr;
- for (FunctionDefinition const* function: _contract.definedFunctions())
- if (function->isConstructor())
- {
- if (constructor)
- m_errorReporter.declarationError(
- function->location(),
- SecondarySourceLocation().append("Another declaration is here:", constructor->location()),
- "More than one constructor defined."
- );
- constructor = function;
- }
- else if (function->isFallback())
- {
- if (fallback)
- m_errorReporter.declarationError(
- function->location(),
- SecondarySourceLocation().append("Another declaration is here:", fallback->location()),
- "Only one fallback function is allowed."
- );
- fallback = function;
- }
- else
- {
- solAssert(!function->name().empty(), "");
- functions[function->name()].push_back(function);
- }
-
- findDuplicateDefinitions(functions, "Function with same name and arguments defined twice.");
-}
-
-void TypeChecker::checkContractDuplicateEvents(ContractDefinition const& _contract)
-{
- /// Checks that two events with the same name defined in this contract have different
- /// argument types
- map<string, vector<EventDefinition const*>> events;
- for (EventDefinition const* event: _contract.events())
- events[event->name()].push_back(event);
-
- findDuplicateDefinitions(events, "Event with same name and arguments defined twice.");
-}
-
-template <class T>
-void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _definitions, string _message)
-{
- for (auto const& it: _definitions)
- {
- vector<T> const& overloads = it.second;
- set<size_t> reported;
- for (size_t i = 0; i < overloads.size() && !reported.count(i); ++i)
- {
- SecondarySourceLocation ssl;
-
- for (size_t j = i + 1; j < overloads.size(); ++j)
- if (FunctionType(*overloads[i]).asCallableFunction(false)->hasEqualParameterTypes(
- *FunctionType(*overloads[j]).asCallableFunction(false))
- )
- {
- ssl.append("Other declaration is here:", overloads[j]->location());
- reported.insert(j);
- }
-
- if (ssl.infos.size() > 0)
- {
- ssl.limitSize(_message);
-
- m_errorReporter.declarationError(
- overloads[i]->location(),
- ssl,
- _message
- );
- }
- }
- }
-}
-
-void TypeChecker::checkContractAbstractFunctions(ContractDefinition const& _contract)
-{
- // Mapping from name to function definition (exactly one per argument type equality class) and
- // flag to indicate whether it is fully implemented.
- using FunTypeAndFlag = std::pair<FunctionTypePointer, bool>;
- map<string, vector<FunTypeAndFlag>> functions;
-
- // Search from base to derived
- for (ContractDefinition const* contract: boost::adaptors::reverse(_contract.annotation().linearizedBaseContracts))
- for (FunctionDefinition const* function: contract->definedFunctions())
- {
- // Take constructors out of overload hierarchy
- if (function->isConstructor())
- continue;
- auto& overloads = functions[function->name()];
- FunctionTypePointer funType = make_shared<FunctionType>(*function)->asCallableFunction(false);
- auto it = find_if(overloads.begin(), overloads.end(), [&](FunTypeAndFlag const& _funAndFlag)
- {
- return funType->hasEqualParameterTypes(*_funAndFlag.first);
- });
- if (it == overloads.end())
- overloads.push_back(make_pair(funType, function->isImplemented()));
- else if (it->second)
- {
- if (!function->isImplemented())
- m_errorReporter.typeError(function->location(), "Redeclaring an already implemented function as abstract");
- }
- else if (function->isImplemented())
- it->second = true;
- }
-
- // Set to not fully implemented if at least one flag is false.
- for (auto const& it: functions)
- for (auto const& funAndFlag: it.second)
- if (!funAndFlag.second)
- {
- FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(&funAndFlag.first->declaration());
- solAssert(function, "");
- _contract.annotation().unimplementedFunctions.push_back(function);
- break;
- }
-}
-
-void TypeChecker::checkContractBaseConstructorArguments(ContractDefinition const& _contract)
-{
- vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
-
- // Determine the arguments that are used for the base constructors.
- for (ContractDefinition const* contract: bases)
- {
- if (FunctionDefinition const* constructor = contract->constructor())
- for (auto const& modifier: constructor->modifiers())
- if (auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(*modifier->name())))
- {
- if (modifier->arguments())
- {
- if (baseContract->constructor())
- annotateBaseConstructorArguments(_contract, baseContract->constructor(), modifier.get());
- }
- else
- m_errorReporter.declarationError(
- modifier->location(),
- "Modifier-style base constructor call without arguments."
- );
- }
-
- for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
- {
- auto baseContract = dynamic_cast<ContractDefinition const*>(&dereference(base->name()));
- solAssert(baseContract, "");
-
- if (baseContract->constructor() && base->arguments() && !base->arguments()->empty())
- annotateBaseConstructorArguments(_contract, baseContract->constructor(), base.get());
- }
- }
-
- // check that we get arguments for all base constructors that need it.
- // If not mark the contract as abstract (not fully implemented)
- for (ContractDefinition const* contract: bases)
- if (FunctionDefinition const* constructor = contract->constructor())
- if (contract != &_contract && !constructor->parameters().empty())
- if (!_contract.annotation().baseConstructorArguments.count(constructor))
- _contract.annotation().unimplementedFunctions.push_back(constructor);
-}
-
-void TypeChecker::annotateBaseConstructorArguments(
- ContractDefinition const& _currentContract,
- FunctionDefinition const* _baseConstructor,
- ASTNode const* _argumentNode
-)
-{
- solAssert(_baseConstructor, "");
- solAssert(_argumentNode, "");
-
- auto insertionResult = _currentContract.annotation().baseConstructorArguments.insert(
- std::make_pair(_baseConstructor, _argumentNode)
- );
- if (!insertionResult.second)
- {
- ASTNode const* previousNode = insertionResult.first->second;
-
- SourceLocation const* mainLocation = nullptr;
- SecondarySourceLocation ssl;
-
- if (
- _currentContract.location().contains(previousNode->location()) ||
- _currentContract.location().contains(_argumentNode->location())
- )
- {
- mainLocation = &previousNode->location();
- ssl.append("Second constructor call is here:", _argumentNode->location());
- }
- else
- {
- mainLocation = &_currentContract.location();
- ssl.append("First constructor call is here: ", _argumentNode->location());
- ssl.append("Second constructor call is here: ", previousNode->location());
- }
-
- m_errorReporter.declarationError(
- *mainLocation,
- ssl,
- "Base constructor arguments given twice."
- );
- }
-
-}
-
-void TypeChecker::checkContractIllegalOverrides(ContractDefinition const& _contract)
-{
- // TODO unify this at a later point. for this we need to put the constness and the access specifier
- // into the types
- map<string, vector<FunctionDefinition const*>> functions;
- map<string, ModifierDefinition const*> modifiers;
-
- // We search from derived to base, so the stored item causes the error.
- for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
- {
- for (FunctionDefinition const* function: contract->definedFunctions())
- {
- if (function->isConstructor())
- continue; // constructors can neither be overridden nor override anything
- string const& name = function->name();
- if (modifiers.count(name))
- m_errorReporter.typeError(modifiers[name]->location(), "Override changes function to modifier.");
-
- for (FunctionDefinition const* overriding: functions[name])
- checkFunctionOverride(*overriding, *function);
-
- functions[name].push_back(function);
- }
- for (ModifierDefinition const* modifier: contract->functionModifiers())
- {
- string const& name = modifier->name();
- ModifierDefinition const*& override = modifiers[name];
- if (!override)
- override = modifier;
- else if (ModifierType(*override) != ModifierType(*modifier))
- m_errorReporter.typeError(override->location(), "Override changes modifier signature.");
- if (!functions[name].empty())
- m_errorReporter.typeError(override->location(), "Override changes modifier to function.");
- }
- }
-}
-
-void TypeChecker::checkFunctionOverride(FunctionDefinition const& _function, FunctionDefinition const& _super)
-{
- FunctionTypePointer functionType = FunctionType(_function).asCallableFunction(false);
- FunctionTypePointer superType = FunctionType(_super).asCallableFunction(false);
-
- if (!functionType->hasEqualParameterTypes(*superType))
- return;
- if (!functionType->hasEqualReturnTypes(*superType))
- overrideError(_function, _super, "Overriding function return types differ.");
-
- if (!_function.annotation().superFunction)
- _function.annotation().superFunction = &_super;
-
- if (_function.visibility() != _super.visibility())
- {
- // Visibility change from external to public is fine.
- // Any other change is disallowed.
- if (!(
- _super.visibility() == FunctionDefinition::Visibility::External &&
- _function.visibility() == FunctionDefinition::Visibility::Public
- ))
- overrideError(_function, _super, "Overriding function visibility differs.");
- }
- if (_function.stateMutability() != _super.stateMutability())
- overrideError(
- _function,
- _super,
- "Overriding function changes state mutability from \"" +
- stateMutabilityToString(_super.stateMutability()) +
- "\" to \"" +
- stateMutabilityToString(_function.stateMutability()) +
- "\"."
- );
-}
-
-void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefinition const& super, string message)
-{
- m_errorReporter.typeError(
- function.location(),
- SecondarySourceLocation().append("Overridden function is here:", super.location()),
- message
- );
-}
-
-void TypeChecker::checkContractExternalTypeClashes(ContractDefinition const& _contract)
-{
- map<string, vector<pair<Declaration const*, FunctionTypePointer>>> externalDeclarations;
- for (ContractDefinition const* contract: _contract.annotation().linearizedBaseContracts)
- {
- for (FunctionDefinition const* f: contract->definedFunctions())
- if (f->isPartOfExternalInterface())
- {
- auto functionType = make_shared<FunctionType>(*f);
- // under non error circumstances this should be true
- if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(f, functionType->asCallableFunction(false))
- );
- }
- for (VariableDeclaration const* v: contract->stateVariables())
- if (v->isPartOfExternalInterface())
- {
- auto functionType = make_shared<FunctionType>(*v);
- // under non error circumstances this should be true
- if (functionType->interfaceFunctionType())
- externalDeclarations[functionType->externalSignature()].push_back(
- make_pair(v, functionType->asCallableFunction(false))
- );
- }
- }
- for (auto const& it: externalDeclarations)
- for (size_t i = 0; i < it.second.size(); ++i)
- for (size_t j = i + 1; j < it.second.size(); ++j)
- if (!it.second[i].second->hasEqualParameterTypes(*it.second[j].second))
- m_errorReporter.typeError(
- it.second[j].first->location(),
- "Function overload clash during conversion to external types for arguments."
- );
-}
-
-void TypeChecker::checkLibraryRequirements(ContractDefinition const& _contract)
-{
- solAssert(_contract.isLibrary(), "");
- if (!_contract.baseContracts().empty())
- m_errorReporter.typeError(_contract.location(), "Library is not allowed to inherit.");
-
- for (auto const& var: _contract.stateVariables())
- if (!var->isConstant())
- m_errorReporter.typeError(var->location(), "Library cannot have non-constant state variables");
-}
-
void TypeChecker::checkDoubleStorageAssignment(Assignment const& _assignment)
{
TupleType const& lhs = dynamic_cast<TupleType const&>(*type(_assignment.leftHandSide()));
@@ -1608,6 +1204,9 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
if (!inlineArrayType)
m_errorReporter.fatalTypeError(_tuple.location(), "Unable to deduce common type for array elements.");
+ else if (!inlineArrayType->canLiveOutsideStorage())
+ m_errorReporter.fatalTypeError(_tuple.location(), "Type " + inlineArrayType->toString() + " is only valid in storage.");
+
_tuple.annotation().type = make_shared<ArrayType>(DataLocation::Memory, inlineArrayType, types.size());
}
else
@@ -1824,26 +1423,6 @@ void TypeChecker::typeCheckFunctionCall(
"\"staticcall\" is not supported by the VM version."
);
- // Check for deprecated function names
- if (_functionType->kind() == FunctionType::Kind::KECCAK256)
- {
- if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
- if (functionName->name() == "sha3")
- m_errorReporter.typeError(
- _functionCall.location(),
- "\"sha3\" has been deprecated in favour of \"keccak256\""
- );
- }
- else if (_functionType->kind() == FunctionType::Kind::Selfdestruct)
- {
- if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
- if (functionName->name() == "suicide")
- m_errorReporter.typeError(
- _functionCall.location(),
- "\"suicide\" has been deprecated in favour of \"selfdestruct\""
- );
- }
-
// Check for event outside of emit statement
if (!m_insideEmitStatement && _functionType->kind() == FunctionType::Kind::Event)
m_errorReporter.typeError(
@@ -2639,6 +2218,23 @@ bool TypeChecker::visit(Identifier const& _identifier)
else if (dynamic_cast<MagicVariableDeclaration const*>(annotation.referencedDeclaration))
if (dynamic_cast<FunctionType const*>(annotation.type.get()))
annotation.isPure = true;
+
+ // Check for deprecated function names.
+ // The check is done here for the case without an actual function call.
+ if (FunctionType const* fType = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
+ {
+ if (_identifier.name() == "sha3" && fType->kind() == FunctionType::Kind::KECCAK256)
+ m_errorReporter.typeError(
+ _identifier.location(),
+ "\"sha3\" has been deprecated in favour of \"keccak256\""
+ );
+ else if (_identifier.name() == "suicide" && fType->kind() == FunctionType::Kind::Selfdestruct)
+ m_errorReporter.typeError(
+ _identifier.location(),
+ "\"suicide\" has been deprecated in favour of \"selfdestruct\""
+ );
+ }
+
return false;
}
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index c98a4c7f..ebfcdadc 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -66,27 +66,6 @@ public:
private:
bool visit(ContractDefinition const& _contract) override;
- /// Checks that two functions defined in this contract with the same name have different
- /// arguments and that there is at most one constructor.
- void checkContractDuplicateFunctions(ContractDefinition const& _contract);
- void checkContractDuplicateEvents(ContractDefinition const& _contract);
- void checkContractIllegalOverrides(ContractDefinition const& _contract);
- /// Reports a type error with an appropriate message if overridden function signature differs.
- /// Also stores the direct super function in the AST annotations.
- void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
- void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
- void checkContractAbstractFunctions(ContractDefinition const& _contract);
- void checkContractBaseConstructorArguments(ContractDefinition const& _contract);
- void annotateBaseConstructorArguments(
- ContractDefinition const& _currentContract,
- FunctionDefinition const* _baseConstructor,
- ASTNode const* _argumentNode
- );
- /// Checks that different functions with external visibility end up having different
- /// external argument types (i.e. different signature).
- void checkContractExternalTypeClashes(ContractDefinition const& _contract);
- /// Checks that all requirements for a library are fulfilled if this is a library.
- void checkLibraryRequirements(ContractDefinition const& _contract);
/// Checks (and warns) if a tuple assignment might cause unexpected overwrites in storage.
/// Should only be called if the left hand side is tuple-typed.
void checkDoubleStorageAssignment(Assignment const& _assignment);
@@ -157,9 +136,6 @@ private:
void endVisit(ElementaryTypeNameExpression const& _expr) override;
void endVisit(Literal const& _literal) override;
- template <class T>
- void findDuplicateDefinitions(std::map<std::string, std::vector<T>> const& _definitions, std::string _message);
-
bool contractDependenciesAreCyclic(
ContractDefinition const& _contract,
std::set<ContractDefinition const*> const& _seenContracts = std::set<ContractDefinition const*>()
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 87eecd2e..121585d9 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -1153,7 +1153,9 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
if (dynamic_cast<ContractType const*>(type->actualType().get()))
{
solAssert(_memberAccess.annotation().type, "_memberAccess has no type");
- if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
+ if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
+ appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
+ else if (auto funType = dynamic_cast<FunctionType const*>(_memberAccess.annotation().type.get()))
{
switch (funType->kind())
{
@@ -1199,8 +1201,6 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
{
// no-op
}
- else if (auto variable = dynamic_cast<VariableDeclaration const*>(_memberAccess.annotation().referencedDeclaration))
- appendVariable(*variable, static_cast<Expression const&>(_memberAccess));
else
_memberAccess.expression().accept(*this);
}
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index cc8214de..7f20876e 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -136,9 +136,10 @@ public:
static Expression ite(Expression _condition, Expression _trueValue, Expression _falseValue)
{
solAssert(*_trueValue.sort == *_falseValue.sort, "");
+ SortPointer sort = _trueValue.sort;
return Expression("ite", std::vector<Expression>{
std::move(_condition), std::move(_trueValue), std::move(_falseValue)
- }, _trueValue.sort);
+ }, std::move(sort));
}
static Expression implies(Expression _a, Expression _b)
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index de4a7ec2..623ccca8 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -28,6 +28,7 @@
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/parsing/Parser.h>
+#include <libsolidity/analysis/ContractLevelChecker.h>
#include <libsolidity/analysis/ControlFlowAnalyzer.h>
#include <libsolidity/analysis/ControlFlowGraph.h>
#include <libsolidity/analysis/GlobalContext.h>
@@ -225,8 +226,21 @@ bool CompilerStack::analyze()
m_contracts[contract->fullyQualifiedName()].contract = contract;
}
- // This cannot be done in the above loop, because cross-contract types couldn't be resolved.
- // A good example is `LibraryName.TypeName x;`.
+ // 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
+ // type checker.
+ ContractLevelChecker contractLevelChecker(m_errorReporter);
+ for (Source const* source: m_sourceOrder)
+ for (ASTPointer<ASTNode> const& node: source->ast->nodes())
+ if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
+ if (!contractLevelChecker.check(*contract))
+ noErrors = false;
+
+ // New we run full type checks that go down to the expression level. This
+ // cannot be done earlier, because we need cross-contract types and information
+ // about whether a contract is abstract for the `new` expression.
+ // This populates the `type` annotation for all expressions.
//
// Note: this does not resolve overloaded functions. In order to do that, types of arguments are needed,
// which is only done one step later.
diff --git a/scripts/install_deps.sh b/scripts/install_deps.sh
index b107f7c9..09d5a249 100755
--- a/scripts/install_deps.sh
+++ b/scripts/install_deps.sh
@@ -55,7 +55,7 @@ detect_linux_distro() {
DISTRO=$(lsb_release -is)
elif [ -f /etc/os-release ]; then
# extract 'foo' from NAME=foo, only on the line with NAME=foo
- DISTRO=$(sed -n -e 's/^NAME="\(.*\)\"/\1/p' /etc/os-release)
+ DISTRO=$(sed -n -e 's/^NAME="\?\([^"]*\)"\?$/\1/p' /etc/os-release)
elif [ -f /etc/centos-release ]; then
DISTRO=CentOS
else
diff --git a/test/libsolidity/SolidityEndToEndTest.cpp b/test/libsolidity/SolidityEndToEndTest.cpp
index 05bb7446..c6135a72 100644
--- a/test/libsolidity/SolidityEndToEndTest.cpp
+++ b/test/libsolidity/SolidityEndToEndTest.cpp
@@ -14213,6 +14213,26 @@ BOOST_AUTO_TEST_CASE(external_public_override)
ABI_CHECK(callContractFunction("f()"), encodeArgs(2));
ABI_CHECK(callContractFunction("g()"), encodeArgs(2));
}
+
+BOOST_AUTO_TEST_CASE(base_access_to_function_type_variables)
+{
+ char const* sourceCode = R"(
+ contract C {
+ function () internal returns (uint) x;
+ function set() public {
+ C.x = g;
+ }
+ function g() public pure returns (uint) { return 2; }
+ function h() public returns (uint) { return C.x(); }
+ }
+ )";
+ compileAndRun(sourceCode);
+ ABI_CHECK(callContractFunction("g()"), encodeArgs(2));
+ ABI_CHECK(callContractFunction("h()"), encodeArgs());
+ ABI_CHECK(callContractFunction("set()"), encodeArgs());
+ ABI_CHECK(callContractFunction("h()"), encodeArgs(2));
+}
+
BOOST_AUTO_TEST_SUITE_END()
}
diff --git a/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol b/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol
new file mode 100644
index 00000000..2e6aeaa5
--- /dev/null
+++ b/test/libsolidity/syntaxTests/constructor/abstract_creation_forward_reference.sol
@@ -0,0 +1,15 @@
+// This used to cause an internal error because of the visitation order.
+contract Test {
+ function createChild() public {
+ Child asset = new Child();
+ }
+}
+
+contract Parent {
+ constructor(address _address) public {}
+}
+
+contract Child is Parent {
+}
+// ----
+// TypeError: (146-155): Trying to create an instance of an abstract contract.
diff --git a/test/libsolidity/syntaxTests/deprecated_functions.sol b/test/libsolidity/syntaxTests/deprecated_functions.sol
index 62dfcff9..c5764e96 100644
--- a/test/libsolidity/syntaxTests/deprecated_functions.sol
+++ b/test/libsolidity/syntaxTests/deprecated_functions.sol
@@ -8,5 +8,5 @@ contract test {
}
}
// ----
-// TypeError: (58-66): "sha3" has been deprecated in favour of "keccak256"
-// TypeError: (101-152): "suicide" has been deprecated in favour of "selfdestruct"
+// TypeError: (58-62): "sha3" has been deprecated in favour of "keccak256"
+// TypeError: (101-108): "suicide" has been deprecated in favour of "selfdestruct"
diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol
new file mode 100644
index 00000000..37b60e5e
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_no_call.sol
@@ -0,0 +1,8 @@
+contract C
+{
+ function f(bytes memory data) public pure {
+ sha3;
+ }
+}
+// ----
+// TypeError: (60-64): "sha3" has been deprecated in favour of "keccak256"
diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol
new file mode 100644
index 00000000..909c2dc3
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_override.sol
@@ -0,0 +1,11 @@
+contract C
+{
+ function sha3() public pure returns (bool) {
+ return true;
+ }
+ function f() public pure returns (bool) {
+ return sha3();
+ }
+}
+// ----
+// Warning: (14-76): This declaration shadows a builtin symbol.
diff --git a/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol b/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol
new file mode 100644
index 00000000..19ee72d9
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/sha3_var.sol
@@ -0,0 +1,9 @@
+contract C
+{
+ function f() public pure returns (bool) {
+ bool sha3 = true;
+ return sha3;
+ }
+}
+// ----
+// Warning: (58-67): This declaration shadows a builtin symbol.
diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol
new file mode 100644
index 00000000..bf3f5ebc
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_no_call.sol
@@ -0,0 +1,8 @@
+contract C
+{
+ function f(bytes memory data) public pure {
+ suicide;
+ }
+}
+// ----
+// TypeError: (60-67): "suicide" has been deprecated in favour of "selfdestruct"
diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol
new file mode 100644
index 00000000..7350da39
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_override.sol
@@ -0,0 +1,11 @@
+contract C
+{
+ function suicide() public pure returns (bool) {
+ return true;
+ }
+ function f() public pure returns (bool) {
+ return suicide();
+ }
+}
+// ----
+// Warning: (14-79): This declaration shadows a builtin symbol.
diff --git a/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol b/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol
new file mode 100644
index 00000000..3549a563
--- /dev/null
+++ b/test/libsolidity/syntaxTests/globalFunctions/suicide_var.sol
@@ -0,0 +1,9 @@
+contract C
+{
+ function f() public pure returns (bool) {
+ bool suicide = true;
+ return suicide;
+ }
+}
+// ----
+// Warning: (58-70): This declaration shadows a builtin symbol.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/dynamic_inline_array.sol
index e613758b..e613758b 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/277_dynamic_inline_array.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/dynamic_inline_array.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion.sol
index 023404f7..023404f7 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/269_inline_array_declaration_and_passing_implicit_conversion.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion_strings.sol
index 025244d3..025244d3 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/270_inline_array_declaration_and_passing_implicit_conversion_strings.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_and_passing_implicit_conversion_strings.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_int_conversion.sol
index e7036bdf..e7036bdf 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/271_inline_array_declaration_const_int_conversion.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_int_conversion.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_string_conversion.sol
index 4e92f6e1..4e92f6e1 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/272_inline_array_declaration_const_string_conversion.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_const_string_conversion.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type.sol
index 4d3e6aed..4d3e6aed 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/273_inline_array_declaration_no_type.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type_strings.sol
index 6d36942d..6d36942d 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/274_inline_array_declaration_no_type_strings.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_declaration_no_type_strings.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_fixed_types.sol
index c46297c3..c46297c3 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/325_inline_array_fixed_types.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_fixed_types.sol
diff --git a/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol
new file mode 100644
index 00000000..59a88130
--- /dev/null
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_of_mapping_type.sol
@@ -0,0 +1,8 @@
+contract C {
+ mapping(int => int) a;
+ function f() public {
+ [a];
+ }
+}
+// ----
+// TypeError: (66-69): Type mapping(int256 => int256) is only valid in storage.
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol b/test/libsolidity/syntaxTests/inline_arrays/inline_array_rationals.sol
index bdc3c2c1..bdc3c2c1 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/326_inline_array_rationals.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/inline_array_rationals.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol
index 03d7266a..03d7266a 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/276_invalid_types_in_inline_array.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/invalid_types_in_inline_array.sol
diff --git a/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol b/test/libsolidity/syntaxTests/inline_arrays/lvalues_as_inline_array.sol
index 5a39f550..5a39f550 100644
--- a/test/libsolidity/syntaxTests/nameAndTypeResolution/278_lvalues_as_inline_array.sol
+++ b/test/libsolidity/syntaxTests/inline_arrays/lvalues_as_inline_array.sol
diff --git a/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol b/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol
new file mode 100644
index 00000000..560a6c2a
--- /dev/null
+++ b/test/libsolidity/syntaxTests/memberLookup/internal_function_type.sol
@@ -0,0 +1,7 @@
+contract C {
+ function () internal returns (uint) x;
+ constructor() public {
+ C.x = g;
+ }
+ function g() public pure returns (uint) {}
+}
diff --git a/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol
new file mode 100644
index 00000000..d2a411ec
--- /dev/null
+++ b/test/libsolidity/syntaxTests/structs/recursion/recursive_struct_forward_reference.sol
@@ -0,0 +1,11 @@
+pragma experimental ABIEncoderV2;
+
+contract C {
+ function f(Data.S memory a) public {}
+}
+contract Data {
+ struct S { S x; }
+}
+// ----
+// Warning: (0-33): Experimental features are turned on. Do not use experimental features on live deployments.
+// TypeError: (63-78): Internal or recursive type is not allowed for public or external functions.