aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/ConstantEvaluator.cpp85
-rw-r--r--libsolidity/analysis/ConstantEvaluator.h18
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp22
-rw-r--r--libsolidity/analysis/DeclarationContainer.h4
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp6
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.h3
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp55
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp14
-rw-r--r--libsolidity/analysis/SyntaxChecker.h2
-rw-r--r--libsolidity/analysis/TypeChecker.cpp42
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp34
-rw-r--r--libsolidity/analysis/ViewPureChecker.h1
-rw-r--r--libsolidity/ast/Types.cpp18
-rw-r--r--libsolidity/ast/Types.h6
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp28
-rw-r--r--libsolidity/codegen/Compiler.cpp1
-rw-r--r--libsolidity/codegen/CompilerContext.cpp12
-rw-r--r--libsolidity/codegen/CompilerContext.h7
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp71
-rw-r--r--libsolidity/codegen/ContractCompiler.h9
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp24
-rw-r--r--libsolidity/formal/SMTChecker.cpp265
-rw-r--r--libsolidity/formal/SMTChecker.h53
-rw-r--r--libsolidity/formal/SolverInterface.h9
-rw-r--r--libsolidity/formal/Z3Interface.cpp5
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.cpp45
-rw-r--r--libsolidity/inlineasm/AsmAnalysis.h9
-rw-r--r--libsolidity/inlineasm/AsmData.h18
-rw-r--r--libsolidity/inlineasm/AsmDataForward.h14
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp159
-rw-r--r--libsolidity/inlineasm/AsmParser.h16
-rw-r--r--libsolidity/inlineasm/AsmPrinter.cpp13
-rw-r--r--libsolidity/inlineasm/AsmPrinter.h1
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.cpp11
-rw-r--r--libsolidity/inlineasm/AsmScopeFiller.h1
-rw-r--r--libsolidity/interface/AssemblyStack.cpp23
-rw-r--r--libsolidity/interface/AssemblyStack.h2
-rw-r--r--libsolidity/interface/CompilerStack.cpp9
-rw-r--r--libsolidity/interface/Exceptions.h12
-rw-r--r--libsolidity/interface/StandardCompiler.cpp8
-rw-r--r--libsolidity/parsing/Parser.cpp14
41 files changed, 854 insertions, 295 deletions
diff --git a/libsolidity/analysis/ConstantEvaluator.cpp b/libsolidity/analysis/ConstantEvaluator.cpp
index 4d546e68..83f37f47 100644
--- a/libsolidity/analysis/ConstantEvaluator.cpp
+++ b/libsolidity/analysis/ConstantEvaluator.cpp
@@ -28,51 +28,42 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
-/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
{
- TypePointer const& subType = _operation.subExpression().annotation().type;
- if (!dynamic_cast<RationalNumberType const*>(subType.get()))
- m_errorReporter.fatalTypeError(_operation.subExpression().location(), "Invalid constant expression.");
- TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
- _operation.annotation().type = t;
+ auto sub = type(_operation.subExpression());
+ if (sub)
+ setType(_operation, sub->unaryOperatorResult(_operation.getOperator()));
}
-/// FIXME: this is pretty much a copy of TypeChecker::endVisit(BinaryOperation)
void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
{
- TypePointer const& leftType = _operation.leftExpression().annotation().type;
- TypePointer const& rightType = _operation.rightExpression().annotation().type;
- if (!dynamic_cast<RationalNumberType const*>(leftType.get()))
- m_errorReporter.fatalTypeError(_operation.leftExpression().location(), "Invalid constant expression.");
- if (!dynamic_cast<RationalNumberType const*>(rightType.get()))
- m_errorReporter.fatalTypeError(_operation.rightExpression().location(), "Invalid constant expression.");
- TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
- if (!commonType)
+ auto left = type(_operation.leftExpression());
+ auto right = type(_operation.rightExpression());
+ if (left && right)
{
- m_errorReporter.typeError(
- _operation.location(),
- "Operator " +
- string(Token::toString(_operation.getOperator())) +
- " not compatible with types " +
- leftType->toString() +
- " and " +
- rightType->toString()
+ auto commonType = left->binaryOperatorResult(_operation.getOperator(), right);
+ if (!commonType)
+ m_errorReporter.fatalTypeError(
+ _operation.location(),
+ "Operator " +
+ string(Token::toString(_operation.getOperator())) +
+ " not compatible with types " +
+ left->toString() +
+ " and " +
+ right->toString()
+ );
+ setType(
+ _operation,
+ Token::isCompareOp(_operation.getOperator()) ?
+ make_shared<BoolType>() :
+ commonType
);
- commonType = leftType;
}
- _operation.annotation().commonType = commonType;
- _operation.annotation().type =
- Token::isCompareOp(_operation.getOperator()) ?
- make_shared<BoolType>() :
- commonType;
}
void ConstantEvaluator::endVisit(Literal const& _literal)
{
- _literal.annotation().type = Type::forLiteral(_literal);
- if (!_literal.annotation().type)
- m_errorReporter.fatalTypeError(_literal.location(), "Invalid literal value.");
+ setType(_literal, Type::forLiteral(_literal));
}
void ConstantEvaluator::endVisit(Identifier const& _identifier)
@@ -81,18 +72,34 @@ void ConstantEvaluator::endVisit(Identifier const& _identifier)
if (!variableDeclaration)
return;
if (!variableDeclaration->isConstant())
- m_errorReporter.fatalTypeError(_identifier.location(), "Identifier must be declared constant.");
+ return;
- ASTPointer<Expression> value = variableDeclaration->value();
+ ASTPointer<Expression> const& value = variableDeclaration->value();
if (!value)
- m_errorReporter.fatalTypeError(_identifier.location(), "Constant identifier declaration must have a constant value.");
-
- if (!value->annotation().type)
+ return;
+ else if (!m_types->count(value.get()))
{
if (m_depth > 32)
m_errorReporter.fatalTypeError(_identifier.location(), "Cyclic constant definition (or maximum recursion depth exhausted).");
- ConstantEvaluator e(*value, m_errorReporter, m_depth + 1);
+ ConstantEvaluator(m_errorReporter, m_depth + 1, m_types).evaluate(*value);
}
- _identifier.annotation().type = value->annotation().type;
+ setType(_identifier, type(*value));
+}
+
+void ConstantEvaluator::setType(ASTNode const& _node, TypePointer const& _type)
+{
+ if (_type && _type->category() == Type::Category::RationalNumber)
+ (*m_types)[&_node] = _type;
+}
+
+TypePointer ConstantEvaluator::type(ASTNode const& _node)
+{
+ return (*m_types)[&_node];
+}
+
+TypePointer ConstantEvaluator::evaluate(Expression const& _expr)
+{
+ _expr.accept(*this);
+ return type(_expr);
}
diff --git a/libsolidity/analysis/ConstantEvaluator.h b/libsolidity/analysis/ConstantEvaluator.h
index 6725d610..77a357b6 100644
--- a/libsolidity/analysis/ConstantEvaluator.h
+++ b/libsolidity/analysis/ConstantEvaluator.h
@@ -38,22 +38,32 @@ class TypeChecker;
class ConstantEvaluator: private ASTConstVisitor
{
public:
- ConstantEvaluator(Expression const& _expr, ErrorReporter& _errorReporter, size_t _newDepth = 0):
+ ConstantEvaluator(
+ ErrorReporter& _errorReporter,
+ size_t _newDepth = 0,
+ std::shared_ptr<std::map<ASTNode const*, TypePointer>> _types = std::make_shared<std::map<ASTNode const*, TypePointer>>()
+ ):
m_errorReporter(_errorReporter),
- m_depth(_newDepth)
+ m_depth(_newDepth),
+ m_types(_types)
{
- _expr.accept(*this);
}
+ TypePointer evaluate(Expression const& _expr);
+
private:
virtual void endVisit(BinaryOperation const& _operation);
virtual void endVisit(UnaryOperation const& _operation);
virtual void endVisit(Literal const& _literal);
virtual void endVisit(Identifier const& _identifier);
+ void setType(ASTNode const& _node, TypePointer const& _type);
+ TypePointer type(ASTNode const& _node);
+
ErrorReporter& m_errorReporter;
/// Current recursion depth.
- size_t m_depth;
+ size_t m_depth = 0;
+ std::shared_ptr<std::map<ASTNode const*, TypePointer>> m_types;
};
}
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index b33c8568..7508ad9e 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -23,6 +23,7 @@
#include <libsolidity/analysis/DeclarationContainer.h>
#include <libsolidity/ast/AST.h>
#include <libsolidity/ast/Types.h>
+#include <libdevcore/StringUtils.h>
using namespace std;
using namespace dev;
@@ -105,7 +106,7 @@ bool DeclarationContainer::registerDeclaration(
return true;
}
-std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
+vector<Declaration const*> DeclarationContainer::resolveName(ASTString const& _name, bool _recursive) const
{
solAssert(!_name.empty(), "Attempt to resolve empty name.");
auto result = m_declarations.find(_name);
@@ -115,3 +116,22 @@ std::vector<Declaration const*> DeclarationContainer::resolveName(ASTString cons
return m_enclosingContainer->resolveName(_name, true);
return vector<Declaration const*>({});
}
+
+vector<ASTString> DeclarationContainer::similarNames(ASTString const& _name) const
+{
+ static size_t const MAXIMUM_EDIT_DISTANCE = 2;
+
+ vector<ASTString> similar;
+
+ for (auto const& declaration: m_declarations)
+ {
+ string const& declarationName = declaration.first;
+ if (stringWithinDistance(_name, declarationName, MAXIMUM_EDIT_DISTANCE))
+ similar.push_back(declarationName);
+ }
+
+ if (m_enclosingContainer)
+ similar += m_enclosingContainer->similarNames(_name);
+
+ return similar;
+}
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index 301998b7..f9b1bda4 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -58,6 +58,10 @@ public:
/// @returns whether declaration is valid, and if not also returns previous declaration.
Declaration const* conflictingDeclaration(Declaration const& _declaration, ASTString const* _name = nullptr) const;
+ /// @returns existing declaration names similar to @a _name.
+ /// Searches this and all parent containers.
+ std::vector<ASTString> similarNames(ASTString const& _name) const;
+
private:
ASTNode const* m_enclosingNode;
DeclarationContainer const* m_enclosingContainer;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index 5d010693..5e4d414b 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -25,6 +25,7 @@
#include <libsolidity/ast/AST.h>
#include <libsolidity/analysis/TypeChecker.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libdevcore/StringUtils.h>
#include <boost/algorithm/string.hpp>
@@ -425,6 +426,11 @@ vector<_T const*> NameAndTypeResolver::cThreeMerge(list<list<_T const*>>& _toMer
return result;
}
+string NameAndTypeResolver::similarNameSuggestions(ASTString const& _name) const
+{
+ return quotedAlternativesList(m_currentScope->similarNames(_name));
+}
+
DeclarationRegistrationHelper::DeclarationRegistrationHelper(
map<ASTNode const*, shared_ptr<DeclarationContainer>>& _scopes,
ASTNode& _astRoot,
diff --git a/libsolidity/analysis/NameAndTypeResolver.h b/libsolidity/analysis/NameAndTypeResolver.h
index d83697cd..9aea07ab 100644
--- a/libsolidity/analysis/NameAndTypeResolver.h
+++ b/libsolidity/analysis/NameAndTypeResolver.h
@@ -93,6 +93,9 @@ public:
/// Generate and store warnings about variables that are named like instructions.
void warnVariablesNamedLikeInstructions();
+ /// @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;
+
private:
/// Internal version of @a resolveNamesAndTypes (called from there) throws exceptions on fatal errors.
bool resolveNamesAndTypesInternal(ASTNode& _node, bool _resolveInsideCode = true);
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index f22c95cc..0bb5e3fe 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -47,7 +47,13 @@ bool ReferencesResolver::visit(Identifier const& _identifier)
{
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
if (declarations.empty())
- fatalDeclarationError(_identifier.location(), "Undeclared identifier.");
+ {
+ string suggestions = m_resolver.similarNameSuggestions(_identifier.name());
+ string errorMessage =
+ "Undeclared identifier." +
+ (suggestions.empty()? "": " Did you mean " + std::move(suggestions) + "?");
+ declarationError(_identifier.location(), errorMessage);
+ }
else if (declarations.size() == 1)
_identifier.annotation().referencedDeclaration = declarations.front();
else
@@ -90,7 +96,10 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
{
Declaration const* declaration = m_resolver.pathFromCurrentScope(_typeName.namePath());
if (!declaration)
- fatalDeclarationError(_typeName.location(), "Identifier not found or not unique.");
+ {
+ declarationError(_typeName.location(), "Identifier not found or not unique.");
+ return;
+ }
_typeName.annotation().referencedDeclaration = declaration;
@@ -101,7 +110,7 @@ void ReferencesResolver::endVisit(UserDefinedTypeName const& _typeName)
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
_typeName.annotation().type = make_shared<ContractType>(*contract);
else
- fatalTypeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
+ typeError(_typeName.location(), "Name has to refer to a struct, enum or contract.");
}
void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
@@ -112,17 +121,25 @@ void ReferencesResolver::endVisit(FunctionTypeName const& _typeName)
case VariableDeclaration::Visibility::External:
break;
default:
- fatalTypeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ typeError(_typeName.location(), "Invalid visibility, can only be \"external\" or \"internal\".");
+ return;
}
if (_typeName.isPayable() && _typeName.visibility() != VariableDeclaration::Visibility::External)
- fatalTypeError(_typeName.location(), "Only external function types can be payable.");
+ {
+ typeError(_typeName.location(), "Only external function types can be payable.");
+ return;
+ }
+
if (_typeName.visibility() == VariableDeclaration::Visibility::External)
for (auto const& t: _typeName.parameterTypes() + _typeName.returnParameterTypes())
{
solAssert(t->annotation().type, "Type not set for parameter.");
if (!t->annotation().type->canBeUsedExternally(false))
- fatalTypeError(t->location(), "Internal type cannot be used for external function type.");
+ {
+ typeError(t->location(), "Internal type cannot be used for external function type.");
+ return;
+ }
}
_typeName.annotation().type = make_shared<FunctionType>(_typeName);
@@ -142,15 +159,21 @@ void ReferencesResolver::endVisit(Mapping const& _typeName)
void ReferencesResolver::endVisit(ArrayTypeName const& _typeName)
{
TypePointer baseType = _typeName.baseType().annotation().type;
+ if (!baseType)
+ {
+ solAssert(!m_errorReporter.errors().empty(), "");
+ return;
+ }
if (baseType->storageBytes() == 0)
fatalTypeError(_typeName.baseType().location(), "Illegal base type of storage size zero for array.");
if (Expression const* length = _typeName.length())
{
- if (!length->annotation().type)
- ConstantEvaluator e(*length, m_errorReporter);
- auto const* lengthType = dynamic_cast<RationalNumberType const*>(length->annotation().type.get());
+ TypePointer lengthTypeGeneric = length->annotation().type;
+ if (!lengthTypeGeneric)
+ lengthTypeGeneric = ConstantEvaluator(m_errorReporter).evaluate(*length);
+ RationalNumberType const* lengthType = dynamic_cast<RationalNumberType const*>(lengthTypeGeneric.get());
if (!lengthType || !lengthType->mobileType())
- fatalTypeError(length->location(), "Invalid array length, expected integer literal.");
+ fatalTypeError(length->location(), "Invalid array length, expected integer literal or constant expression.");
else if (lengthType->isFractional())
fatalTypeError(length->location(), "Array with fractional length specified.");
else if (lengthType->isNegative())
@@ -206,7 +229,7 @@ bool ReferencesResolver::visit(InlineAssembly const& _inlineAssembly)
// Will be re-generated later with correct information
assembly::AsmAnalysisInfo analysisInfo;
- assembly::AsmAnalyzer(analysisInfo, errorsIgnored, false, resolver).analyze(_inlineAssembly.operations());
+ assembly::AsmAnalyzer(analysisInfo, errorsIgnored, assembly::AsmFlavour::Loose, resolver).analyze(_inlineAssembly.operations());
return false;
}
@@ -321,17 +344,13 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
type = ref->copyForLocation(typeLoc, isPointer);
}
else if (varLoc != Location::Default && !ref)
- fatalTypeError(_variable.location(), "Storage location can only be given for array or struct types.");
-
- if (!type)
- fatalTypeError(_variable.location(), "Invalid type name.");
+ typeError(_variable.location(), "Storage location can only be given for array or struct types.");
+ _variable.annotation().type = type;
}
else if (!_variable.canHaveAutoType())
- fatalTypeError(_variable.location(), "Explicit type needed.");
+ typeError(_variable.location(), "Explicit type needed.");
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
-
- _variable.annotation().type = type;
}
void ReferencesResolver::typeError(SourceLocation const& _location, string const& _description)
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index b6cc04da..5a3745b0 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -224,3 +224,17 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
return true;
}
+
+bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
+{
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (!_declaration.typeName())
+ {
+ if (v050)
+ m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
+ else
+ m_errorReporter.warning(_declaration.location(), "Use of the \"var\" keyword is deprecated.");
+ }
+ return true;
+}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index d5d72f14..871bf0a9 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -69,6 +69,8 @@ private:
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(FunctionTypeName const& _node) override;
+ virtual bool visit(VariableDeclaration const& _declaration) override;
+
ErrorReporter& m_errorReporter;
/// Flag that indicates whether a function modifier actually contains '_'.
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 73047e76..d67142e4 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -171,13 +171,7 @@ void TypeChecker::checkContractDuplicateFunctions(ContractDefinition const& _con
ssl.append("Another declaration is here:", (*it)->location());
string msg = "More than one constructor defined.";
- size_t occurrences = ssl.infos.size();
- if (occurrences > 32)
- {
- ssl.infos.resize(32);
- msg += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
- }
-
+ ssl.limitSize(msg);
m_errorReporter.declarationError(
functions[_contract.name()].front()->location(),
ssl,
@@ -219,12 +213,7 @@ void TypeChecker::findDuplicateDefinitions(map<string, vector<T>> const& _defini
if (ssl.infos.size() > 0)
{
- size_t occurrences = ssl.infos.size();
- if (occurrences > 32)
- {
- ssl.infos.resize(32);
- _message += " Truncated from " + boost::lexical_cast<string>(occurrences) + " to the first 32 occurrences.";
- }
+ ssl.limitSize(_message);
m_errorReporter.declarationError(
overloads[i]->location(),
@@ -570,6 +559,17 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
m_errorReporter.typeError(var->location(), "Type is required to live outside storage.");
if (_function.visibility() >= FunctionDefinition::Visibility::Public && !(type(*var)->interfaceType(isLibraryFunction)))
m_errorReporter.fatalTypeError(var->location(), "Internal or recursive type is not allowed for public or external functions.");
+ if (
+ _function.visibility() > FunctionDefinition::Visibility::Internal &&
+ type(*var)->category() == Type::Category::Struct &&
+ !type(*var)->dataStoredIn(DataLocation::Storage) &&
+ !_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::ABIEncoderV2)
+ )
+ m_errorReporter.typeError(
+ var->location(),
+ "Structs are only supported in the new experimental ABI encoder. "
+ "Use \"pragma experimental ABIEncoderV2;\" to enable the feature."
+ );
var->accept(*this);
}
@@ -604,6 +604,8 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (_function.visibility() < FunctionDefinition::Visibility::Public)
m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot be internal or private.");
+ else if (_function.visibility() != FunctionDefinition::Visibility::External)
+ m_errorReporter.warning(_function.location(), "Functions in interfaces should be declared external.");
}
if (_function.isConstructor())
m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces.");
@@ -873,7 +875,7 @@ bool TypeChecker::visit(InlineAssembly const& _inlineAssembly)
assembly::AsmAnalyzer analyzer(
*_inlineAssembly.annotation().analysisInfo,
m_errorReporter,
- false,
+ assembly::AsmFlavour::Loose,
identifierAccess
);
if (!analyzer.analyze(_inlineAssembly.operations()))
@@ -1060,7 +1062,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
_statement.initialValue()->location(),
"Invalid rational " +
valueComponentType->toString() +
- " (absolute value too large or divison by zero)."
+ " (absolute value too large or division by zero)."
);
else
solAssert(false, "");
@@ -1551,8 +1553,12 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (!functionType->takesArbitraryParameters() && parameterTypes.size() != arguments.size())
{
+ bool isStructConstructorCall = _functionCall.annotation().kind == FunctionCallKind::StructConstructorCall;
+
string msg =
- "Wrong argument count for function call: " +
+ "Wrong argument count for " +
+ string(isStructConstructorCall ? "struct constructor" : "function call") +
+ ": " +
toString(arguments.size()) +
" arguments given but expected " +
toString(parameterTypes.size()) +
@@ -1668,10 +1674,12 @@ void TypeChecker::endVisit(NewExpression const& _newExpression)
SecondarySourceLocation ssl;
for (auto function: contract->annotation().unimplementedFunctions)
ssl.append("Missing implementation:", function->location());
+ string msg = "Trying to create an instance of an abstract contract.";
+ ssl.limitSize(msg);
m_errorReporter.typeError(
_newExpression.location(),
ssl,
- "Trying to create an instance of an abstract contract."
+ msg
);
}
if (!contract->constructorIsPublic())
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 7e41fc16..13c3ab68 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -40,19 +40,20 @@ public:
void operator()(assembly::Label const&) { }
void operator()(assembly::Instruction const& _instruction)
{
- if (eth::SemanticInformation::invalidInViewFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::NonPayable, _instruction.location);
- else if (eth::SemanticInformation::invalidInPureFunctions(_instruction.instruction))
- m_reportMutability(StateMutability::View, _instruction.location);
+ checkInstruction(_instruction.location, _instruction.instruction);
}
void operator()(assembly::Literal const&) {}
void operator()(assembly::Identifier const&) {}
void operator()(assembly::FunctionalInstruction const& _instr)
{
- (*this)(_instr.instruction);
+ checkInstruction(_instr.location, _instr.instruction);
for (auto const& arg: _instr.arguments)
boost::apply_visitor(*this, arg);
}
+ void operator()(assembly::ExpressionStatement const& _expr)
+ {
+ boost::apply_visitor(*this, _expr.expression);
+ }
void operator()(assembly::StackAssignment const&) {}
void operator()(assembly::Assignment const& _assignment)
{
@@ -102,6 +103,13 @@ public:
private:
std::function<void(StateMutability, SourceLocation const&)> m_reportMutability;
+ void checkInstruction(SourceLocation _location, solidity::Instruction _instruction)
+ {
+ if (eth::SemanticInformation::invalidInViewFunctions(_instruction))
+ m_reportMutability(StateMutability::NonPayable, _location);
+ else if (eth::SemanticInformation::invalidInPureFunctions(_instruction))
+ m_reportMutability(StateMutability::View, _location);
+ }
};
}
@@ -267,6 +275,22 @@ void ViewPureChecker::endVisit(FunctionCall const& _functionCall)
reportMutability(mut, _functionCall.location());
}
+bool ViewPureChecker::visit(MemberAccess const& _memberAccess)
+{
+ // Catch the special case of `this.f.selector` which is a pure expression.
+ ASTString const& member = _memberAccess.memberName();
+ if (
+ _memberAccess.expression().annotation().type->category() == Type::Category::Function &&
+ member == "selector"
+ )
+ if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
+ if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
+ if (exprInt->name() == "this")
+ // Do not continue visiting.
+ return false;
+ return true;
+}
+
void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
{
StateMutability mutability = StateMutability::Pure;
diff --git a/libsolidity/analysis/ViewPureChecker.h b/libsolidity/analysis/ViewPureChecker.h
index fec060b6..0b882cd8 100644
--- a/libsolidity/analysis/ViewPureChecker.h
+++ b/libsolidity/analysis/ViewPureChecker.h
@@ -56,6 +56,7 @@ private:
virtual bool visit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(ModifierDefinition const& _modifierDef) override;
virtual void endVisit(Identifier const& _identifier) override;
+ virtual bool visit(MemberAccess const& _memberAccess) override;
virtual void endVisit(MemberAccess const& _memberAccess) override;
virtual void endVisit(IndexAccess const& _indexAccess) override;
virtual void endVisit(ModifierInvocation const& _modifier) override;
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 21daac2c..e4b7e4fd 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -949,11 +949,25 @@ bool RationalNumberType::operator==(Type const& _other) const
return m_value == other.m_value;
}
+string RationalNumberType::bigintToReadableString(dev::bigint const& _num)
+{
+ string str = _num.str();
+ if (str.size() > 32)
+ {
+ int omitted = str.size() - 8;
+ str = str.substr(0, 4) + "...(" + to_string(omitted) + " digits omitted)..." + str.substr(str.size() - 4, 4);
+ }
+ return str;
+}
+
string RationalNumberType::toString(bool) const
{
if (!isFractional())
- return "int_const " + m_value.numerator().str();
- return "rational_const " + m_value.numerator().str() + '/' + m_value.denominator().str();
+ return "int_const " + bigintToReadableString(m_value.numerator());
+
+ string numerator = bigintToReadableString(m_value.numerator());
+ string denominator = bigintToReadableString(m_value.denominator());
+ return "rational_const " + numerator + " / " + denominator;
}
u256 RationalNumberType::literalValue(Literal const*) const
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 635279ab..2e7d05ba 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -257,7 +257,7 @@ public:
}
virtual u256 literalValue(Literal const*) const
{
- solAssert(false, "Literal value requested for type without literals.");
+ solAssert(false, "Literal value requested for type without literals: " + toString(false));
}
/// @returns a (simpler) type that is encoded in the same way for external function calls.
@@ -437,6 +437,10 @@ private:
/// @returns true if the literal is a valid rational number.
static std::tuple<bool, rational> parseRational(std::string const& _value);
+
+ /// @returns a truncated readable representation of the bigint keeping only
+ /// up to 4 leading and 4 trailing digits.
+ static std::string bigintToReadableString(dev::bigint const& num);
};
/**
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 6648be06..00f59065 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -120,7 +120,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
Whiskers templ(R"(
function <functionName>(headStart, dataEnd) -> <valueReturnParams> {
- switch slt(sub(dataEnd, headStart), <minimumSize>) case 1 { revert(0, 0) }
+ if slt(sub(dataEnd, headStart), <minimumSize>) { revert(0, 0) }
<decodeElements>
}
)");
@@ -151,7 +151,7 @@ string ABIFunctions::tupleDecoder(TypePointers const& _types, bool _fromMemory)
R"(
{
let offset := <load>(add(headStart, <pos>))
- switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) }
+ if gt(offset, 0xffffffffffffffff) { revert(0, 0) }
<values> := <abiDecode>(add(headStart, offset), dataEnd)
}
)" :
@@ -1134,7 +1134,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
R"(
// <readableTypeName>
function <functionName>(offset, end) -> array {
- switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
+ if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
let length := <retrieveLength>
array := <allocate>(<allocationSize>(length))
let dst := array
@@ -1169,7 +1169,7 @@ string ABIFunctions::abiDecodingFunctionArray(ArrayType const& _type, bool _from
else
{
string baseEncodedSize = toCompactHexWithPrefix(_type.baseType()->calldataEncodedSize());
- templ("staticBoundsCheck", "switch gt(add(src, mul(length, " + baseEncodedSize + ")), end) case 1 { revert(0, 0) }");
+ templ("staticBoundsCheck", "if gt(add(src, mul(length, " + baseEncodedSize + ")), end) { revert(0, 0) }");
templ("retrieveElementPos", "src");
templ("baseEncodedSize", baseEncodedSize);
}
@@ -1197,11 +1197,11 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
templ = R"(
// <readableTypeName>
function <functionName>(offset, end) -> arrayPos, length {
- switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
+ if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
length := calldataload(offset)
- switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) }
+ if gt(length, 0xffffffffffffffff) { revert(0, 0) }
arrayPos := add(offset, 0x20)
- switch gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) case 1 { revert(0, 0) }
+ if gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) { revert(0, 0) }
}
)";
else
@@ -1209,7 +1209,7 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
// <readableTypeName>
function <functionName>(offset, end) -> arrayPos {
arrayPos := offset
- switch gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) case 1 { revert(0, 0) }
+ if gt(add(arrayPos, mul(<length>, <baseEncodedSize>)), end) { revert(0, 0) }
}
)";
Whiskers w{templ};
@@ -1235,13 +1235,13 @@ string ABIFunctions::abiDecodingFunctionByteArray(ArrayType const& _type, bool _
Whiskers templ(
R"(
function <functionName>(offset, end) -> array {
- switch slt(add(offset, 0x1f), end) case 0 { revert(0, 0) }
+ if iszero(slt(add(offset, 0x1f), end)) { revert(0, 0) }
let length := <load>(offset)
array := <allocate>(<allocationSize>(length))
mstore(array, length)
let src := add(offset, 0x20)
let dst := add(array, 0x20)
- switch gt(add(src, length), end) case 1 { revert(0, 0) }
+ if gt(add(src, length), end) { revert(0, 0) }
<copyToMemFun>(src, dst, length)
}
)"
@@ -1268,7 +1268,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
Whiskers templ(R"(
// <readableTypeName>
function <functionName>(headStart, end) -> value {
- switch slt(sub(end, headStart), <minimumSize>) case 1 { revert(0, 0) }
+ if slt(sub(end, headStart), <minimumSize>) { revert(0, 0) }
value := <allocate>(<memorySize>)
<#members>
{
@@ -1296,7 +1296,7 @@ string ABIFunctions::abiDecodingFunctionStruct(StructType const& _type, bool _fr
dynamic ?
R"(
let offset := <load>(add(headStart, <pos>))
- switch gt(offset, 0xffffffffffffffff) case 1 { revert(0, 0) }
+ if gt(offset, 0xffffffffffffffff) { revert(0, 0) }
mstore(add(value, <memoryOffset>), <abiDecode>(add(headStart, offset), end))
)" :
R"(
@@ -1501,7 +1501,7 @@ string ABIFunctions::arrayAllocationSizeFunction(ArrayType const& _type)
Whiskers w(R"(
function <functionName>(length) -> size {
// Make sure we can allocate memory without overflow
- switch gt(length, 0xffffffffffffffff) case 1 { revert(0, 0) }
+ if gt(length, 0xffffffffffffffff) { revert(0, 0) }
size := <allocationSize>
<addLengthSlot>
}
@@ -1620,7 +1620,7 @@ string ABIFunctions::allocationFunction()
memPtr := mload(<freeMemoryPointer>)
let newFreePtr := add(memPtr, size)
// protect against overflow
- switch or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) case 1 { revert(0, 0) }
+ if or(gt(newFreePtr, 0xffffffffffffffff), lt(newFreePtr, memPtr)) { revert(0, 0) }
mstore(<freeMemoryPointer>, newFreePtr)
}
)")
diff --git a/libsolidity/codegen/Compiler.cpp b/libsolidity/codegen/Compiler.cpp
index 44264a07..d3afada5 100644
--- a/libsolidity/codegen/Compiler.cpp
+++ b/libsolidity/codegen/Compiler.cpp
@@ -51,6 +51,7 @@ void Compiler::compileClone(
map<ContractDefinition const*, eth::Assembly const*> const& _contracts
)
{
+ solAssert(!_contract.isLibrary(), "");
ContractCompiler runtimeCompiler(nullptr, m_runtimeContext, m_optimize);
ContractCompiler cloneCompiler(&runtimeCompiler, m_context, m_optimize);
m_runtimeSub = cloneCompiler.compileClone(_contract, _contracts);
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index ce9c3b7f..7a88475a 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -319,14 +319,19 @@ void CompilerContext::appendInlineAssembly(
ErrorList errors;
ErrorReporter errorReporter(errors);
auto scanner = make_shared<Scanner>(CharStream(_assembly), "--CODEGEN--");
- auto parserResult = assembly::Parser(errorReporter).parse(scanner);
+ auto parserResult = assembly::Parser(errorReporter, assembly::AsmFlavour::Strict).parse(scanner);
#ifdef SOL_OUTPUT_ASM
cout << assembly::AsmPrinter()(*parserResult) << endl;
#endif
assembly::AsmAnalysisInfo analysisInfo;
bool analyzerResult = false;
if (parserResult)
- analyzerResult = assembly::AsmAnalyzer(analysisInfo, errorReporter, false, identifierAccess.resolve).analyze(*parserResult);
+ analyzerResult = assembly::AsmAnalyzer(
+ analysisInfo,
+ errorReporter,
+ assembly::AsmFlavour::Strict,
+ identifierAccess.resolve
+ ).analyze(*parserResult);
if (!parserResult || !errorReporter.errors().empty() || !analyzerResult)
{
string message =
@@ -347,6 +352,9 @@ void CompilerContext::appendInlineAssembly(
solAssert(errorReporter.errors().empty(), "Failed to analyze inline assembly block.");
assembly::CodeGenerator::assemble(*parserResult, analysisInfo, *m_asm, identifierAccess, _system);
+
+ // Reset the source location to the one of the node (instead of the CODEGEN source location)
+ updateSourceLocation();
}
FunctionDefinition const& CompilerContext::resolveVirtualFunction(
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 7743fd3f..a155a3a5 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -174,6 +174,9 @@ public:
eth::AssemblyItem appendData(bytes const& _data) { return m_asm->append(_data); }
/// Appends the address (virtual, will be filled in by linker) of a library.
void appendLibraryAddress(std::string const& _identifier) { m_asm->appendLibraryAddress(_identifier); }
+ /// Appends a zero-address that can be replaced by something else at deploy time (if the
+ /// position in bytecode is known).
+ void appendDeployTimeAddress() { m_asm->append(eth::PushDeployTimeAddress); }
/// Resets the stack of visited nodes with a new stack having only @c _node
void resetVisitedNodes(ASTNode const* _node);
/// Pops the stack of visited nodes
@@ -187,8 +190,8 @@ public:
CompilerContext& operator<<(u256 const& _value) { m_asm->append(_value); return *this; }
CompilerContext& operator<<(bytes const& _data) { m_asm->append(_data); return *this; }
- /// Appends inline assembly. @a _replacements are string-matching replacements that are performed
- /// prior to parsing the inline assembly.
+ /// Appends inline assembly (strict mode).
+ /// @a _replacements are string-matching replacements that are performed prior to parsing the inline assembly.
/// @param _localVariables assigns stack positions to variables with the last one being the stack top
/// @param _system if true, this is a "system-level" assembly where all functions use named labels.
void appendInlineAssembly(
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index a81ba518..f463db94 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -64,6 +64,12 @@ void ContractCompiler::compileContract(
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
+
+ if (_contract.isLibrary())
+ // Check whether this is a call (true) or a delegatecall (false).
+ // This has to be the first code in the contract.
+ appendDelegatecallCheck();
+
initializeContext(_contract, _contracts);
appendFunctionSelector(_contract);
appendMissingFunctions();
@@ -75,8 +81,13 @@ size_t ContractCompiler::compileConstructor(
)
{
CompilerContext::LocationSetter locationSetter(m_context, _contract);
- initializeContext(_contract, _contracts);
- return packIntoContractCreator(_contract);
+ if (_contract.isLibrary())
+ return deployLibrary(_contract);
+ else
+ {
+ initializeContext(_contract, _contracts);
+ return packIntoContractCreator(_contract);
+ }
}
size_t ContractCompiler::compileClone(
@@ -122,6 +133,7 @@ void ContractCompiler::appendCallValueCheck()
void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
{
+ solAssert(!_contract.isLibrary(), "Tried to initialize library.");
CompilerContext::LocationSetter locationSetter(m_context, _contract);
// Determine the arguments that are used for the base constructors.
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
@@ -163,6 +175,7 @@ void ContractCompiler::appendInitAndConstructorCode(ContractDefinition const& _c
size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _contract)
{
solAssert(!!m_runtimeCompiler, "");
+ solAssert(!_contract.isLibrary(), "Tried to use contract creator or library.");
appendInitAndConstructorCode(_contract);
@@ -188,6 +201,34 @@ size_t ContractCompiler::packIntoContractCreator(ContractDefinition const& _cont
return m_context.runtimeSub();
}
+size_t ContractCompiler::deployLibrary(ContractDefinition const& _contract)
+{
+ solAssert(!!m_runtimeCompiler, "");
+ solAssert(_contract.isLibrary(), "Tried to deploy contract as library.");
+
+ CompilerContext::LocationSetter locationSetter(m_context, _contract);
+
+ solAssert(m_context.runtimeSub() != size_t(-1), "Runtime sub not registered");
+ m_context.pushSubroutineSize(m_context.runtimeSub());
+ m_context.pushSubroutineOffset(m_context.runtimeSub());
+ m_context.appendInlineAssembly(R"(
+ {
+ // If code starts at 11, an mstore(0) writes to the full PUSH20 plus data
+ // without the need for a shift.
+ let codepos := 11
+ codecopy(codepos, subOffset, subSize)
+ // Check that the first opcode is a PUSH20
+ switch eq(0x73, byte(0, mload(codepos)))
+ case 0 { invalid() }
+ mstore(0, address())
+ mstore8(codepos, 0x73)
+ return(codepos, subSize)
+ }
+ )", {"subSize", "subOffset"});
+
+ return m_context.runtimeSub();
+}
+
void ContractCompiler::appendBaseConstructor(FunctionDefinition const& _constructor)
{
CompilerContext::LocationSetter locationSetter(m_context, _constructor);
@@ -244,11 +285,26 @@ void ContractCompiler::appendConstructor(FunctionDefinition const& _constructor)
_constructor.accept(*this);
}
+void ContractCompiler::appendDelegatecallCheck()
+{
+ // Special constant that will be replaced by the address at deploy time.
+ // At compilation time, this is just "PUSH20 00...000".
+ m_context.appendDeployTimeAddress();
+ m_context << Instruction::ADDRESS << Instruction::EQ;
+ // The result on the stack is
+ // "We have not been called via DELEGATECALL".
+}
+
void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contract)
{
map<FixedHash<4>, FunctionTypePointer> interfaceFunctions = _contract.interfaceFunctions();
map<FixedHash<4>, const eth::AssemblyItem> callDataUnpackerEntryPoints;
+ if (_contract.isLibrary())
+ {
+ solAssert(m_context.stackHeight() == 1, "CALL / DELEGATECALL flag expected.");
+ }
+
FunctionDefinition const* fallback = _contract.fallbackFunction();
eth::AssemblyItem notFound = m_context.newTag();
// directly jump to fallback if the data is too short to contain a function selector
@@ -260,7 +316,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
if (!interfaceFunctions.empty())
CompilerUtils(m_context).loadFromMemory(0, IntegerType(CompilerUtils::dataStartOffset * 8), true);
- // stack now is: 1 0 <funhash>
+ // stack now is: <can-call-non-view-functions>? <funhash>
for (auto const& it: interfaceFunctions)
{
callDataUnpackerEntryPoints.insert(std::make_pair(it.first, m_context.newTag()));
@@ -272,6 +328,7 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
m_context << notFound;
if (fallback)
{
+ solAssert(!_contract.isLibrary(), "");
if (!fallback->isPayable())
appendCallValueCheck();
@@ -291,6 +348,13 @@ void ContractCompiler::appendFunctionSelector(ContractDefinition const& _contrac
CompilerContext::LocationSetter locationSetter(m_context, functionType->declaration());
m_context << callDataUnpackerEntryPoints.at(it.first);
+ if (_contract.isLibrary() && functionType->stateMutability() > StateMutability::View)
+ {
+ // If the function is not a view function and is called without DELEGATECALL,
+ // we revert.
+ m_context << dupInstruction(2);
+ m_context.appendConditionalRevert();
+ }
m_context.setStackOffset(0);
// We have to allow this for libraries, because value of the previous
// call is still visible in the delegatecall.
@@ -441,6 +505,7 @@ void ContractCompiler::registerStateVariables(ContractDefinition const& _contrac
void ContractCompiler::initializeStateVariables(ContractDefinition const& _contract)
{
+ solAssert(!_contract.isLibrary(), "Tried to initialize state variables of library.");
for (VariableDeclaration const* variable: _contract.stateVariables())
if (variable->value() && !variable->isConstant())
ExpressionCompiler(m_context, m_optimise).appendStateVariableInitialization(*variable);
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 7c5ee59f..1fd80d05 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -75,10 +75,19 @@ private:
/// with a new and initialized context. Adds the constructor code.
/// @returns the identifier of the runtime sub assembly
size_t packIntoContractCreator(ContractDefinition const& _contract);
+ /// Appends code that deploys the given contract as a library.
+ /// Will also add code that modifies the contract in memory by injecting the current address
+ /// for the call protector.
+ size_t deployLibrary(ContractDefinition const& _contract);
/// Appends state variable initialisation and constructor code.
void appendInitAndConstructorCode(ContractDefinition const& _contract);
void appendBaseConstructor(FunctionDefinition const& _constructor);
void appendConstructor(FunctionDefinition const& _constructor);
+ /// Appends code that returns a boolean flag on the stack that tells whether
+ /// the contract has been called via delegatecall (false) or regular call (true).
+ /// This is done by inserting a specific push constant as the first instruction
+ /// whose data will be modified in memory at deploy time.
+ void appendDelegatecallCheck();
void appendFunctionSelector(ContractDefinition const& _contract);
void appendCallValueCheck();
/// Creates code that unpacks the arguments for the given function represented by a vector of TypePointers.
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index bb8c4a94..8e1cf019 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -1014,6 +1014,30 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
_memberAccess.expression().accept(*this);
return false;
}
+ // Another special case for `this.f.selector` which does not need the address.
+ // There are other uses of `.selector` which do need the address, but we want this
+ // specific use to be a pure expression.
+ if (
+ _memberAccess.expression().annotation().type->category() == Type::Category::Function &&
+ member == "selector"
+ )
+ if (auto const* expr = dynamic_cast<MemberAccess const*>(&_memberAccess.expression()))
+ if (auto const* exprInt = dynamic_cast<Identifier const*>(&expr->expression()))
+ if (exprInt->name() == "this")
+ if (Declaration const* declaration = expr->annotation().referencedDeclaration)
+ {
+ u256 identifier;
+ if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
+ identifier = FunctionType(*variable).externalIdentifier();
+ else if (auto const* function = dynamic_cast<FunctionDefinition const*>(declaration))
+ identifier = FunctionType(*function).externalIdentifier();
+ else
+ solAssert(false, "Contract member is neither variable nor function.");
+ m_context << identifier;
+ /// need to store store it as bytes4
+ utils().leftShiftNumberOnStack(224);
+ return false;
+ }
_memberAccess.expression().accept(*this);
switch (_memberAccess.expression().annotation().type->category())
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index 1050621e..a64024b3 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -55,7 +55,7 @@ void SMTChecker::analyze(SourceUnit const& _source)
void SMTChecker::endVisit(VariableDeclaration const& _varDecl)
{
if (_varDecl.isLocalVariable() && _varDecl.type()->isValueType() &&_varDecl.value())
- assignment(_varDecl, *_varDecl.value());
+ assignment(_varDecl, *_varDecl.value(), _varDecl.location());
}
bool SMTChecker::visit(FunctionDefinition const& _function)
@@ -71,6 +71,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
m_interface->reset();
m_currentSequenceCounter.clear();
m_nextFreeSequenceCounter.clear();
+ m_pathConditions.clear();
m_conditionalExecutionHappened = false;
initializeLocalVariables(_function);
return true;
@@ -90,15 +91,16 @@ bool SMTChecker::visit(IfStatement const& _node)
checkBooleanNotConstant(_node.condition(), "Condition is always $VALUE.");
- visitBranch(_node.trueStatement(), expr(_node.condition()));
+ auto countersEndFalse = m_currentSequenceCounter;
+ auto countersEndTrue = visitBranch(_node.trueStatement(), expr(_node.condition()));
vector<Declaration const*> touchedVariables = m_variableUsage->touchedVariables(_node.trueStatement());
if (_node.falseStatement())
{
- visitBranch(*_node.falseStatement(), !expr(_node.condition()));
+ countersEndFalse = visitBranch(*_node.falseStatement(), !expr(_node.condition()));
touchedVariables += m_variableUsage->touchedVariables(*_node.falseStatement());
}
- resetVariables(touchedVariables);
+ mergeVariables(touchedVariables, expr(_node.condition()), countersEndTrue, countersEndFalse);
return false;
}
@@ -178,8 +180,7 @@ void SMTChecker::endVisit(VariableDeclarationStatement const& _varDecl)
else if (knownVariable(*_varDecl.declarations()[0]))
{
if (_varDecl.initialValue())
- // TODO more checks?
- assignment(*_varDecl.declarations()[0], *_varDecl.initialValue());
+ assignment(*_varDecl.declarations()[0], *_varDecl.initialValue(), _varDecl.location());
}
else
m_errorReporter.warning(
@@ -208,7 +209,10 @@ void SMTChecker::endVisit(Assignment const& _assignment)
{
Declaration const* decl = identifier->annotation().referencedDeclaration;
if (knownVariable(*decl))
- assignment(*decl, _assignment.rightHandSide());
+ {
+ assignment(*decl, _assignment.rightHandSide(), _assignment.location());
+ defineExpr(_assignment, expr(_assignment.rightHandSide()));
+ }
else
m_errorReporter.warning(
_assignment.location(),
@@ -230,7 +234,81 @@ void SMTChecker::endVisit(TupleExpression const& _tuple)
"Assertion checker does not yet implement tules and inline arrays."
);
else
- m_interface->addAssertion(expr(_tuple) == expr(*_tuple.components()[0]));
+ defineExpr(_tuple, expr(*_tuple.components()[0]));
+}
+
+void SMTChecker::checkUnderOverflow(smt::Expression _value, IntegerType const& _type, SourceLocation const& _location)
+{
+ checkCondition(
+ _value < minValue(_type),
+ _location,
+ "Underflow (resulting value less than " + formatNumber(_type.minValue()) + ")",
+ "value",
+ &_value
+ );
+ checkCondition(
+ _value > maxValue(_type),
+ _location,
+ "Overflow (resulting value larger than " + formatNumber(_type.maxValue()) + ")",
+ "value",
+ &_value
+ );
+}
+
+void SMTChecker::endVisit(UnaryOperation const& _op)
+{
+ switch (_op.getOperator())
+ {
+ case Token::Not: // !
+ {
+ solAssert(_op.annotation().type->category() == Type::Category::Bool, "");
+ defineExpr(_op, !expr(_op.subExpression()));
+ break;
+ }
+ case Token::Inc: // ++ (pre- or postfix)
+ case Token::Dec: // -- (pre- or postfix)
+ {
+ solAssert(_op.annotation().type->category() == Type::Category::Integer, "");
+ solAssert(_op.subExpression().annotation().lValueRequested, "");
+ if (Identifier const* identifier = dynamic_cast<Identifier const*>(&_op.subExpression()))
+ {
+ Declaration const* decl = identifier->annotation().referencedDeclaration;
+ if (knownVariable(*decl))
+ {
+ auto innerValue = currentValue(*decl);
+ auto newValue = _op.getOperator() == Token::Inc ? innerValue + 1 : innerValue - 1;
+ assignment(*decl, newValue, _op.location());
+ defineExpr(_op, _op.isPrefixOperation() ? newValue : innerValue);
+ }
+ else
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement such assignments."
+ );
+ }
+ else
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement such increments / decrements."
+ );
+ break;
+ }
+ case Token::Add: // +
+ defineExpr(_op, expr(_op.subExpression()));
+ break;
+ case Token::Sub: // -
+ {
+ defineExpr(_op, 0 - expr(_op.subExpression()));
+ if (auto intType = dynamic_cast<IntegerType const*>(_op.annotation().type.get()))
+ checkUnderOverflow(expr(_op), *intType, _op.location());
+ break;
+ }
+ default:
+ m_errorReporter.warning(
+ _op.location(),
+ "Assertion checker does not yet implement this operator."
+ );
+ }
}
void SMTChecker::endVisit(BinaryOperation const& _op)
@@ -268,16 +346,14 @@ void SMTChecker::endVisit(FunctionCall const& _funCall)
solAssert(args.size() == 1, "");
solAssert(args[0]->annotation().type->category() == Type::Category::Bool, "");
checkCondition(!(expr(*args[0])), _funCall.location(), "Assertion violation");
- m_interface->addAssertion(expr(*args[0]));
+ addPathImpliedExpression(expr(*args[0]));
}
else if (funType.kind() == FunctionType::Kind::Require)
{
solAssert(args.size() == 1, "");
solAssert(args[0]->annotation().type->category() == Type::Category::Bool, "");
- m_interface->addAssertion(expr(*args[0]));
- checkCondition(!(expr(*args[0])), _funCall.location(), "Unreachable code");
- // TODO is there something meaningful we can check here?
- // We can check whether the condition is always fulfilled or never fulfilled.
+ checkBooleanNotConstant(*args[0], "Condition is always $VALUE.");
+ addPathImpliedExpression(expr(*args[0]));
}
}
@@ -290,7 +366,7 @@ void SMTChecker::endVisit(Identifier const& _identifier)
// Will be translated as part of the node that requested the lvalue.
}
else if (dynamic_cast<IntegerType const*>(_identifier.annotation().type.get()))
- m_interface->addAssertion(expr(_identifier) == currentValue(*decl));
+ defineExpr(_identifier, currentValue(*decl));
else if (FunctionType const* fun = dynamic_cast<FunctionType const*>(_identifier.annotation().type.get()))
{
if (fun->kind() == FunctionType::Kind::Assert || fun->kind() == FunctionType::Kind::Require)
@@ -306,10 +382,10 @@ void SMTChecker::endVisit(Literal const& _literal)
if (RationalNumberType const* rational = dynamic_cast<RationalNumberType const*>(&type))
solAssert(!rational->isFractional(), "");
- m_interface->addAssertion(expr(_literal) == smt::Expression(type.literalValue(&_literal)));
+ defineExpr(_literal, smt::Expression(type.literalValue(&_literal)));
}
else if (type.category() == Type::Category::Bool)
- m_interface->addAssertion(expr(_literal) == smt::Expression(_literal.token() == Token::TrueLiteral ? true : false));
+ defineExpr(_literal, smt::Expression(_literal.token() == Token::TrueLiteral ? true : false));
else
m_errorReporter.warning(
_literal.location(),
@@ -326,36 +402,30 @@ void SMTChecker::arithmeticOperation(BinaryOperation const& _op)
case Token::Add:
case Token::Sub:
case Token::Mul:
+ case Token::Div:
{
solAssert(_op.annotation().commonType, "");
solAssert(_op.annotation().commonType->category() == Type::Category::Integer, "");
+ auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType);
smt::Expression left(expr(_op.leftExpression()));
smt::Expression right(expr(_op.rightExpression()));
Token::Value op = _op.getOperator();
smt::Expression value(
op == Token::Add ? left + right :
op == Token::Sub ? left - right :
+ op == Token::Div ? division(left, right, intType) :
/*op == Token::Mul*/ left * right
);
- // Overflow check
- auto const& intType = dynamic_cast<IntegerType const&>(*_op.annotation().commonType);
- checkCondition(
- value < minValue(intType),
- _op.location(),
- "Underflow (resulting value less than " + formatNumber(intType.minValue()) + ")",
- "value",
- &value
- );
- checkCondition(
- value > maxValue(intType),
- _op.location(),
- "Overflow (resulting value larger than " + formatNumber(intType.maxValue()) + ")",
- "value",
- &value
- );
+ if (_op.getOperator() == Token::Div)
+ {
+ checkCondition(right == 0, _op.location(), "Division by zero", "value", &right);
+ m_interface->addAssertion(right != 0);
+ }
+
+ checkUnderOverflow(value, intType, _op.location());
- m_interface->addAssertion(expr(_op) == value);
+ defineExpr(_op, value);
break;
}
default:
@@ -383,7 +453,7 @@ void SMTChecker::compareOperation(BinaryOperation const& _op)
/*op == Token::GreaterThanOrEqual*/ (left >= right)
);
// TODO: check that other values for op are not possible.
- m_interface->addAssertion(expr(_op) == value);
+ defineExpr(_op, value);
}
else
m_errorReporter.warning(
@@ -400,9 +470,9 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op)
{
// @TODO check that both of them are not constant
if (_op.getOperator() == Token::And)
- m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) && expr(_op.rightExpression()));
+ defineExpr(_op, expr(_op.leftExpression()) && expr(_op.rightExpression()));
else
- m_interface->addAssertion(expr(_op) == expr(_op.leftExpression()) || expr(_op.rightExpression()));
+ defineExpr(_op, expr(_op.leftExpression()) || expr(_op.rightExpression()));
}
else
m_errorReporter.warning(
@@ -411,30 +481,50 @@ void SMTChecker::booleanOperation(BinaryOperation const& _op)
);
}
-void SMTChecker::assignment(Declaration const& _variable, Expression const& _value)
+smt::Expression SMTChecker::division(smt::Expression _left, smt::Expression _right, IntegerType const& _type)
{
- // TODO more checks?
- // TODO add restrictions about type (might be assignment from smaller type)
- m_interface->addAssertion(newValue(_variable) == expr(_value));
+ // Signed division in SMTLIB2 rounds differently for negative division.
+ if (_type.isSigned())
+ return (smt::Expression::ite(
+ _left >= 0,
+ smt::Expression::ite(_right >= 0, _left / _right, 0 - (_left / (0 - _right))),
+ smt::Expression::ite(_right >= 0, 0 - ((0 - _left) / _right), (0 - _left) / (0 - _right))
+ ));
+ else
+ return _left / _right;
+}
+
+void SMTChecker::assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location)
+{
+ assignment(_variable, expr(_value), _location);
}
-void SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition)
+void SMTChecker::assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location)
{
- visitBranch(_statement, &_condition);
+ TypePointer type = _variable.type();
+ if (auto const* intType = dynamic_cast<IntegerType const*>(type.get()))
+ checkUnderOverflow(_value, *intType, _location);
+ m_interface->addAssertion(newValue(_variable) == _value);
}
-void SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition)
+SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression _condition)
+{
+ return visitBranch(_statement, &_condition);
+}
+
+SMTChecker::VariableSequenceCounters SMTChecker::visitBranch(Statement const& _statement, smt::Expression const* _condition)
{
VariableSequenceCounters sequenceCountersStart = m_currentSequenceCounter;
- m_interface->push();
if (_condition)
- m_interface->addAssertion(*_condition);
+ pushPathCondition(*_condition);
_statement.accept(*this);
- m_interface->pop();
+ if (_condition)
+ popPathCondition();
m_conditionalExecutionHappened = true;
- m_currentSequenceCounter = sequenceCountersStart;
+ std::swap(sequenceCountersStart, m_currentSequenceCounter);
+ return sequenceCountersStart;
}
void SMTChecker::checkCondition(
@@ -446,7 +536,7 @@ void SMTChecker::checkCondition(
)
{
m_interface->push();
- m_interface->addAssertion(_condition);
+ addPathConjoinedExpression(_condition);
vector<smt::Expression> expressionsToEvaluate;
vector<string> expressionNames;
@@ -472,7 +562,7 @@ void SMTChecker::checkCondition(
}
smt::CheckResult result;
vector<string> values;
- tie(result, values) = checkSatisifableAndGenerateModel(expressionsToEvaluate);
+ tie(result, values) = checkSatisfiableAndGenerateModel(expressionsToEvaluate);
string conditionalComment;
if (m_conditionalExecutionHappened)
@@ -518,13 +608,13 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co
return;
m_interface->push();
- m_interface->addAssertion(expr(_condition));
- auto positiveResult = checkSatisifable();
+ addPathConjoinedExpression(expr(_condition));
+ auto positiveResult = checkSatisfiable();
m_interface->pop();
m_interface->push();
- m_interface->addAssertion(!expr(_condition));
- auto negatedResult = checkSatisifable();
+ addPathConjoinedExpression(!expr(_condition));
+ auto negatedResult = checkSatisfiable();
m_interface->pop();
if (positiveResult == smt::CheckResult::ERROR || negatedResult == smt::CheckResult::ERROR)
@@ -554,7 +644,7 @@ void SMTChecker::checkBooleanNotConstant(Expression const& _condition, string co
}
pair<smt::CheckResult, vector<string>>
-SMTChecker::checkSatisifableAndGenerateModel(vector<smt::Expression> const& _expressionsToEvaluate)
+SMTChecker::checkSatisfiableAndGenerateModel(vector<smt::Expression> const& _expressionsToEvaluate)
{
smt::CheckResult result;
vector<string> values;
@@ -584,9 +674,9 @@ SMTChecker::checkSatisifableAndGenerateModel(vector<smt::Expression> const& _exp
return make_pair(result, values);
}
-smt::CheckResult SMTChecker::checkSatisifable()
+smt::CheckResult SMTChecker::checkSatisfiable()
{
- return checkSatisifableAndGenerateModel({}).first;
+ return checkSatisfiableAndGenerateModel({}).first;
}
void SMTChecker::initializeLocalVariables(FunctionDefinition const& _function)
@@ -614,6 +704,22 @@ void SMTChecker::resetVariables(vector<Declaration const*> _variables)
}
}
+void SMTChecker::mergeVariables(vector<Declaration const*> const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse)
+{
+ set<Declaration const*> uniqueVars(_variables.begin(), _variables.end());
+ for (auto const* decl: uniqueVars)
+ {
+ int trueCounter = _countersEndTrue.at(decl);
+ int falseCounter = _countersEndFalse.at(decl);
+ solAssert(trueCounter != falseCounter, "");
+ m_interface->addAssertion(newValue(*decl) == smt::Expression::ite(
+ _condition,
+ valueAtSequence(*decl, trueCounter),
+ valueAtSequence(*decl, falseCounter))
+ );
+ }
+}
+
bool SMTChecker::createVariable(VariableDeclaration const& _varDecl)
{
if (dynamic_cast<IntegerType const*>(_varDecl.type().get()))
@@ -696,6 +802,18 @@ smt::Expression SMTChecker::expr(Expression const& _e)
{
if (!m_expressions.count(&_e))
{
+ m_errorReporter.warning(_e.location(), "Internal error: Expression undefined for SMT solver." );
+ createExpr(_e);
+ }
+ return m_expressions.at(&_e);
+}
+
+void SMTChecker::createExpr(Expression const& _e)
+{
+ if (m_expressions.count(&_e))
+ m_errorReporter.warning(_e.location(), "Internal error: Expression created twice in SMT solver." );
+ else
+ {
solAssert(_e.annotation().type, "");
switch (_e.annotation().type->category())
{
@@ -716,7 +834,12 @@ smt::Expression SMTChecker::expr(Expression const& _e)
solAssert(false, "Type not implemented.");
}
}
- return m_expressions.at(&_e);
+}
+
+void SMTChecker::defineExpr(Expression const& _e, smt::Expression _value)
+{
+ createExpr(_e);
+ m_interface->addAssertion(expr(_e) == _value);
}
smt::Expression SMTChecker::var(Declaration const& _decl)
@@ -724,3 +847,31 @@ smt::Expression SMTChecker::var(Declaration const& _decl)
solAssert(m_variables.count(&_decl), "");
return m_variables.at(&_decl);
}
+
+void SMTChecker::popPathCondition()
+{
+ solAssert(m_pathConditions.size() > 0, "Cannot pop path condition, empty.");
+ m_pathConditions.pop_back();
+}
+
+void SMTChecker::pushPathCondition(smt::Expression const& _e)
+{
+ m_pathConditions.push_back(currentPathConditions() && _e);
+}
+
+smt::Expression SMTChecker::currentPathConditions()
+{
+ if (m_pathConditions.size() == 0)
+ return smt::Expression(true);
+ return m_pathConditions.back();
+}
+
+void SMTChecker::addPathConjoinedExpression(smt::Expression const& _e)
+{
+ m_interface->addAssertion(currentPathConditions() && _e);
+}
+
+void SMTChecker::addPathImpliedExpression(smt::Expression const& _e)
+{
+ m_interface->addAssertion(smt::Expression::implies(currentPathConditions(), _e));
+}
diff --git a/libsolidity/formal/SMTChecker.h b/libsolidity/formal/SMTChecker.h
index 8e07d74d..b57f0f96 100644
--- a/libsolidity/formal/SMTChecker.h
+++ b/libsolidity/formal/SMTChecker.h
@@ -26,6 +26,7 @@
#include <map>
#include <string>
+#include <vector>
namespace dev
{
@@ -57,6 +58,7 @@ private:
virtual void endVisit(ExpressionStatement const& _node) override;
virtual void endVisit(Assignment const& _node) override;
virtual void endVisit(TupleExpression const& _node) override;
+ virtual void endVisit(UnaryOperation const& _node) override;
virtual void endVisit(BinaryOperation const& _node) override;
virtual void endVisit(FunctionCall const& _node) override;
virtual void endVisit(Identifier const& _node) override;
@@ -66,12 +68,21 @@ private:
void compareOperation(BinaryOperation const& _op);
void booleanOperation(BinaryOperation const& _op);
- void assignment(Declaration const& _variable, Expression const& _value);
+ /// Division expression in the given type. Requires special treatment because
+ /// of rounding for signed division.
+ smt::Expression division(smt::Expression _left, smt::Expression _right, IntegerType const& _type);
- // Visits the branch given by the statement, pushes and pops the SMT checker.
- // @param _condition if present, asserts that this condition is true within the branch.
- void visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr);
- void visitBranch(Statement const& _statement, smt::Expression _condition);
+ void assignment(Declaration const& _variable, Expression const& _value, SourceLocation const& _location);
+ void assignment(Declaration const& _variable, smt::Expression const& _value, SourceLocation const& _location);
+
+ /// Maps a variable to an SSA index.
+ using VariableSequenceCounters = std::map<Declaration const*, int>;
+
+ /// Visits the branch given by the statement, pushes and pops the current path conditions.
+ /// @param _condition if present, asserts that this condition is true within the branch.
+ /// @returns the variable sequence counter after visiting the branch.
+ VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression const* _condition = nullptr);
+ VariableSequenceCounters visitBranch(Statement const& _statement, smt::Expression _condition);
/// Check that a condition can be satisfied.
void checkCondition(
@@ -88,14 +99,21 @@ private:
Expression const& _condition,
std::string const& _description
);
+ /// Checks that the value is in the range given by the type.
+ void checkUnderOverflow(smt::Expression _value, IntegerType const& _Type, SourceLocation const& _location);
+
std::pair<smt::CheckResult, std::vector<std::string>>
- checkSatisifableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
+ checkSatisfiableAndGenerateModel(std::vector<smt::Expression> const& _expressionsToEvaluate);
- smt::CheckResult checkSatisifable();
+ smt::CheckResult checkSatisfiable();
void initializeLocalVariables(FunctionDefinition const& _function);
void resetVariables(std::vector<Declaration const*> _variables);
+ /// Given two different branches and the touched variables,
+ /// merge the touched variables into after-branch ite variables
+ /// using the branch condition as guard.
+ void mergeVariables(std::vector<Declaration const*> const& _variables, smt::Expression const& _condition, VariableSequenceCounters const& _countersEndTrue, VariableSequenceCounters const& _countersEndFalse);
/// Tries to create an uninitialized variable and returns true on success.
/// This fails if the type is not supported.
bool createVariable(VariableDeclaration const& _varDecl);
@@ -124,15 +142,27 @@ private:
static smt::Expression minValue(IntegerType const& _t);
static smt::Expression maxValue(IntegerType const& _t);
- using VariableSequenceCounters = std::map<Declaration const*, int>;
-
- /// Returns the expression corresponding to the AST node. Creates a new expression
- /// if it does not exist yet.
+ /// Returns the expression corresponding to the AST node. Throws if the expression does not exist.
smt::Expression expr(Expression const& _e);
+ /// Creates the expression (value can be arbitrary)
+ void createExpr(Expression const& _e);
+ /// Creates the expression and sets its value.
+ void defineExpr(Expression const& _e, smt::Expression _value);
/// Returns the function declaration corresponding to the given variable.
/// The function takes one argument which is the "sequence number".
smt::Expression var(Declaration const& _decl);
+ /// Adds a new path condition
+ void pushPathCondition(smt::Expression const& _e);
+ /// Remove the last path condition
+ void popPathCondition();
+ /// Returns the conjunction of all path conditions or True if empty
+ smt::Expression currentPathConditions();
+ /// Conjoin the current path conditions with the given parameter and add to the solver
+ void addPathConjoinedExpression(smt::Expression const& _e);
+ /// Add to the solver: the given expression implied by the current path conditions
+ void addPathImpliedExpression(smt::Expression const& _e);
+
std::shared_ptr<smt::SolverInterface> m_interface;
std::shared_ptr<VariableUsage> m_variableUsage;
bool m_conditionalExecutionHappened = false;
@@ -140,6 +170,7 @@ private:
std::map<Declaration const*, int> m_nextFreeSequenceCounter;
std::map<Expression const*, smt::Expression> m_expressions;
std::map<Declaration const*, smt::Expression> m_variables;
+ std::vector<smt::Expression> m_pathConditions;
ErrorReporter& m_errorReporter;
FunctionDefinition const* m_currentFunction = nullptr;
diff --git a/libsolidity/formal/SolverInterface.h b/libsolidity/formal/SolverInterface.h
index c9adf863..88487310 100644
--- a/libsolidity/formal/SolverInterface.h
+++ b/libsolidity/formal/SolverInterface.h
@@ -72,6 +72,11 @@ public:
}, _trueValue.sort);
}
+ static Expression implies(Expression _a, Expression _b)
+ {
+ return !std::move(_a) || std::move(_b);
+ }
+
friend Expression operator!(Expression _a)
{
return Expression("not", std::move(_a), Sort::Bool);
@@ -120,6 +125,10 @@ public:
{
return Expression("*", std::move(_a), std::move(_b), Sort::Int);
}
+ friend Expression operator/(Expression _a, Expression _b)
+ {
+ return Expression("/", std::move(_a), std::move(_b), Sort::Int);
+ }
Expression operator()(Expression _a) const
{
solAssert(
diff --git a/libsolidity/formal/Z3Interface.cpp b/libsolidity/formal/Z3Interface.cpp
index e5c1aef4..769e6edb 100644
--- a/libsolidity/formal/Z3Interface.cpp
+++ b/libsolidity/formal/Z3Interface.cpp
@@ -127,7 +127,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
{">=", 2},
{"+", 2},
{"-", 2},
- {"*", 2}
+ {"*", 2},
+ {"/", 2}
};
string const& n = _expr.name;
if (m_functions.count(n))
@@ -173,6 +174,8 @@ z3::expr Z3Interface::toZ3Expr(Expression const& _expr)
return arguments[0] - arguments[1];
else if (n == "*")
return arguments[0] * arguments[1];
+ else if (n == "/")
+ return arguments[0] / arguments[1];
// Cannot reach here.
solAssert(false, "");
return arguments[0];
diff --git a/libsolidity/inlineasm/AsmAnalysis.cpp b/libsolidity/inlineasm/AsmAnalysis.cpp
index 2804ddfc..2d6e58de 100644
--- a/libsolidity/inlineasm/AsmAnalysis.cpp
+++ b/libsolidity/inlineasm/AsmAnalysis.cpp
@@ -54,14 +54,15 @@ bool AsmAnalyzer::analyze(Block const& _block)
bool AsmAnalyzer::operator()(Label const& _label)
{
- solAssert(!m_julia, "");
+ solAssert(m_flavour == AsmFlavour::Loose, "");
m_info.stackHeightInfo[&_label] = m_stackHeight;
+ warnOnInstructions(solidity::Instruction::JUMPDEST, _label.location);
return true;
}
bool AsmAnalyzer::operator()(assembly::Instruction const& _instruction)
{
- solAssert(!m_julia, "");
+ solAssert(m_flavour == AsmFlavour::Loose, "");
auto const& info = instructionInfo(_instruction.instruction);
m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instruction] = m_stackHeight;
@@ -140,22 +141,34 @@ bool AsmAnalyzer::operator()(assembly::Identifier const& _identifier)
bool AsmAnalyzer::operator()(FunctionalInstruction const& _instr)
{
- solAssert(!m_julia, "");
+ solAssert(m_flavour != AsmFlavour::IULIA, "");
bool success = true;
for (auto const& arg: _instr.arguments | boost::adaptors::reversed)
if (!expectExpression(arg))
success = false;
// Parser already checks that the number of arguments is correct.
- solAssert(instructionInfo(_instr.instruction.instruction).args == int(_instr.arguments.size()), "");
- if (!(*this)(_instr.instruction))
- success = false;
+ auto const& info = instructionInfo(_instr.instruction);
+ solAssert(info.args == int(_instr.arguments.size()), "");
+ m_stackHeight += info.ret - info.args;
m_info.stackHeightInfo[&_instr] = m_stackHeight;
+ warnOnInstructions(_instr.instruction, _instr.location);
+ return success;
+}
+
+bool AsmAnalyzer::operator()(assembly::ExpressionStatement const& _statement)
+{
+ size_t initialStackHeight = m_stackHeight;
+ bool success = boost::apply_visitor(*this, _statement.expression);
+ if (m_flavour != AsmFlavour::Loose)
+ if (!expectDeposit(0, initialStackHeight, _statement.location))
+ success = false;
+ m_info.stackHeightInfo[&_statement] = m_stackHeight;
return success;
}
bool AsmAnalyzer::operator()(assembly::StackAssignment const& _assignment)
{
- solAssert(!m_julia, "");
+ solAssert(m_flavour == AsmFlavour::Loose, "");
bool success = checkAssignment(_assignment.variableName, size_t(-1));
m_info.stackHeightInfo[&_assignment] = m_stackHeight;
return success;
@@ -217,14 +230,14 @@ bool AsmAnalyzer::operator()(assembly::FunctionDefinition const& _funDef)
Block const* virtualBlock = m_info.virtualBlocks.at(&_funDef).get();
solAssert(virtualBlock, "");
Scope& varScope = scope(virtualBlock);
- for (auto const& var: _funDef.arguments + _funDef.returns)
+ for (auto const& var: _funDef.parameters + _funDef.returnVariables)
{
expectValidType(var.type, var.location);
m_activeVariables.insert(&boost::get<Scope::Variable>(varScope.identifiers.at(var.name)));
}
int const stackHeight = m_stackHeight;
- m_stackHeight = _funDef.arguments.size() + _funDef.returns.size();
+ m_stackHeight = _funDef.parameters.size() + _funDef.returnVariables.size();
bool success = (*this)(_funDef.body);
@@ -405,13 +418,13 @@ bool AsmAnalyzer::operator()(Block const& _block)
return success;
}
-bool AsmAnalyzer::expectExpression(Statement const& _statement)
+bool AsmAnalyzer::expectExpression(Expression const& _expr)
{
bool success = true;
int const initialHeight = m_stackHeight;
- if (!boost::apply_visitor(*this, _statement))
+ if (!boost::apply_visitor(*this, _expr))
success = false;
- if (!expectDeposit(1, initialHeight, locationOf(_statement)))
+ if (!expectDeposit(1, initialHeight, locationOf(_expr)))
success = false;
return success;
}
@@ -495,7 +508,7 @@ Scope& AsmAnalyzer::scope(Block const* _block)
}
void AsmAnalyzer::expectValidType(string const& type, SourceLocation const& _location)
{
- if (!m_julia)
+ if (m_flavour != AsmFlavour::IULIA)
return;
if (!builtinTypes.count(type))
@@ -522,11 +535,11 @@ void AsmAnalyzer::warnOnInstructions(solidity::Instruction _instr, SourceLocatio
"the Metropolis hard fork. Before that it acts as an invalid instruction."
);
- if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI)
+ if (_instr == solidity::Instruction::JUMP || _instr == solidity::Instruction::JUMPI || _instr == solidity::Instruction::JUMPDEST)
m_errorReporter.warning(
_location,
- "Jump instructions are low-level EVM features that can lead to "
+ "Jump instructions and labels are low-level EVM features that can lead to "
"incorrect stack access. Because of that they are discouraged. "
- "Please consider using \"switch\" or \"for\" statements instead."
+ "Please consider using \"switch\", \"if\" or \"for\" statements instead."
);
}
diff --git a/libsolidity/inlineasm/AsmAnalysis.h b/libsolidity/inlineasm/AsmAnalysis.h
index e484b876..7a81dbf8 100644
--- a/libsolidity/inlineasm/AsmAnalysis.h
+++ b/libsolidity/inlineasm/AsmAnalysis.h
@@ -54,9 +54,9 @@ public:
explicit AsmAnalyzer(
AsmAnalysisInfo& _analysisInfo,
ErrorReporter& _errorReporter,
- bool _julia = false,
+ AsmFlavour _flavour = AsmFlavour::Loose,
julia::ExternalIdentifierAccess::Resolver const& _resolver = julia::ExternalIdentifierAccess::Resolver()
- ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_julia(_julia) {}
+ ): m_resolver(_resolver), m_info(_analysisInfo), m_errorReporter(_errorReporter), m_flavour(_flavour) {}
bool analyze(assembly::Block const& _block);
@@ -65,6 +65,7 @@ public:
bool operator()(assembly::Identifier const&);
bool operator()(assembly::FunctionalInstruction const& _functionalInstruction);
bool operator()(assembly::Label const& _label);
+ bool operator()(assembly::ExpressionStatement const&);
bool operator()(assembly::StackAssignment const&);
bool operator()(assembly::Assignment const& _assignment);
bool operator()(assembly::VariableDeclaration const& _variableDeclaration);
@@ -77,7 +78,7 @@ public:
private:
/// Visits the statement and expects it to deposit one item onto the stack.
- bool expectExpression(Statement const& _statement);
+ bool expectExpression(Expression const& _expr);
bool expectDeposit(int _deposit, int _oldHeight, SourceLocation const& _location);
/// Verifies that a variable to be assigned to exists and has the same size
@@ -96,7 +97,7 @@ private:
std::set<Scope::Variable const*> m_activeVariables;
AsmAnalysisInfo& m_info;
ErrorReporter& m_errorReporter;
- bool m_julia = false;
+ AsmFlavour m_flavour = AsmFlavour::Loose;
};
}
diff --git a/libsolidity/inlineasm/AsmData.h b/libsolidity/inlineasm/AsmData.h
index a792a1b8..2982d5e0 100644
--- a/libsolidity/inlineasm/AsmData.h
+++ b/libsolidity/inlineasm/AsmData.h
@@ -58,23 +58,25 @@ struct StackAssignment { SourceLocation location; Identifier variableName; };
/// Multiple assignment ("x, y := f()"), where the left hand side variables each occupy
/// a single stack slot and expects a single expression on the right hand returning
/// the same amount of items as the number of variables.
-struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Statement> value; };
+struct Assignment { SourceLocation location; std::vector<Identifier> variableNames; std::shared_ptr<Expression> value; };
/// Functional instruction, e.g. "mul(mload(20:u256), add(2:u256, x))"
-struct FunctionalInstruction { SourceLocation location; Instruction instruction; std::vector<Statement> arguments; };
-struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Statement> arguments; };
+struct FunctionalInstruction { SourceLocation location; solidity::Instruction instruction; std::vector<Expression> arguments; };
+struct FunctionCall { SourceLocation location; Identifier functionName; std::vector<Expression> arguments; };
+/// Statement that contains only a single expression
+struct ExpressionStatement { SourceLocation location; Expression expression; };
/// Block-scope variable declaration ("let x:u256 := mload(20:u256)"), non-hoisted
-struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Statement> value; };
+struct VariableDeclaration { SourceLocation location; TypedNameList variables; std::shared_ptr<Expression> value; };
/// Block that creates a scope (frees declared stack variables)
struct Block { SourceLocation location; std::vector<Statement> statements; };
/// Function definition ("function f(a, b) -> (d, e) { ... }")
-struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList arguments; TypedNameList returns; Block body; };
+struct FunctionDefinition { SourceLocation location; std::string name; TypedNameList parameters; TypedNameList returnVariables; Block body; };
/// Conditional execution without "else" part.
-struct If { SourceLocation location; std::shared_ptr<Statement> condition; Block body; };
+struct If { SourceLocation location; std::shared_ptr<Expression> condition; Block body; };
/// Switch case or default case
struct Case { SourceLocation location; std::shared_ptr<Literal> value; Block body; };
/// Switch statement
-struct Switch { SourceLocation location; std::shared_ptr<Statement> expression; std::vector<Case> cases; };
-struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Statement> condition; Block post; Block body; };
+struct Switch { SourceLocation location; std::shared_ptr<Expression> expression; std::vector<Case> cases; };
+struct ForLoop { SourceLocation location; Block pre; std::shared_ptr<Expression> condition; Block post; Block body; };
struct LocationExtractor: boost::static_visitor<SourceLocation>
{
diff --git a/libsolidity/inlineasm/AsmDataForward.h b/libsolidity/inlineasm/AsmDataForward.h
index d627b41a..3a9600fe 100644
--- a/libsolidity/inlineasm/AsmDataForward.h
+++ b/libsolidity/inlineasm/AsmDataForward.h
@@ -43,10 +43,22 @@ struct FunctionDefinition;
struct FunctionCall;
struct If;
struct Switch;
+struct Case;
struct ForLoop;
+struct ExpressionStatement;
struct Block;
-using Statement = boost::variant<Instruction, Literal, Label, StackAssignment, Identifier, Assignment, FunctionCall, FunctionalInstruction, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
+struct TypedName;
+
+using Expression = boost::variant<FunctionalInstruction, FunctionCall, Identifier, Literal>;
+using Statement = boost::variant<ExpressionStatement, Instruction, Label, StackAssignment, Assignment, VariableDeclaration, FunctionDefinition, If, Switch, ForLoop, Block>;
+
+enum class AsmFlavour
+{
+ Loose, // no types, EVM instructions as function, jumps and direct stack manipulations
+ Strict, // no types, EVM instructions as functions, but no jumps and no direct stack manipulations
+ IULIA // same as Strict mode with types
+};
}
}
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 8f171005..306b07e6 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -77,9 +77,7 @@ assembly::Statement Parser::parseStatement()
{
assembly::If _if = createWithLocation<assembly::If>();
m_scanner->next();
- _if.condition = make_shared<Statement>(parseExpression());
- if (_if.condition->type() == typeid(assembly::Instruction))
- fatalParserError("Instructions are not supported as conditions for if - try to append \"()\".");
+ _if.condition = make_shared<Expression>(parseExpression());
_if.body = parseBlock();
return _if;
}
@@ -87,9 +85,7 @@ assembly::Statement Parser::parseStatement()
{
assembly::Switch _switch = createWithLocation<assembly::Switch>();
m_scanner->next();
- _switch.expression = make_shared<Statement>(parseExpression());
- if (_switch.expression->type() == typeid(assembly::Instruction))
- fatalParserError("Instructions are not supported as expressions for switch - try to append \"()\".");
+ _switch.expression = make_shared<Expression>(parseExpression());
while (m_scanner->currentToken() == Token::Case)
_switch.cases.emplace_back(parseCase());
if (m_scanner->currentToken() == Token::Default)
@@ -107,14 +103,14 @@ assembly::Statement Parser::parseStatement()
return parseForLoop();
case Token::Assign:
{
- if (m_julia)
+ if (m_flavour != AsmFlavour::Loose)
break;
assembly::StackAssignment assignment = createWithLocation<assembly::StackAssignment>();
advance();
expectToken(Token::Colon);
assignment.variableName.location = location();
assignment.variableName.name = currentLiteral();
- if (!m_julia && instructions().count(assignment.variableName.name))
+ if (instructions().count(assignment.variableName.name))
fatalParserError("Identifier expected, got instruction name.");
assignment.location.end = endPosition();
expectToken(Token::Identifier);
@@ -127,18 +123,21 @@ assembly::Statement Parser::parseStatement()
// Simple instruction (might turn into functional),
// literal,
// identifier (might turn into label or functional assignment)
- Statement statement(parseElementaryOperation(false));
+ ElementaryOperation elementary(parseElementaryOperation());
switch (currentToken())
{
case Token::LParen:
- return parseCall(std::move(statement));
+ {
+ Expression expr = parseCall(std::move(elementary));
+ return ExpressionStatement{locationOf(expr), expr};
+ }
case Token::Comma:
{
// if a comma follows, a multiple assignment is assumed
- if (statement.type() != typeid(assembly::Identifier))
+ if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Label name / variable name must precede \",\" (multiple assignment).");
- assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
+ assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
Assignment assignment = createWithLocation<Assignment>(identifier.location);
assignment.variableNames.emplace_back(identifier);
@@ -146,43 +145,43 @@ assembly::Statement Parser::parseStatement()
do
{
expectToken(Token::Comma);
- statement = parseElementaryOperation(false);
- if (statement.type() != typeid(assembly::Identifier))
+ elementary = parseElementaryOperation();
+ if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Variable name expected in multiple assignemnt.");
- assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(statement));
+ assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary));
}
while (currentToken() == Token::Comma);
expectToken(Token::Colon);
expectToken(Token::Assign);
- assignment.value.reset(new Statement(parseExpression()));
+ assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return assignment;
}
case Token::Colon:
{
- if (statement.type() != typeid(assembly::Identifier))
+ if (elementary.type() != typeid(assembly::Identifier))
fatalParserError("Label name / variable name must precede \":\".");
- assembly::Identifier const& identifier = boost::get<assembly::Identifier>(statement);
+ assembly::Identifier const& identifier = boost::get<assembly::Identifier>(elementary);
advance();
// identifier:=: should be parsed as identifier: =: (i.e. a label),
// while identifier:= (being followed by a non-colon) as identifier := (assignment).
if (currentToken() == Token::Assign && peekNextToken() != Token::Colon)
{
assembly::Assignment assignment = createWithLocation<assembly::Assignment>(identifier.location);
- if (!m_julia && instructions().count(identifier.name))
+ if (m_flavour != AsmFlavour::IULIA && instructions().count(identifier.name))
fatalParserError("Cannot use instruction names for identifier names.");
advance();
assignment.variableNames.emplace_back(identifier);
- assignment.value.reset(new Statement(parseExpression()));
+ assignment.value.reset(new Expression(parseExpression()));
assignment.location.end = locationOf(*assignment.value).end;
return assignment;
}
else
{
// label
- if (m_julia)
+ if (m_flavour != AsmFlavour::Loose)
fatalParserError("Labels are not supported.");
Label label = createWithLocation<Label>(identifier.location);
label.name = identifier.name;
@@ -190,11 +189,25 @@ assembly::Statement Parser::parseStatement()
}
}
default:
- if (m_julia)
+ if (m_flavour != AsmFlavour::Loose)
fatalParserError("Call or assignment expected.");
break;
}
- return statement;
+ if (elementary.type() == typeid(assembly::Identifier))
+ {
+ Expression expr = boost::get<assembly::Identifier>(elementary);
+ return ExpressionStatement{locationOf(expr), expr};
+ }
+ else if (elementary.type() == typeid(assembly::Literal))
+ {
+ Expression expr = boost::get<assembly::Literal>(elementary);
+ return ExpressionStatement{locationOf(expr), expr};
+ }
+ else
+ {
+ solAssert(elementary.type() == typeid(assembly::Instruction), "Invalid elementary operation.");
+ return boost::get<assembly::Instruction>(elementary);
+ }
}
assembly::Case Parser::parseCase()
@@ -206,10 +219,10 @@ assembly::Case Parser::parseCase()
else if (m_scanner->currentToken() == Token::Case)
{
m_scanner->next();
- assembly::Statement statement = parseElementaryOperation();
- if (statement.type() != typeid(assembly::Literal))
+ ElementaryOperation literal = parseElementaryOperation();
+ if (literal.type() != typeid(assembly::Literal))
fatalParserError("Literal expected.");
- _case.value = make_shared<Literal>(std::move(boost::get<assembly::Literal>(statement)));
+ _case.value = make_shared<Literal>(boost::get<assembly::Literal>(std::move(literal)));
}
else
fatalParserError("Case or default case expected.");
@@ -224,22 +237,39 @@ assembly::ForLoop Parser::parseForLoop()
ForLoop forLoop = createWithLocation<ForLoop>();
expectToken(Token::For);
forLoop.pre = parseBlock();
- forLoop.condition = make_shared<Statement>(parseExpression());
- if (forLoop.condition->type() == typeid(assembly::Instruction))
- fatalParserError("Instructions are not supported as conditions for the for statement.");
+ forLoop.condition = make_shared<Expression>(parseExpression());
forLoop.post = parseBlock();
forLoop.body = parseBlock();
forLoop.location.end = forLoop.body.location.end;
return forLoop;
}
-assembly::Statement Parser::parseExpression()
+assembly::Expression Parser::parseExpression()
{
RecursionGuard recursionGuard(*this);
- Statement operation = parseElementaryOperation(true);
+ // In strict mode, this might parse a plain Instruction, but
+ // it will be converted to a FunctionalInstruction inside
+ // parseCall below.
+ ElementaryOperation operation = parseElementaryOperation();
if (operation.type() == typeid(Instruction))
{
Instruction const& instr = boost::get<Instruction>(operation);
+ // Disallow instructions returning multiple values (and DUP/SWAP) as expression.
+ if (
+ instructionInfo(instr.instruction).ret != 1 ||
+ isDupInstruction(instr.instruction) ||
+ isSwapInstruction(instr.instruction)
+ )
+ fatalParserError(
+ "Instruction \"" +
+ instructionNames().at(instr.instruction) +
+ "\" not allowed in this context."
+ );
+ if (m_flavour != AsmFlavour::Loose && currentToken() != Token::LParen)
+ fatalParserError(
+ "Non-functional instructions are not allowed in this context."
+ );
+ // Enforce functional notation for instructions requiring multiple arguments.
int args = instructionInfo(instr.instruction).args;
if (args > 0 && currentToken() != Token::LParen)
fatalParserError(string(
@@ -252,8 +282,20 @@ assembly::Statement Parser::parseExpression()
}
if (currentToken() == Token::LParen)
return parseCall(std::move(operation));
+ else if (operation.type() == typeid(Instruction))
+ {
+ // Instructions not taking arguments are allowed as expressions.
+ solAssert(m_flavour == AsmFlavour::Loose, "");
+ Instruction& instr = boost::get<Instruction>(operation);
+ return FunctionalInstruction{std::move(instr.location), instr.instruction, {}};
+ }
+ else if (operation.type() == typeid(assembly::Identifier))
+ return boost::get<assembly::Identifier>(operation);
else
- return operation;
+ {
+ solAssert(operation.type() == typeid(assembly::Literal), "");
+ return boost::get<assembly::Literal>(operation);
+ }
}
std::map<string, dev::solidity::Instruction> const& Parser::instructions()
@@ -296,10 +338,10 @@ std::map<dev::solidity::Instruction, string> const& Parser::instructionNames()
return s_instructionNames;
}
-assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
+Parser::ElementaryOperation Parser::parseElementaryOperation()
{
RecursionGuard recursionGuard(*this);
- Statement ret;
+ ElementaryOperation ret;
switch (currentToken())
{
case Token::Identifier:
@@ -317,15 +359,9 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
else
literal = currentLiteral();
// first search the set of instructions.
- if (!m_julia && instructions().count(literal))
+ if (m_flavour != AsmFlavour::IULIA && instructions().count(literal))
{
dev::solidity::Instruction const& instr = instructions().at(literal);
- if (_onlySinglePusher)
- {
- InstructionInfo info = dev::solidity::instructionInfo(instr);
- if (info.ret != 1)
- fatalParserError("Instruction \"" + literal + "\" not allowed in this context.");
- }
ret = Instruction{location(), instr};
}
else
@@ -364,7 +400,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
""
};
advance();
- if (m_julia)
+ if (m_flavour == AsmFlavour::IULIA)
{
expectToken(Token::Colon);
literal.location.end = endPosition();
@@ -377,7 +413,7 @@ assembly::Statement Parser::parseElementaryOperation(bool _onlySinglePusher)
}
default:
fatalParserError(
- m_julia ?
+ m_flavour == AsmFlavour::IULIA ?
"Literal or identifier expected." :
"Literal, identifier or instruction expected."
);
@@ -402,7 +438,7 @@ assembly::VariableDeclaration Parser::parseVariableDeclaration()
{
expectToken(Token::Colon);
expectToken(Token::Assign);
- varDecl.value.reset(new Statement(parseExpression()));
+ varDecl.value.reset(new Expression(parseExpression()));
varDecl.location.end = locationOf(*varDecl.value).end;
}
else
@@ -419,7 +455,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::LParen);
while (currentToken() != Token::RParen)
{
- funDef.arguments.emplace_back(parseTypedName());
+ funDef.parameters.emplace_back(parseTypedName());
if (currentToken() == Token::RParen)
break;
expectToken(Token::Comma);
@@ -431,7 +467,7 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
expectToken(Token::GreaterThan);
while (true)
{
- funDef.returns.emplace_back(parseTypedName());
+ funDef.returnVariables.emplace_back(parseTypedName());
if (currentToken() == Token::LBrace)
break;
expectToken(Token::Comma);
@@ -442,16 +478,17 @@ assembly::FunctionDefinition Parser::parseFunctionDefinition()
return funDef;
}
-assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
+assembly::Expression Parser::parseCall(Parser::ElementaryOperation&& _initialOp)
{
RecursionGuard recursionGuard(*this);
- if (_instruction.type() == typeid(Instruction))
+ if (_initialOp.type() == typeid(Instruction))
{
- solAssert(!m_julia, "Instructions are invalid in JULIA");
+ solAssert(m_flavour != AsmFlavour::IULIA, "Instructions are invalid in JULIA");
+ Instruction& instruction = boost::get<Instruction>(_initialOp);
FunctionalInstruction ret;
- ret.instruction = std::move(boost::get<Instruction>(_instruction));
- ret.location = ret.instruction.location;
- solidity::Instruction instr = ret.instruction.instruction;
+ ret.instruction = instruction.instruction;
+ ret.location = std::move(instruction.location);
+ solidity::Instruction instr = ret.instruction;
InstructionInfo instrInfo = instructionInfo(instr);
if (solidity::isDupInstruction(instr))
fatalParserError("DUPi instructions not allowed for functional notation");
@@ -498,10 +535,10 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
expectToken(Token::RParen);
return ret;
}
- else if (_instruction.type() == typeid(Identifier))
+ else if (_initialOp.type() == typeid(Identifier))
{
FunctionCall ret;
- ret.functionName = std::move(boost::get<Identifier>(_instruction));
+ ret.functionName = std::move(boost::get<Identifier>(_initialOp));
ret.location = ret.functionName.location;
expectToken(Token::LParen);
while (currentToken() != Token::RParen)
@@ -517,7 +554,7 @@ assembly::Statement Parser::parseCall(assembly::Statement&& _instruction)
}
else
fatalParserError(
- m_julia ?
+ m_flavour == AsmFlavour::IULIA ?
"Function name expected." :
"Assembly instruction or function name required in front of \"(\")"
);
@@ -530,7 +567,7 @@ TypedName Parser::parseTypedName()
RecursionGuard recursionGuard(*this);
TypedName typedName = createWithLocation<TypedName>();
typedName.name = expectAsmIdentifier();
- if (m_julia)
+ if (m_flavour == AsmFlavour::IULIA)
{
expectToken(Token::Colon);
typedName.location.end = endPosition();
@@ -542,12 +579,18 @@ TypedName Parser::parseTypedName()
string Parser::expectAsmIdentifier()
{
string name = currentLiteral();
- if (m_julia)
+ if (m_flavour == AsmFlavour::IULIA)
{
- if (currentToken() == Token::Bool)
+ switch (currentToken())
{
+ case Token::Return:
+ case Token::Byte:
+ case Token::Address:
+ case Token::Bool:
advance();
return name;
+ default:
+ break;
}
}
else if (instructions().count(name))
diff --git a/libsolidity/inlineasm/AsmParser.h b/libsolidity/inlineasm/AsmParser.h
index e46d1732..015aeef3 100644
--- a/libsolidity/inlineasm/AsmParser.h
+++ b/libsolidity/inlineasm/AsmParser.h
@@ -37,13 +37,16 @@ namespace assembly
class Parser: public ParserBase
{
public:
- explicit Parser(ErrorReporter& _errorReporter, bool _julia = false): ParserBase(_errorReporter), m_julia(_julia) {}
+ explicit Parser(ErrorReporter& _errorReporter, AsmFlavour _flavour = AsmFlavour::Loose):
+ ParserBase(_errorReporter), m_flavour(_flavour) {}
/// Parses an inline assembly block starting with `{` and ending with `}`.
/// @returns an empty shared pointer on error.
std::shared_ptr<Block> parse(std::shared_ptr<Scanner> const& _scanner);
protected:
+ using ElementaryOperation = boost::variant<assembly::Instruction, assembly::Literal, assembly::Identifier>;
+
/// Creates an inline assembly node with the given source location.
template <class T> T createWithLocation(SourceLocation const& _loc = SourceLocation()) const
{
@@ -65,20 +68,23 @@ protected:
Case parseCase();
ForLoop parseForLoop();
/// Parses a functional expression that has to push exactly one stack element
- Statement parseExpression();
+ assembly::Expression parseExpression();
static std::map<std::string, dev::solidity::Instruction> const& instructions();
static std::map<dev::solidity::Instruction, std::string> const& instructionNames();
- Statement parseElementaryOperation(bool _onlySinglePusher = false);
+ /// Parses an elementary operation, i.e. a literal, identifier or instruction.
+ /// This will parse instructions even in strict mode as part of the full parser
+ /// for FunctionalInstruction.
+ ElementaryOperation parseElementaryOperation();
VariableDeclaration parseVariableDeclaration();
FunctionDefinition parseFunctionDefinition();
- Statement parseCall(Statement&& _instruction);
+ assembly::Expression parseCall(ElementaryOperation&& _initialOp);
TypedName parseTypedName();
std::string expectAsmIdentifier();
static bool isValidNumberLiteral(std::string const& _literal);
private:
- bool m_julia = false;
+ AsmFlavour m_flavour = AsmFlavour::Loose;
};
}
diff --git a/libsolidity/inlineasm/AsmPrinter.cpp b/libsolidity/inlineasm/AsmPrinter.cpp
index 0f183244..bacd7a94 100644
--- a/libsolidity/inlineasm/AsmPrinter.cpp
+++ b/libsolidity/inlineasm/AsmPrinter.cpp
@@ -94,7 +94,7 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional
{
solAssert(!m_julia, "");
return
- (*this)(_functionalInstruction.instruction) +
+ boost::to_lower_copy(instructionInfo(_functionalInstruction.instruction).name) +
"(" +
boost::algorithm::join(
_functionalInstruction.arguments | boost::adaptors::transformed(boost::apply_visitor(*this)),
@@ -102,6 +102,11 @@ string AsmPrinter::operator()(assembly::FunctionalInstruction const& _functional
")";
}
+string AsmPrinter::operator()(ExpressionStatement const& _statement)
+{
+ return boost::apply_visitor(*this, _statement.expression);
+}
+
string AsmPrinter::operator()(assembly::Label const& _label)
{
solAssert(!m_julia, "");
@@ -144,17 +149,17 @@ string AsmPrinter::operator()(assembly::FunctionDefinition const& _functionDefin
{
string out = "function " + _functionDefinition.name + "(";
out += boost::algorithm::join(
- _functionDefinition.arguments | boost::adaptors::transformed(
+ _functionDefinition.parameters | boost::adaptors::transformed(
[this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
),
", "
);
out += ")";
- if (!_functionDefinition.returns.empty())
+ if (!_functionDefinition.returnVariables.empty())
{
out += " -> ";
out += boost::algorithm::join(
- _functionDefinition.returns | boost::adaptors::transformed(
+ _functionDefinition.returnVariables | boost::adaptors::transformed(
[this](TypedName argument) { return argument.name + appendTypeName(argument.type); }
),
", "
diff --git a/libsolidity/inlineasm/AsmPrinter.h b/libsolidity/inlineasm/AsmPrinter.h
index eadf81d9..5bd87aba 100644
--- a/libsolidity/inlineasm/AsmPrinter.h
+++ b/libsolidity/inlineasm/AsmPrinter.h
@@ -42,6 +42,7 @@ public:
std::string operator()(assembly::Literal const& _literal);
std::string operator()(assembly::Identifier const& _identifier);
std::string operator()(assembly::FunctionalInstruction const& _functionalInstruction);
+ std::string operator()(assembly::ExpressionStatement const& _expr);
std::string operator()(assembly::Label const& _label);
std::string operator()(assembly::StackAssignment const& _assignment);
std::string operator()(assembly::Assignment const& _assignment);
diff --git a/libsolidity/inlineasm/AsmScopeFiller.cpp b/libsolidity/inlineasm/AsmScopeFiller.cpp
index 77ae9102..86f3809c 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.cpp
+++ b/libsolidity/inlineasm/AsmScopeFiller.cpp
@@ -45,6 +45,11 @@ ScopeFiller::ScopeFiller(AsmAnalysisInfo& _info, ErrorReporter& _errorReporter):
m_currentScope = &scope(nullptr);
}
+bool ScopeFiller::operator()(ExpressionStatement const& _expr)
+{
+ return boost::apply_visitor(*this, _expr.expression);
+}
+
bool ScopeFiller::operator()(Label const& _item)
{
if (!m_currentScope->registerLabel(_item.name))
@@ -71,10 +76,10 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
{
bool success = true;
vector<Scope::JuliaType> arguments;
- for (auto const& _argument: _funDef.arguments)
+ for (auto const& _argument: _funDef.parameters)
arguments.push_back(_argument.type);
vector<Scope::JuliaType> returns;
- for (auto const& _return: _funDef.returns)
+ for (auto const& _return: _funDef.returnVariables)
returns.push_back(_return.type);
if (!m_currentScope->registerFunction(_funDef.name, arguments, returns))
{
@@ -91,7 +96,7 @@ bool ScopeFiller::operator()(assembly::FunctionDefinition const& _funDef)
varScope.superScope = m_currentScope;
m_currentScope = &varScope;
varScope.functionScope = true;
- for (auto const& var: _funDef.arguments + _funDef.returns)
+ for (auto const& var: _funDef.parameters + _funDef.returnVariables)
if (!registerVariable(var, _funDef.location, varScope))
success = false;
diff --git a/libsolidity/inlineasm/AsmScopeFiller.h b/libsolidity/inlineasm/AsmScopeFiller.h
index ed28abbf..bb023f61 100644
--- a/libsolidity/inlineasm/AsmScopeFiller.h
+++ b/libsolidity/inlineasm/AsmScopeFiller.h
@@ -53,6 +53,7 @@ public:
bool operator()(assembly::Literal const&) { return true; }
bool operator()(assembly::Identifier const&) { return true; }
bool operator()(assembly::FunctionalInstruction const&) { return true; }
+ bool operator()(assembly::ExpressionStatement const& _expr);
bool operator()(assembly::Label const& _label);
bool operator()(assembly::StackAssignment const&) { return true; }
bool operator()(assembly::Assignment const&) { return true; }
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index 504ad92c..1b4bd270 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -38,6 +38,25 @@ using namespace std;
using namespace dev;
using namespace dev::solidity;
+namespace
+{
+assembly::AsmFlavour languageToAsmFlavour(AssemblyStack::Language _language)
+{
+ switch (_language)
+ {
+ case AssemblyStack::Language::Assembly:
+ return assembly::AsmFlavour::Loose;
+ case AssemblyStack::Language::StrictAssembly:
+ return assembly::AsmFlavour::Strict;
+ case AssemblyStack::Language::JULIA:
+ return assembly::AsmFlavour::IULIA;
+ }
+ solAssert(false, "");
+ return assembly::AsmFlavour::IULIA;
+}
+
+}
+
Scanner const& AssemblyStack::scanner() const
{
@@ -50,7 +69,7 @@ bool AssemblyStack::parseAndAnalyze(std::string const& _sourceName, std::string
m_errors.clear();
m_analysisSuccessful = false;
m_scanner = make_shared<Scanner>(CharStream(_source), _sourceName);
- m_parserResult = assembly::Parser(m_errorReporter, m_language == Language::JULIA).parse(m_scanner);
+ m_parserResult = assembly::Parser(m_errorReporter, languageToAsmFlavour(m_language)).parse(m_scanner);
if (!m_errorReporter.errors().empty())
return false;
solAssert(m_parserResult, "");
@@ -72,7 +91,7 @@ bool AssemblyStack::analyze(assembly::Block const& _block, Scanner const* _scann
bool AssemblyStack::analyzeParsed()
{
m_analysisInfo = make_shared<assembly::AsmAnalysisInfo>();
- assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, m_language == Language::JULIA);
+ assembly::AsmAnalyzer analyzer(*m_analysisInfo, m_errorReporter, languageToAsmFlavour(m_language));
m_analysisSuccessful = analyzer.analyze(*m_parserResult);
return m_analysisSuccessful;
}
diff --git a/libsolidity/interface/AssemblyStack.h b/libsolidity/interface/AssemblyStack.h
index 2ae596ed..6ae7e8d1 100644
--- a/libsolidity/interface/AssemblyStack.h
+++ b/libsolidity/interface/AssemblyStack.h
@@ -51,7 +51,7 @@ struct MachineAssemblyObject
class AssemblyStack
{
public:
- enum class Language { JULIA, Assembly };
+ enum class Language { JULIA, Assembly, StrictAssembly };
enum class Machine { EVM, EVM15, eWasm };
explicit AssemblyStack(Language _language = Language::Assembly):
diff --git a/libsolidity/interface/CompilerStack.cpp b/libsolidity/interface/CompilerStack.cpp
index 5713256a..3b5e65e8 100644
--- a/libsolidity/interface/CompilerStack.cpp
+++ b/libsolidity/interface/CompilerStack.cpp
@@ -734,9 +734,12 @@ void CompilerStack::compileContract(
try
{
- Compiler cloneCompiler(m_optimize, m_optimizeRuns);
- cloneCompiler.compileClone(_contract, _compiledContracts);
- compiledContract.cloneObject = cloneCompiler.assembledObject();
+ if (!_contract.isLibrary())
+ {
+ Compiler cloneCompiler(m_optimize, m_optimizeRuns);
+ cloneCompiler.compileClone(_contract, _compiledContracts);
+ compiledContract.cloneObject = cloneCompiler.assembledObject();
+ }
}
catch (eth::AssemblyException const&)
{
diff --git a/libsolidity/interface/Exceptions.h b/libsolidity/interface/Exceptions.h
index 09301b10..7c66d572 100644
--- a/libsolidity/interface/Exceptions.h
+++ b/libsolidity/interface/Exceptions.h
@@ -109,6 +109,18 @@ public:
infos.push_back(std::make_pair(_errMsg, _sourceLocation));
return *this;
}
+ /// Limits the number of secondary source locations to 32 and appends a notice to the
+ /// error message.
+ void limitSize(std::string& _message)
+ {
+ size_t occurrences = infos.size();
+ if (occurrences > 32)
+ {
+ infos.resize(32);
+ _message += " Truncated from " + boost::lexical_cast<std::string>(occurrences) + " to the first 32 occurrences.";
+ }
+ }
+
std::vector<errorSourceLocationInfo> infos;
};
diff --git a/libsolidity/interface/StandardCompiler.cpp b/libsolidity/interface/StandardCompiler.cpp
index ad01821e..04f5bd25 100644
--- a/libsolidity/interface/StandardCompiler.cpp
+++ b/libsolidity/interface/StandardCompiler.cpp
@@ -81,15 +81,15 @@ Json::Value formatErrorWithException(
else
message = _message;
+ Json::Value sourceLocation;
if (location && location->sourceName)
{
- Json::Value sourceLocation = Json::objectValue;
sourceLocation["file"] = *location->sourceName;
sourceLocation["start"] = location->start;
sourceLocation["end"] = location->end;
}
- return formatError(_warning, _type, _component, message, formattedMessage, location);
+ return formatError(_warning, _type, _component, message, formattedMessage, sourceLocation);
}
set<string> requestedContractNames(Json::Value const& _outputSelection)
@@ -193,7 +193,7 @@ Json::Value formatLinkReferences(std::map<size_t, std::string> const& linkRefere
for (auto const& ref: linkReferences)
{
string const& fullname = ref.second;
- size_t colon = fullname.find(':');
+ size_t colon = fullname.rfind(':');
solAssert(colon != string::npos, "");
string file = fullname.substr(0, colon);
string name = fullname.substr(colon + 1);
@@ -461,7 +461,7 @@ Json::Value StandardCompiler::compileInternal(Json::Value const& _input)
Json::Value contractsOutput = Json::objectValue;
for (string const& contractName: compilationSuccess ? m_compilerStack.contractNames() : vector<string>())
{
- size_t colon = contractName.find(':');
+ size_t colon = contractName.rfind(':');
solAssert(colon != string::npos, "");
string file = contractName.substr(0, colon);
string name = contractName.substr(colon + 1);
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index 821e81d2..05b877b5 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -644,15 +644,11 @@ ASTPointer<EventDefinition> Parser::parseEventDefinition()
expectToken(Token::Event);
ASTPointer<ASTString> name(expectIdentifierToken());
- ASTPointer<ParameterList> parameters;
- if (m_scanner->currentToken() == Token::LParen)
- {
- VarDeclParserOptions options;
- options.allowIndexed = true;
- parameters = parseParameterList(options);
- }
- else
- parameters = createEmptyParameterList();
+
+ VarDeclParserOptions options;
+ options.allowIndexed = true;
+ ASTPointer<ParameterList> parameters = parseParameterList(options);
+
bool anonymous = false;
if (m_scanner->currentToken() == Token::Anonymous)
{