aboutsummaryrefslogtreecommitdiffstats
path: root/libsolidity
diff options
context:
space:
mode:
Diffstat (limited to 'libsolidity')
-rw-r--r--libsolidity/analysis/DeclarationContainer.cpp5
-rw-r--r--libsolidity/analysis/DeclarationContainer.h3
-rw-r--r--libsolidity/analysis/NameAndTypeResolver.cpp12
-rw-r--r--libsolidity/analysis/ReferencesResolver.cpp17
-rw-r--r--libsolidity/analysis/StaticAnalyzer.cpp10
-rw-r--r--libsolidity/analysis/SyntaxChecker.cpp82
-rw-r--r--libsolidity/analysis/SyntaxChecker.h4
-rw-r--r--libsolidity/analysis/TypeChecker.cpp357
-rw-r--r--libsolidity/analysis/TypeChecker.h2
-rw-r--r--libsolidity/analysis/ViewPureChecker.cpp3
-rw-r--r--libsolidity/ast/AST.cpp7
-rw-r--r--libsolidity/ast/AST.h25
-rw-r--r--libsolidity/ast/ASTAnnotations.h7
-rw-r--r--libsolidity/ast/ASTJsonConverter.cpp4
-rw-r--r--libsolidity/ast/Types.cpp79
-rw-r--r--libsolidity/ast/Types.h4
-rw-r--r--libsolidity/codegen/ABIFunctions.cpp3
-rw-r--r--libsolidity/codegen/ABIFunctions.h2
-rw-r--r--libsolidity/codegen/CompilerContext.cpp25
-rw-r--r--libsolidity/codegen/CompilerContext.h6
-rw-r--r--libsolidity/codegen/CompilerUtils.cpp13
-rw-r--r--libsolidity/codegen/CompilerUtils.h4
-rw-r--r--libsolidity/codegen/ContractCompiler.cpp118
-rw-r--r--libsolidity/codegen/ContractCompiler.h24
-rw-r--r--libsolidity/codegen/ExpressionCompiler.cpp109
-rw-r--r--libsolidity/formal/SMTChecker.cpp2
-rw-r--r--libsolidity/inlineasm/AsmParser.cpp2
-rw-r--r--libsolidity/interface/AssemblyStack.cpp2
-rw-r--r--libsolidity/interface/CompilerStack.h2
-rw-r--r--libsolidity/interface/ErrorReporter.h6
-rw-r--r--libsolidity/interface/Natspec.cpp7
-rw-r--r--libsolidity/interface/Natspec.h2
-rw-r--r--libsolidity/interface/StandardCompiler.h2
-rw-r--r--libsolidity/parsing/Parser.cpp30
-rw-r--r--libsolidity/parsing/Parser.h8
-rw-r--r--libsolidity/parsing/Scanner.h2
-rw-r--r--libsolidity/parsing/Token.h1
37 files changed, 458 insertions, 533 deletions
diff --git a/libsolidity/analysis/DeclarationContainer.cpp b/libsolidity/analysis/DeclarationContainer.cpp
index 786272e4..9e2bf6d3 100644
--- a/libsolidity/analysis/DeclarationContainer.cpp
+++ b/libsolidity/analysis/DeclarationContainer.cpp
@@ -96,6 +96,11 @@ void DeclarationContainer::activateVariable(ASTString const& _name)
m_invisibleDeclarations.erase(_name);
}
+bool DeclarationContainer::isInvisible(ASTString const& _name) const
+{
+ return m_invisibleDeclarations.count(_name);
+}
+
bool DeclarationContainer::registerDeclaration(
Declaration const& _declaration,
ASTString const* _name,
diff --git a/libsolidity/analysis/DeclarationContainer.h b/libsolidity/analysis/DeclarationContainer.h
index a3e0bd0a..9d7a17a3 100644
--- a/libsolidity/analysis/DeclarationContainer.h
+++ b/libsolidity/analysis/DeclarationContainer.h
@@ -62,6 +62,9 @@ public:
/// VariableDeclarationStatements.
void activateVariable(ASTString const& _name);
+ /// @returns true if declaration is currently invisible.
+ bool isInvisible(ASTString const& _name) const;
+
/// @returns existing declaration names similar to @a _name.
/// Searches this and all parent containers.
std::vector<ASTString> similarNames(ASTString const& _name) const;
diff --git a/libsolidity/analysis/NameAndTypeResolver.cpp b/libsolidity/analysis/NameAndTypeResolver.cpp
index b856544a..823378c7 100644
--- a/libsolidity/analysis/NameAndTypeResolver.cpp
+++ b/libsolidity/analysis/NameAndTypeResolver.cpp
@@ -156,7 +156,13 @@ bool NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
void NameAndTypeResolver::activateVariable(string const& _name)
{
solAssert(m_currentScope, "");
- m_currentScope->activateVariable(_name);
+ // Scoped local variables are invisible before activation.
+ // When a local variable is activated, its name is removed
+ // from a scope's invisible variables.
+ // This is used to avoid activation of variables of same name
+ // in the same scope (an error is returned).
+ if (m_currentScope->isInvisible(_name))
+ m_currentScope->activateVariable(_name);
}
vector<Declaration const*> NameAndTypeResolver::resolveName(ASTString const& _name, ASTNode const* _scope) const
@@ -704,10 +710,6 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
dynamic_cast<EventDefinition const*>(m_currentScope)
)
warnAboutShadowing = false;
- // Do not warn about the constructor shadowing the contract.
- if (auto fun = dynamic_cast<FunctionDefinition const*>(&_declaration))
- if (fun->isConstructor())
- warnAboutShadowing = false;
// Register declaration as inactive if we are in block scope.
bool inactive =
diff --git a/libsolidity/analysis/ReferencesResolver.cpp b/libsolidity/analysis/ReferencesResolver.cpp
index 58b659f7..dfcbf888 100644
--- a/libsolidity/analysis/ReferencesResolver.cpp
+++ b/libsolidity/analysis/ReferencesResolver.cpp
@@ -377,19 +377,10 @@ void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
{
typeLoc = DataLocation::Storage;
if (_variable.isLocalVariable())
- {
- if (_variable.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
- typeError(
- _variable.location(),
- "Data location must be specified as either \"memory\" or \"storage\"."
- );
- else
- m_errorReporter.warning(
- _variable.location(),
- "Variable is declared as a storage pointer. "
- "Use an explicit \"storage\" keyword to silence this warning."
- );
- }
+ typeError(
+ _variable.location(),
+ "Data location must be specified as either \"memory\" or \"storage\"."
+ );
}
}
else
diff --git a/libsolidity/analysis/StaticAnalyzer.cpp b/libsolidity/analysis/StaticAnalyzer.cpp
index 323282ca..60a58665 100644
--- a/libsolidity/analysis/StaticAnalyzer.cpp
+++ b/libsolidity/analysis/StaticAnalyzer.cpp
@@ -51,16 +51,6 @@ void StaticAnalyzer::endVisit(ContractDefinition const&)
bool StaticAnalyzer::visit(FunctionDefinition const& _function)
{
- const bool isInterface = m_currentContract->contractKind() == ContractDefinition::ContractKind::Interface;
-
- if (_function.noVisibilitySpecified())
- m_errorReporter.warning(
- _function.location(),
- "No visibility specified. Defaulting to \"" +
- Declaration::visibilityToString(_function.visibility()) +
- "\"." +
- (isInterface ? " In interfaces it defaults to external." : "")
- );
if (_function.isImplemented())
m_currentFunction = &_function;
else
diff --git a/libsolidity/analysis/SyntaxChecker.cpp b/libsolidity/analysis/SyntaxChecker.cpp
index cd0dc2a4..5d16a33f 100644
--- a/libsolidity/analysis/SyntaxChecker.cpp
+++ b/libsolidity/analysis/SyntaxChecker.cpp
@@ -22,6 +22,7 @@
#include <libsolidity/analysis/SemVerHandler.h>
#include <libsolidity/interface/ErrorReporter.h>
#include <libsolidity/interface/Version.h>
+#include <boost/algorithm/cxx11/all_of.hpp>
using namespace std;
using namespace dev;
@@ -174,33 +175,19 @@ bool SyntaxChecker::visit(Break const& _breakStatement)
bool SyntaxChecker::visit(Throw const& _throwStatement)
{
- bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
- if (v050)
- m_errorReporter.syntaxError(
- _throwStatement.location(),
- "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
- );
- else
- m_errorReporter.warning(
- _throwStatement.location(),
- "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
- );
+ m_errorReporter.syntaxError(
+ _throwStatement.location(),
+ "\"throw\" is deprecated in favour of \"revert()\", \"require()\" and \"assert()\"."
+ );
return true;
}
bool SyntaxChecker::visit(UnaryOperation const& _operation)
{
- bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
if (_operation.getOperator() == Token::Add)
- {
- if (v050)
- m_errorReporter.syntaxError(_operation.location(), "Use of unary + is deprecated.");
- else
- m_errorReporter.warning(_operation.location(), "Use of unary + is deprecated.");
- }
+ m_errorReporter.syntaxError(_operation.location(), "Use of unary + is disallowed.");
+
return true;
}
@@ -210,28 +197,33 @@ bool SyntaxChecker::visit(PlaceholderStatement const&)
return true;
}
-bool SyntaxChecker::visit(FunctionDefinition const& _function)
+bool SyntaxChecker::visit(ContractDefinition const& _contract)
{
- bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+ m_isInterface = _contract.contractKind() == ContractDefinition::ContractKind::Interface;
- if (v050 && _function.noVisibilitySpecified())
- m_errorReporter.syntaxError(_function.location(), "No visibility specified.");
-
- if (_function.isOldStyleConstructor())
- {
- if (v050)
- m_errorReporter.syntaxError(
- _function.location(),
+ ASTString const& contractName = _contract.name();
+ for (FunctionDefinition const* function: _contract.definedFunctions())
+ if (function->name() == contractName)
+ m_errorReporter.syntaxError(function->location(),
"Functions are not allowed to have the same name as the contract. "
"If you intend this to be a constructor, use \"constructor(...) { ... }\" to define it."
);
- else
- m_errorReporter.warning(
- _function.location(),
- "Defining constructors as functions with the same name as the contract is deprecated. "
- "Use \"constructor(...) { ... }\" instead."
- );
+ return true;
+}
+
+bool SyntaxChecker::visit(FunctionDefinition const& _function)
+{
+ bool const v050 = m_sourceUnit->annotation().experimentalFeatures.count(ExperimentalFeature::V050);
+
+ if (_function.noVisibilitySpecified())
+ {
+ string suggestedVisibility = _function.isFallback() || m_isInterface ? "external" : "public";
+ m_errorReporter.syntaxError(
+ _function.location(),
+ "No visibility specified. Did you intend to add \"" + suggestedVisibility + "\"?"
+ );
}
+
if (!_function.isImplemented() && !_function.modifiers().empty())
{
if (v050)
@@ -239,11 +231,6 @@ bool SyntaxChecker::visit(FunctionDefinition const& _function)
else
m_errorReporter.warning(_function.location(), "Modifiers of functions without implementation are ignored." );
}
- if (_function.name() == "constructor")
- m_errorReporter.warning(_function.location(),
- "This function is named \"constructor\" but is not the constructor of the contract. "
- "If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it."
- );
return true;
}
@@ -260,12 +247,15 @@ bool SyntaxChecker::visit(FunctionTypeName const& _node)
return true;
}
-bool SyntaxChecker::visit(VariableDeclaration const& _declaration)
+bool SyntaxChecker::visit(VariableDeclarationStatement const& _statement)
{
- if (!_declaration.typeName())
- {
- m_errorReporter.syntaxError(_declaration.location(), "Use of the \"var\" keyword is disallowed.");
- }
+ // Report if none of the variable components in the tuple have a name (only possible via deprecated "var")
+ if (boost::algorithm::all_of_equal(_statement.declarations(), nullptr))
+ m_errorReporter.syntaxError(
+ _statement.location(),
+ "The use of the \"var\" keyword is disallowed. The declaration part of the statement can be removed, since it is empty."
+ );
+
return true;
}
diff --git a/libsolidity/analysis/SyntaxChecker.h b/libsolidity/analysis/SyntaxChecker.h
index 1579df57..28a0f66e 100644
--- a/libsolidity/analysis/SyntaxChecker.h
+++ b/libsolidity/analysis/SyntaxChecker.h
@@ -66,10 +66,11 @@ private:
virtual bool visit(PlaceholderStatement const& _placeholderStatement) override;
+ virtual bool visit(ContractDefinition const& _contract) override;
virtual bool visit(FunctionDefinition const& _function) override;
virtual bool visit(FunctionTypeName const& _node) override;
- virtual bool visit(VariableDeclaration const& _declaration) override;
+ virtual bool visit(VariableDeclarationStatement const& _statement) override;
virtual bool visit(StructDefinition const& _struct) override;
@@ -82,6 +83,7 @@ private:
bool m_versionPragmaFound = false;
int m_inLoopDepth = 0;
+ bool m_isInterface = false;
SourceUnit const* m_sourceUnit = nullptr;
};
diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp
index 676b3cd6..8536e934 100644
--- a/libsolidity/analysis/TypeChecker.cpp
+++ b/libsolidity/analysis/TypeChecker.cpp
@@ -23,12 +23,14 @@
#include <libsolidity/analysis/TypeChecker.h>
#include <memory>
#include <boost/algorithm/string/predicate.hpp>
+#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/reversed.hpp>
#include <libsolidity/ast/AST.h>
#include <libsolidity/inlineasm/AsmAnalysis.h>
#include <libsolidity/inlineasm/AsmAnalysisInfo.h>
#include <libsolidity/inlineasm/AsmData.h>
#include <libsolidity/interface/ErrorReporter.h>
+#include <libdevcore/Algorithms.h>
using namespace std;
using namespace dev;
@@ -123,10 +125,7 @@ bool TypeChecker::visit(ContractDefinition const& _contract)
m_errorReporter.typeError(function->parameterList().location(), "Fallback function cannot take parameters.");
if (!function->returnParameters().empty())
m_errorReporter.typeError(function->returnParameterList()->location(), "Fallback function cannot return values.");
- if (
- _contract.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050) &&
- function->visibility() != FunctionDefinition::Visibility::External
- )
+ if (function->visibility() != FunctionDefinition::Visibility::External)
m_errorReporter.typeError(function->location(), "Fallback function must be defined as \"external\".");
}
@@ -451,7 +450,7 @@ void TypeChecker::overrideError(FunctionDefinition const& function, FunctionDefi
{
m_errorReporter.typeError(
function.location(),
- SecondarySourceLocation().append("Overriden function is here:", super.location()),
+ SecondarySourceLocation().append("Overridden function is here:", super.location()),
message
);
}
@@ -551,33 +550,18 @@ void TypeChecker::endVisit(InheritanceSpecifier const& _inheritance)
if (arguments)
{
- bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
if (parameterTypes.size() != arguments->size())
{
- if (arguments->size() == 0 && !v050)
- m_errorReporter.warning(
- _inheritance.location(),
- "Wrong argument count for constructor call: " +
- toString(arguments->size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- "."
- );
- else
- {
- m_errorReporter.typeError(
- _inheritance.location(),
- "Wrong argument count for constructor call: " +
- toString(arguments->size()) +
- " arguments given but expected " +
- toString(parameterTypes.size()) +
- "."
- );
- return;
- }
+ m_errorReporter.typeError(
+ _inheritance.location(),
+ "Wrong argument count for constructor call: " +
+ toString(arguments->size()) +
+ " arguments given but expected " +
+ toString(parameterTypes.size()) +
+ ". Remove parentheses if you do not want to provide arguments here."
+ );
}
- for (size_t i = 0; i < arguments->size(); ++i)
+ for (size_t i = 0; i < std::min(arguments->size(), parameterTypes.size()); ++i)
if (!type(*(*arguments)[i])->isImplicitlyConvertibleTo(*parameterTypes[i]))
m_errorReporter.typeError(
(*arguments)[i]->location(),
@@ -610,22 +594,24 @@ bool TypeChecker::visit(StructDefinition const& _struct)
m_errorReporter.typeError(member->location(), "Type cannot be used in struct.");
// Check recursion, fatal error if detected.
- using StructPointer = StructDefinition const*;
- using StructPointersSet = set<StructPointer>;
- function<void(StructPointer,StructPointersSet const&)> check = [&](StructPointer _struct, StructPointersSet const& _parents)
- {
- if (_parents.count(_struct))
- m_errorReporter.fatalTypeError(_struct->location(), "Recursive struct definition.");
- StructPointersSet parents = _parents;
- parents.insert(_struct);
- for (ASTPointer<VariableDeclaration> const& member: _struct->members())
- if (type(*member)->category() == Type::Category::Struct)
+ auto visitor = [&](StructDefinition const& _struct, CycleDetector<StructDefinition>& _cycleDetector)
+ {
+ for (ASTPointer<VariableDeclaration> const& member: _struct.members())
+ {
+ Type const* memberType = type(*member).get();
+ while (auto arrayType = dynamic_cast<ArrayType const*>(memberType))
{
- auto const& typeName = dynamic_cast<UserDefinedTypeName const&>(*member->typeName());
- check(&dynamic_cast<StructDefinition const&>(*typeName.annotation().referencedDeclaration), parents);
+ if (arrayType->isDynamicallySized())
+ break;
+ memberType = arrayType->baseType().get();
}
+ if (auto structType = dynamic_cast<StructType const*>(memberType))
+ if (_cycleDetector.run(structType->structDefinition()))
+ return;
+ }
};
- check(&_struct, StructPointersSet{});
+ if (CycleDetector<StructDefinition>(visitor).run(_struct) != nullptr)
+ m_errorReporter.fatalTypeError(_struct.location(), "Recursive struct definition.");
ASTNode::listAccept(_struct.members(), *this);
@@ -685,18 +671,10 @@ bool TypeChecker::visit(FunctionDefinition const& _function)
{
if (_function.isImplemented())
m_errorReporter.typeError(_function.location(), "Functions in interfaces cannot have an implementation.");
- if (_function.sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
- {
- if (_function.visibility() != FunctionDefinition::Visibility::External)
- m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external.");
- }
- else
- {
- 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.visibility() != FunctionDefinition::Visibility::External)
+ m_errorReporter.typeError(_function.location(), "Functions in interfaces must be declared external.");
+
if (_function.isConstructor())
m_errorReporter.typeError(_function.location(), "Constructor cannot be defined in interfaces.");
}
@@ -1054,9 +1032,49 @@ void TypeChecker::endVisit(EmitStatement const& _emit)
m_insideEmitStatement = false;
}
+namespace
+{
+/**
+ * @returns a suggested left-hand-side of a multi-variable declaration contairing
+ * the variable declarations given in @a _decls.
+ */
+string createTupleDecl(vector<ASTPointer<VariableDeclaration>> const& _decls)
+{
+ vector<string> components;
+ for (ASTPointer<VariableDeclaration> const& decl: _decls)
+ if (decl)
+ components.emplace_back(decl->annotation().type->toString(false) + " " + decl->name());
+ else
+ components.emplace_back();
+
+ if (_decls.size() == 1)
+ return components.front();
+ else
+ return "(" + boost::algorithm::join(components, ", ") + ")";
+}
+
+bool typeCanBeExpressed(vector<ASTPointer<VariableDeclaration>> const& decls)
+{
+ for (ASTPointer<VariableDeclaration> const& decl: decls)
+ {
+ // skip empty tuples (they can be expressed of course)
+ if (!decl)
+ continue;
+
+ if (auto functionType = dynamic_cast<FunctionType const*>(decl->annotation().type.get()))
+ if (
+ functionType->kind() != FunctionType::Kind::Internal &&
+ functionType->kind() != FunctionType::Kind::External
+ )
+ return false;
+ }
+
+ return true;
+}
+}
+
bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
{
- bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
if (!_statement.initialValue())
{
// No initial value is only permitted for single variables with specified type.
@@ -1073,10 +1091,7 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
if (varDecl.referenceLocation() == VariableDeclaration::Location::Default)
errorText += " Did you mean '<type> memory " + varDecl.name() + "'?";
solAssert(m_scope, "");
- if (v050)
- m_errorReporter.declarationError(varDecl.location(), errorText);
- else
- m_errorReporter.warning(varDecl.location(), errorText);
+ m_errorReporter.declarationError(varDecl.location(), errorText);
}
}
else if (dynamic_cast<MappingType const*>(type(varDecl).get()))
@@ -1098,85 +1113,34 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
else
valueTypes = TypePointers{type(*_statement.initialValue())};
- // Determine which component is assigned to which variable.
- // If numbers do not match, fill up if variables begin or end empty (not both).
- vector<VariableDeclaration const*>& assignments = _statement.annotation().assignments;
- assignments.resize(valueTypes.size(), nullptr);
vector<ASTPointer<VariableDeclaration>> const& variables = _statement.declarations();
if (variables.empty())
- {
- if (!valueTypes.empty())
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Too many components (" +
- toString(valueTypes.size()) +
- ") in value for variable assignment (0) needed"
- );
- }
+ // We already have an error for this in the SyntaxChecker.
+ solAssert(m_errorReporter.hasErrors(), "");
else if (valueTypes.size() != variables.size())
- {
- if (v050)
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Different number of components on the left hand side (" +
- toString(variables.size()) +
- ") than on the right hand side (" +
- toString(valueTypes.size()) +
- ")."
- );
- else if (!variables.front() && !variables.back())
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Wildcard both at beginning and end of variable declaration list is only allowed "
- "if the number of components is equal."
- );
- else
- m_errorReporter.warning(
- _statement.location(),
- "Different number of components on the left hand side (" +
- toString(variables.size()) +
- ") than on the right hand side (" +
- toString(valueTypes.size()) +
- ")."
- );
- }
- size_t minNumValues = variables.size();
- if (!variables.empty() && (!variables.back() || !variables.front()))
- --minNumValues;
- if (valueTypes.size() < minNumValues)
- m_errorReporter.fatalTypeError(
- _statement.location(),
- "Not enough components (" +
- toString(valueTypes.size()) +
- ") in value to assign all variables (" +
- toString(minNumValues) + ")."
- );
- if (valueTypes.size() > variables.size() && variables.front() && variables.back())
- m_errorReporter.fatalTypeError(
+ m_errorReporter.typeError(
_statement.location(),
- "Too many components (" +
+ "Different number of components on the left hand side (" +
+ toString(variables.size()) +
+ ") than on the right hand side (" +
toString(valueTypes.size()) +
- ") in value for variable assignment (" +
- toString(minNumValues) +
- " needed)."
+ ")."
);
- bool fillRight = !variables.empty() && (!variables.back() || variables.front());
- for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
- if (fillRight)
- assignments[i] = variables[i].get();
- else
- assignments[assignments.size() - i - 1] = variables[variables.size() - i - 1].get();
- for (size_t i = 0; i < assignments.size(); ++i)
+ bool autoTypeDeductionNeeded = false;
+
+ for (size_t i = 0; i < min(variables.size(), valueTypes.size()); ++i)
{
- if (!assignments[i])
+ if (!variables[i])
continue;
- VariableDeclaration const& var = *assignments[i];
+ VariableDeclaration const& var = *variables[i];
solAssert(!var.value(), "Value has to be tied to statement.");
TypePointer const& valueComponentType = valueTypes[i];
solAssert(!!valueComponentType, "");
if (!var.annotation().type)
{
+ autoTypeDeductionNeeded = true;
+
// Infer type from value.
solAssert(!var.typeName(), "");
var.annotation().type = valueComponentType->mobileType();
@@ -1220,14 +1184,6 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
}
else
solAssert(dynamic_cast<FixedPointType const*>(var.annotation().type.get()), "Unknown type.");
-
- m_errorReporter.warning(
- _statement.location(),
- "The type of this variable was inferred as " +
- typeName +
- extension +
- ". This is probably not desired. Use an explicit type to silence this warning."
- );
}
var.accept(*this);
@@ -1264,6 +1220,23 @@ bool TypeChecker::visit(VariableDeclarationStatement const& _statement)
}
}
}
+
+ if (autoTypeDeductionNeeded)
+ {
+ if (!typeCanBeExpressed(variables))
+ m_errorReporter.syntaxError(
+ _statement.location(),
+ "Use of the \"var\" keyword is disallowed. "
+ "Type cannot be expressed in syntax."
+ );
+ else
+ m_errorReporter.syntaxError(
+ _statement.location(),
+ "Use of the \"var\" keyword is disallowed. "
+ "Use explicit declaration `" + createTupleDecl(variables) + " = ...ยด instead."
+ );
+ }
+
return false;
}
@@ -1337,7 +1310,6 @@ bool TypeChecker::visit(Conditional const& _conditional)
bool TypeChecker::visit(Assignment const& _assignment)
{
- bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
requireLValue(_assignment.leftHandSide());
TypePointer t = type(_assignment.leftHandSide());
_assignment.annotation().type = t;
@@ -1354,25 +1326,8 @@ bool TypeChecker::visit(Assignment const& _assignment)
expectType(_assignment.rightHandSide(), *tupleType);
// expectType does not cause fatal errors, so we have to check again here.
- if (TupleType const* rhsType = dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
- {
+ if (dynamic_cast<TupleType const*>(type(_assignment.rightHandSide()).get()))
checkDoubleStorageAssignment(_assignment);
- // @todo For 0.5.0, this code shoud move to TupleType::isImplicitlyConvertibleTo,
- // but we cannot do it right now.
- if (rhsType->components().size() != tupleType->components().size())
- {
- string message =
- "Different number of components on the left hand side (" +
- toString(tupleType->components().size()) +
- ") than on the right hand side (" +
- toString(rhsType->components().size()) +
- ").";
- if (v050)
- m_errorReporter.typeError(_assignment.location(), message);
- else
- m_errorReporter.warning(_assignment.location(), message);
- }
- }
}
else if (t->category() == Type::Category::Mapping)
{
@@ -1429,14 +1384,12 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
}
else
{
- bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
bool isPure = true;
TypePointer inlineArrayType;
for (size_t i = 0; i < components.size(); ++i)
{
- // Outside of an lvalue-context, the only situation where a component can be empty is (x,).
- if (!components[i] && !(i == 1 && components.size() == 2))
+ if (!components[i])
m_errorReporter.fatalTypeError(_tuple.location(), "Tuple component cannot be empty.");
else if (components[i])
{
@@ -1448,10 +1401,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
{
if (_tuple.isInlineArray())
m_errorReporter.fatalTypeError(components[i]->location(), "Array component cannot be empty.");
- if (v050)
- m_errorReporter.fatalTypeError(components[i]->location(), "Tuple component cannot be empty.");
- else
- m_errorReporter.warning(components[i]->location(), "Tuple component cannot be empty.");
+ m_errorReporter.typeError(components[i]->location(), "Tuple component cannot be empty.");
}
// Note: code generation will visit each of the expression even if they are not assigned from.
@@ -1489,11 +1439,7 @@ bool TypeChecker::visit(TupleExpression const& _tuple)
if (components.size() == 1)
_tuple.annotation().type = type(*components[0]);
else
- {
- if (components.size() == 2 && !components[1])
- types.pop_back();
_tuple.annotation().type = make_shared<TupleType>(types);
- }
}
}
@@ -1684,8 +1630,6 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
else
_functionCall.annotation().type = make_shared<TupleType>(returnTypes);
- bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
if (auto functionName = dynamic_cast<Identifier const*>(&_functionCall.expression()))
{
if (functionName->name() == "sha3" && functionType->kind() == FunctionType::Kind::SHA3)
@@ -1705,23 +1649,15 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
auto const& argType = type(*arguments[i]);
if (auto literal = dynamic_cast<RationalNumberType const*>(argType.get()))
{
- /* If no mobile type is available an error will be raised elsewhere. */
if (literal->mobileType())
+ m_errorReporter.typeError(
+ arguments[i]->location(),
+ "Cannot perform packed encoding for a literal. Please convert it to an explicit type first."
+ );
+ else
{
- if (v050)
- m_errorReporter.typeError(
- arguments[i]->location(),
- "Cannot perform packed encoding for a literal. Please convert it to an explicit type first."
- );
- else
- m_errorReporter.warning(
- arguments[i]->location(),
- "The type of \"" +
- argType->toString() +
- "\" was inferred as " +
- literal->mobileType()->toString() +
- ". This is probably not desired. Use an explicit type to silence this warning."
- );
+ /* If no mobile type is available an error will be raised elsewhere. */
+ solAssert(m_errorReporter.hasErrors(), "");
}
}
}
@@ -1848,7 +1784,7 @@ bool TypeChecker::visit(FunctionCall const& _functionCall)
if (functionType->takesArbitraryParameters())
m_errorReporter.typeError(
_functionCall.location(),
- "Named arguments cannnot be used for functions that take arbitrary parameters."
+ "Named arguments cannot be used for functions that take arbitrary parameters."
);
else if (parameterNames.size() > argumentNames.size())
m_errorReporter.typeError(_functionCall.location(), "Some argument names are missing.");
@@ -1994,6 +1930,9 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
else
++it;
}
+
+ auto& annotation = _memberAccess.annotation();
+
if (possibleMembers.size() == 0)
{
if (initialMemberCount == 0)
@@ -2011,11 +1950,21 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
" outside of storage."
);
}
+ string errorMsg = "Member \"" + memberName + "\" not found or not visible "
+ "after argument-dependent lookup in " + exprType->toString() +
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".");
+ if (exprType->category() == Type::Category::Contract)
+ for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr))
+ if (addressMember.name == memberName)
+ {
+ Identifier const* var = dynamic_cast<Identifier const*>(&_memberAccess.expression());
+ string varName = var ? var->name() : "...";
+ errorMsg += " Use \"address(" + varName + ")." + memberName + "\" to access this address member.";
+ break;
+ }
m_errorReporter.fatalTypeError(
_memberAccess.location(),
- "Member \"" + memberName + "\" not found or not visible "
- "after argument-dependent lookup in " + exprType->toString() +
- (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
+ errorMsg
);
}
else if (possibleMembers.size() > 1)
@@ -2023,10 +1972,9 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
_memberAccess.location(),
"Member \"" + memberName + "\" not unique "
"after argument-dependent lookup in " + exprType->toString() +
- (memberName == "value" ? " - did you forget the \"payable\" modifier?" : "")
+ (memberName == "value" ? " - did you forget the \"payable\" modifier?" : ".")
);
- auto& annotation = _memberAccess.annotation();
annotation.referencedDeclaration = possibleMembers.front().declaration;
annotation.type = possibleMembers.front().type;
@@ -2035,7 +1983,7 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
m_errorReporter.typeError(
_memberAccess.location(),
"Function \"" + memberName + "\" cannot be called on an object of type " +
- exprType->toString() + " (expected " + funType->selfType()->toString() + ")"
+ exprType->toString() + " (expected " + funType->selfType()->toString() + ")."
);
if (exprType->category() == Type::Category::Struct)
@@ -2059,20 +2007,6 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess)
if (exprType->category() == Type::Category::Contract)
{
- // Warn about using address members on contracts
- bool v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
- for (auto const& addressMember: IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr))
- if (addressMember.name == memberName && *annotation.type == *addressMember.type)
- {
- solAssert(!v050, "Address member still present on contract in v0.5.0.");
- m_errorReporter.warning(
- _memberAccess.location(),
- "Using contract member \"" + memberName +"\" inherited from the address type is deprecated." +
- " Convert the contract to \"address\" type to access the member,"
- " for example use \"address(contract)." + memberName + "\" instead."
- );
- }
-
// Warn about using send or transfer with a non-payable fallback function.
if (auto callType = dynamic_cast<FunctionType const*>(type(_memberAccess).get()))
{
@@ -2267,11 +2201,9 @@ void TypeChecker::endVisit(ElementaryTypeNameExpression const& _expr)
void TypeChecker::endVisit(Literal const& _literal)
{
- bool const v050 = m_scope->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050);
-
if (_literal.looksLikeAddress())
{
- // Assign type here if it even looks like an address. This prevents double error in 050 mode for invalid address
+ // Assign type here if it even looks like an address. This prevents double errors for invalid addresses
_literal.annotation().type = make_shared<IntegerType>(160, IntegerType::Modifier::Address);
string msg;
@@ -2298,20 +2230,11 @@ void TypeChecker::endVisit(Literal const& _literal)
}
if (_literal.isHexNumber() && _literal.subDenomination() != Literal::SubDenomination::None)
- {
- if (v050)
- m_errorReporter.fatalTypeError(
- _literal.location(),
- "Hexadecimal numbers cannot be used with unit denominations. "
- "You can use an expression of the form \"0x1234 * 1 day\" instead."
- );
- else
- m_errorReporter.warning(
- _literal.location(),
- "Hexadecimal numbers with unit denominations are deprecated. "
- "You can use an expression of the form \"0x1234 * 1 day\" instead."
- );
- }
+ m_errorReporter.fatalTypeError(
+ _literal.location(),
+ "Hexadecimal numbers cannot be used with unit denominations. "
+ "You can use an expression of the form \"0x1234 * 1 day\" instead."
+ );
if (_literal.subDenomination() == Literal::SubDenomination::Year)
m_errorReporter.typeError(
diff --git a/libsolidity/analysis/TypeChecker.h b/libsolidity/analysis/TypeChecker.h
index 2245abd6..8dc6b376 100644
--- a/libsolidity/analysis/TypeChecker.h
+++ b/libsolidity/analysis/TypeChecker.h
@@ -68,7 +68,7 @@ private:
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
void checkContractDuplicateEvents(ContractDefinition const& _contract);
void checkContractIllegalOverrides(ContractDefinition const& _contract);
- /// Reports a type error with an appropiate message if overriden function signature differs.
+ /// Reports a type error with an appropriate message if overridden function signature differs.
/// Also stores the direct super function in the AST annotations.
void checkFunctionOverride(FunctionDefinition const& function, FunctionDefinition const& super);
void overrideError(FunctionDefinition const& function, FunctionDefinition const& super, std::string message);
diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp
index 18c642c3..d936ada0 100644
--- a/libsolidity/analysis/ViewPureChecker.cpp
+++ b/libsolidity/analysis/ViewPureChecker.cpp
@@ -287,9 +287,8 @@ void ViewPureChecker::endVisit(MemberAccess const& _memberAccess)
ASTString const& member = _memberAccess.memberName();
switch (_memberAccess.expression().annotation().type->category())
{
- case Type::Category::Contract:
case Type::Category::Integer:
- if (member == "balance" && !_memberAccess.annotation().referencedDeclaration)
+ if (member == "balance")
mutability = StateMutability::View;
break;
case Type::Category::Magic:
diff --git a/libsolidity/ast/AST.cpp b/libsolidity/ast/AST.cpp
index 16c9b2d2..7719d080 100644
--- a/libsolidity/ast/AST.cpp
+++ b/libsolidity/ast/AST.cpp
@@ -535,13 +535,6 @@ ReturnAnnotation& Return::annotation() const
return dynamic_cast<ReturnAnnotation&>(*m_annotation);
}
-VariableDeclarationStatementAnnotation& VariableDeclarationStatement::annotation() const
-{
- if (!m_annotation)
- m_annotation = new VariableDeclarationStatementAnnotation();
- return dynamic_cast<VariableDeclarationStatementAnnotation&>(*m_annotation);
-}
-
ExpressionAnnotation& Expression::annotation() const
{
if (!m_annotation)
diff --git a/libsolidity/ast/AST.h b/libsolidity/ast/AST.h
index d703ae53..9ed3b9aa 100644
--- a/libsolidity/ast/AST.h
+++ b/libsolidity/ast/AST.h
@@ -614,7 +614,6 @@ public:
StateMutability stateMutability() const { return m_stateMutability; }
bool isConstructor() const { return m_isConstructor; }
- bool isOldStyleConstructor() const { return m_isConstructor && !name().empty(); }
bool isFallback() const { return !m_isConstructor && name().empty(); }
bool isPayable() const { return m_stateMutability == StateMutability::Payable; }
std::vector<ASTPointer<ModifierInvocation>> const& modifiers() const { return m_functionModifiers; }
@@ -1169,11 +1168,11 @@ public:
Statement const& body() const { return *m_body; }
private:
- /// For statement's initialization expresion. for(XXX; ; ). Can be empty
+ /// For statement's initialization expression. for(XXX; ; ). Can be empty
ASTPointer<Statement> m_initExpression;
- /// For statement's condition expresion. for(; XXX ; ). Can be empty
+ /// For statement's condition expression. for(; XXX ; ). Can be empty
ASTPointer<Expression> m_condExpression;
- /// For statement's loop expresion. for(;;XXX). Can be empty
+ /// For statement's loop expression. for(;;XXX). Can be empty
ASTPointer<ExpressionStatement> m_loopExpression;
/// The body of the loop
ASTPointer<Statement> m_body;
@@ -1250,13 +1249,12 @@ private:
};
/**
- * Definition of a variable as a statement inside a function. It requires a type name (which can
- * also be "var") but the actual assignment can be missing.
- * Examples: var a = 2; uint256 a;
- * As a second form, multiple variables can be declared, cannot have a type and must be assigned
- * right away. If the first or last component is unnamed, it can "consume" an arbitrary number
- * of components.
- * Examples: var (a, b) = f(); var (a,,,c) = g(); var (a,) = d();
+ * Definition of one or more variables as a statement inside a function.
+ * If multiple variables are declared, a value has to be assigned directly.
+ * If only a single variable is declared, the value can be missing.
+ * Examples:
+ * uint[] memory a; uint a = 2;
+ * (uint a, bytes32 b, ) = f(); (, uint a, , StructName storage x) = g();
*/
class VariableDeclarationStatement: public Statement
{
@@ -1271,13 +1269,14 @@ public:
virtual void accept(ASTVisitor& _visitor) override;
virtual void accept(ASTConstVisitor& _visitor) const override;
- VariableDeclarationStatementAnnotation& annotation() const override;
-
std::vector<ASTPointer<VariableDeclaration>> const& declarations() const { return m_variables; }
Expression const* initialValue() const { return m_initialValue.get(); }
private:
/// List of variables, some of which can be empty pointers (unnamed components).
+ /// Note that the ``m_value`` member of these is unused. Instead, ``m_initialValue``
+ /// below is used, because the initial value can be a single expression assigned
+ /// to all variables.
std::vector<ASTPointer<VariableDeclaration>> m_variables;
/// The assigned expression / initial value.
ASTPointer<Expression> m_initialValue;
diff --git a/libsolidity/ast/ASTAnnotations.h b/libsolidity/ast/ASTAnnotations.h
index 5cbe42bd..e0b3f492 100644
--- a/libsolidity/ast/ASTAnnotations.h
+++ b/libsolidity/ast/ASTAnnotations.h
@@ -164,13 +164,6 @@ struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
ContractDefinition const* contractScope = nullptr;
};
-struct VariableDeclarationStatementAnnotation: StatementAnnotation
-{
- /// Information about which component of the value is assigned to which variable.
- /// The pointer can be null to signify that the component is discarded.
- std::vector<VariableDeclaration const*> assignments;
-};
-
struct ExpressionAnnotation: ASTAnnotation
{
/// Inferred type of the expression.
diff --git a/libsolidity/ast/ASTJsonConverter.cpp b/libsolidity/ast/ASTJsonConverter.cpp
index b7855668..a26828a6 100644
--- a/libsolidity/ast/ASTJsonConverter.cpp
+++ b/libsolidity/ast/ASTJsonConverter.cpp
@@ -555,8 +555,8 @@ bool ASTJsonConverter::visit(EmitStatement const& _node)
bool ASTJsonConverter::visit(VariableDeclarationStatement const& _node)
{
Json::Value varDecs(Json::arrayValue);
- for (auto const& v: _node.annotation().assignments)
- appendMove(varDecs, idOrNull(v));
+ for (auto const& v: _node.declarations())
+ appendMove(varDecs, idOrNull(v.get()));
setJsonNode(_node, "VariableDeclarationStatement", {
make_pair("assignments", std::move(varDecs)),
make_pair("declarations", toJson(_node.declarations())),
diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp
index 1c4eb76e..2c2d3b68 100644
--- a/libsolidity/ast/Types.cpp
+++ b/libsolidity/ast/Types.cpp
@@ -771,20 +771,23 @@ tuple<bool, rational> RationalNumberType::isValidLiteral(Literal const& _literal
}
else if (expPoint != _literal.value().end())
{
- // Parse base and exponent. Checks numeric limit.
- bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
+ // Parse mantissa and exponent. Checks numeric limit.
+ tuple<bool, rational> mantissa = parseRational(string(_literal.value().begin(), expPoint));
- if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
+ if (!get<0>(mantissa))
return make_tuple(false, rational(0));
+ value = get<1>(mantissa);
- uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
-
+ // 0E... is always zero.
+ if (value == 0)
+ return make_tuple(true, rational(0));
- tuple<bool, rational> base = parseRational(string(_literal.value().begin(), expPoint));
+ bigint exp = bigint(string(expPoint + 1, _literal.value().end()));
- if (!get<0>(base))
+ if (exp > numeric_limits<int32_t>::max() || exp < numeric_limits<int32_t>::min())
return make_tuple(false, rational(0));
- value = get<1>(base);
+
+ uint32_t expAbs = bigint(abs(exp)).convert_to<uint32_t>();
if (exp < 0)
{
@@ -949,7 +952,7 @@ TypePointer RationalNumberType::binaryOperatorResult(Token::Value _operator, Typ
RationalNumberType const& other = dynamic_cast<RationalNumberType const&>(*_other);
if (Token::isCompareOp(_operator))
{
- // Since we do not have a "BoolConstantType", we have to do the acutal comparison
+ // Since we do not have a "BoolConstantType", we have to do the actual comparison
// at runtime and convert to mobile typse first. Such a comparison is not a very common
// use-case and will be optimized away.
TypePointer thisMobile = mobileType();
@@ -1870,47 +1873,9 @@ MemberList::MemberMap ContractType::nativeMembers(ContractDefinition const* _con
&it.second->declaration()
));
}
- // In 0.5.0 address members are not populated into the contract.
- if (!_contract->sourceUnit().annotation().experimentalFeatures.count(ExperimentalFeature::V050))
- addNonConflictingAddressMembers(members);
return members;
}
-void ContractType::addNonConflictingAddressMembers(MemberList::MemberMap& _members)
-{
- MemberList::MemberMap addressMembers = IntegerType(160, IntegerType::Modifier::Address).nativeMembers(nullptr);
- for (auto const& addressMember: addressMembers)
- {
- bool clash = false;
- for (auto const& member: _members)
- {
- if (
- member.name == addressMember.name &&
- (
- // Members with different types are not allowed
- member.type->category() != addressMember.type->category() ||
- // Members must overload functions without clash
- (
- member.type->category() == Type::Category::Function &&
- dynamic_cast<FunctionType const&>(*member.type).hasEqualArgumentTypes(dynamic_cast<FunctionType const&>(*addressMember.type))
- )
- )
- )
- {
- clash = true;
- break;
- }
- }
-
- if (!clash)
- _members.push_back(MemberList::Member(
- addressMember.name,
- addressMember.type,
- addressMember.declaration
- ));
- }
-}
-
shared_ptr<FunctionType const> const& ContractType::newExpressionType() const
{
if (!m_constructorType)
@@ -2238,25 +2203,13 @@ bool TupleType::isImplicitlyConvertibleTo(Type const& _other) const
TypePointers const& targets = tupleType->components();
if (targets.empty())
return components().empty();
- if (components().size() != targets.size() && !targets.front() && !targets.back())
- return false; // (,a,) = (1,2,3,4) - unable to position `a` in the tuple.
- size_t minNumValues = targets.size();
- if (!targets.back() || !targets.front())
- --minNumValues; // wildcards can also match 0 components
- if (components().size() < minNumValues)
+ if (components().size() != targets.size())
return false;
- if (components().size() > targets.size() && targets.front() && targets.back())
- return false; // larger source and no wildcard
- bool fillRight = !targets.back() || targets.front();
- for (size_t i = 0; i < min(targets.size(), components().size()); ++i)
- {
- auto const& s = components()[fillRight ? i : components().size() - i - 1];
- auto const& t = targets[fillRight ? i : targets.size() - i - 1];
- if (!s && t)
+ for (size_t i = 0; i < targets.size(); ++i)
+ if (!components()[i] && targets[i])
return false;
- else if (s && t && !s->isImplicitlyConvertibleTo(*t))
+ else if (components()[i] && targets[i] && !components()[i]->isImplicitlyConvertibleTo(*targets[i]))
return false;
- }
return true;
}
else
diff --git a/libsolidity/ast/Types.h b/libsolidity/ast/Types.h
index 4415fb4b..1a676b42 100644
--- a/libsolidity/ast/Types.h
+++ b/libsolidity/ast/Types.h
@@ -740,8 +740,6 @@ public:
std::vector<std::tuple<VariableDeclaration const*, u256, unsigned>> stateVariables() const;
private:
- static void addNonConflictingAddressMembers(MemberList::MemberMap& _members);
-
ContractDefinition const& m_contract;
/// If true, it is the "super" type of the current contract, i.e. it contains only inherited
/// members.
@@ -1031,7 +1029,7 @@ public:
/// @param _selfType if the function is bound, this has to be supplied and is the type of the
/// expression the function is called on.
bool canTakeArguments(TypePointers const& _arguments, TypePointer const& _selfType = TypePointer()) const;
- /// @returns true if the types of parameters are equal (does't check return parameter types)
+ /// @returns true if the types of parameters are equal (doesn't check return parameter types)
bool hasEqualArgumentTypes(FunctionType const& _other) const;
/// @returns true if the ABI is used for this call (only meaningful for external calls)
diff --git a/libsolidity/codegen/ABIFunctions.cpp b/libsolidity/codegen/ABIFunctions.cpp
index 4818e111..b3f1bc7e 100644
--- a/libsolidity/codegen/ABIFunctions.cpp
+++ b/libsolidity/codegen/ABIFunctions.cpp
@@ -1188,7 +1188,8 @@ string ABIFunctions::abiDecodingFunctionCalldataArray(ArrayType const& _type)
solAssert(_type.dataStoredIn(DataLocation::CallData), "");
if (!_type.isDynamicallySized())
solAssert(_type.length() < u256("0xffffffffffffffff"), "");
- solAssert(!_type.baseType()->isDynamicallyEncoded(), "");
+ if (_type.baseType()->isDynamicallyEncoded())
+ solUnimplemented("Calldata arrays with non-value base types are not yet supported by Solidity.");
solAssert(_type.baseType()->calldataEncodedSize() < u256("0xffffffffffffffff"), "");
string functionName =
diff --git a/libsolidity/codegen/ABIFunctions.h b/libsolidity/codegen/ABIFunctions.h
index 6bfb3f15..3caaa1d9 100644
--- a/libsolidity/codegen/ABIFunctions.h
+++ b/libsolidity/codegen/ABIFunctions.h
@@ -203,7 +203,7 @@ private:
std::string arrayLengthFunction(ArrayType const& _type);
/// @returns the name of a function that computes the number of bytes required
/// to store an array in memory given its length (internally encoded, not ABI encoded).
- /// The function reverts for too large lengthes.
+ /// The function reverts for too large lengths.
std::string arrayAllocationSizeFunction(ArrayType const& _type);
/// @returns the name of a function that converts a storage slot number
/// or a memory pointer to the slot number / memory pointer for the data position of an array
diff --git a/libsolidity/codegen/CompilerContext.cpp b/libsolidity/codegen/CompilerContext.cpp
index a35eea73..3b1b4ec0 100644
--- a/libsolidity/codegen/CompilerContext.cpp
+++ b/libsolidity/codegen/CompilerContext.cpp
@@ -127,10 +127,14 @@ void CompilerContext::addVariable(VariableDeclaration const& _declaration,
unsigned _offsetToCurrent)
{
solAssert(m_asm->deposit() >= 0 && unsigned(m_asm->deposit()) >= _offsetToCurrent, "");
+ unsigned sizeOnStack = _declaration.annotation().type->sizeOnStack();
+ // Variables should not have stack size other than [1, 2],
+ // but that might change when new types are introduced.
+ solAssert(sizeOnStack == 1 || sizeOnStack == 2, "");
m_localVariables[&_declaration].push_back(unsigned(m_asm->deposit()) - _offsetToCurrent);
}
-void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
+void CompilerContext::removeVariable(Declaration const& _declaration)
{
solAssert(m_localVariables.count(&_declaration) && !m_localVariables[&_declaration].empty(), "");
m_localVariables[&_declaration].pop_back();
@@ -138,6 +142,25 @@ void CompilerContext::removeVariable(VariableDeclaration const& _declaration)
m_localVariables.erase(&_declaration);
}
+void CompilerContext::removeVariablesAboveStackHeight(unsigned _stackHeight)
+{
+ vector<Declaration const*> toRemove;
+ for (auto _var: m_localVariables)
+ {
+ solAssert(!_var.second.empty(), "");
+ solAssert(_var.second.back() <= stackHeight(), "");
+ if (_var.second.back() >= _stackHeight)
+ toRemove.push_back(_var.first);
+ }
+ for (auto _var: toRemove)
+ removeVariable(*_var);
+}
+
+unsigned CompilerContext::numberOfLocalVariables() const
+{
+ return m_localVariables.size();
+}
+
eth::Assembly const& CompilerContext::compiledContract(const ContractDefinition& _contract) const
{
auto ret = m_compiledContracts.find(&_contract);
diff --git a/libsolidity/codegen/CompilerContext.h b/libsolidity/codegen/CompilerContext.h
index 5776b5d1..3f357821 100644
--- a/libsolidity/codegen/CompilerContext.h
+++ b/libsolidity/codegen/CompilerContext.h
@@ -71,7 +71,11 @@ public:
void addStateVariable(VariableDeclaration const& _declaration, u256 const& _storageOffset, unsigned _byteOffset);
void addVariable(VariableDeclaration const& _declaration, unsigned _offsetToCurrent = 0);
- void removeVariable(VariableDeclaration const& _declaration);
+ void removeVariable(Declaration const& _declaration);
+ /// Removes all local variables currently allocated above _stackHeight.
+ void removeVariablesAboveStackHeight(unsigned _stackHeight);
+ /// Returns the number of currently allocated local variables.
+ unsigned numberOfLocalVariables() const;
void setCompiledContracts(std::map<ContractDefinition const*, eth::Assembly const*> const& _contracts) { m_compiledContracts = _contracts; }
eth::Assembly const& compiledContract(ContractDefinition const& _contract) const;
diff --git a/libsolidity/codegen/CompilerUtils.cpp b/libsolidity/codegen/CompilerUtils.cpp
index 2f45765a..2d81a106 100644
--- a/libsolidity/codegen/CompilerUtils.cpp
+++ b/libsolidity/codegen/CompilerUtils.cpp
@@ -363,8 +363,8 @@ void CompilerUtils::encodeToMemory(
// Stack during operation:
// <v1> <v2> ... <vn> <mem_start> <dyn_head_1> ... <dyn_head_r> <end_of_mem>
- // The values dyn_head_i are added during the first loop and they point to the head part
- // of the ith dynamic parameter, which is filled once the dynamic parts are processed.
+ // The values dyn_head_n are added during the first loop and they point to the head part
+ // of the nth dynamic parameter, which is filled once the dynamic parts are processed.
// store memory start pointer
m_context << Instruction::DUP1;
@@ -1170,6 +1170,15 @@ void CompilerUtils::popStackSlots(size_t _amount)
m_context << Instruction::POP;
}
+void CompilerUtils::popAndJump(unsigned _toHeight, eth::AssemblyItem const& _jumpTo)
+{
+ solAssert(m_context.stackHeight() >= _toHeight, "");
+ unsigned amount = m_context.stackHeight() - _toHeight;
+ popStackSlots(amount);
+ m_context.appendJumpTo(_jumpTo);
+ m_context.adjustStackOffset(amount);
+}
+
unsigned CompilerUtils::sizeOnStack(vector<shared_ptr<Type const>> const& _variableTypes)
{
unsigned size = 0;
diff --git a/libsolidity/codegen/CompilerUtils.h b/libsolidity/codegen/CompilerUtils.h
index 0ff3ad7c..26df4765 100644
--- a/libsolidity/codegen/CompilerUtils.h
+++ b/libsolidity/codegen/CompilerUtils.h
@@ -241,6 +241,10 @@ public:
void popStackElement(Type const& _type);
/// Removes element from the top of the stack _amount times.
void popStackSlots(size_t _amount);
+ /// Pops slots from the stack such that its height is _toHeight.
+ /// Adds jump to _jumpTo.
+ /// Readjusts the stack offset to the original value.
+ void popAndJump(unsigned _toHeight, eth::AssemblyItem const& _jumpTo);
template <class T>
static unsigned sizeOnStack(std::vector<T> const& _variables);
diff --git a/libsolidity/codegen/ContractCompiler.cpp b/libsolidity/codegen/ContractCompiler.cpp
index 81aba21e..bbb3db3d 100644
--- a/libsolidity/codegen/ContractCompiler.cpp
+++ b/libsolidity/codegen/ContractCompiler.cpp
@@ -427,7 +427,7 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
m_context.startFunction(_function);
// stack upon entry: [return address] [arg0] [arg1] ... [argn]
- // reserve additional slots: [retarg0] ... [retargm] [localvar0] ... [localvarp]
+ // reserve additional slots: [retarg0] ... [retargm]
unsigned parametersSize = CompilerUtils::sizeOnStack(_function.parameters());
if (!_function.isConstructor())
@@ -441,8 +441,6 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
appendStackVariableInitialisation(*variable);
- for (VariableDeclaration const* localVariable: _function.localVariables())
- appendStackVariableInitialisation(*localVariable);
if (_function.isConstructor())
if (auto c = m_context.nextConstructor(dynamic_cast<ContractDefinition const&>(*_function.scope())))
@@ -451,12 +449,11 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
solAssert(m_returnTags.empty(), "");
m_breakTags.clear();
m_continueTags.clear();
- m_stackCleanupForReturn = 0;
m_currentFunction = &_function;
m_modifierDepth = -1;
+ m_scopeStackHeight.clear();
appendModifierOrFunctionCode();
-
solAssert(m_returnTags.empty(), "");
// Now we need to re-shuffle the stack. For this we keep a record of the stack layout
@@ -467,14 +464,12 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
unsigned const c_argumentsSize = CompilerUtils::sizeOnStack(_function.parameters());
unsigned const c_returnValuesSize = CompilerUtils::sizeOnStack(_function.returnParameters());
- unsigned const c_localVariablesSize = CompilerUtils::sizeOnStack(_function.localVariables());
vector<int> stackLayout;
stackLayout.push_back(c_returnValuesSize); // target of return address
stackLayout += vector<int>(c_argumentsSize, -1); // discard all arguments
for (unsigned i = 0; i < c_returnValuesSize; ++i)
stackLayout.push_back(i);
- stackLayout += vector<int>(c_localVariablesSize, -1);
if (stackLayout.size() > 17)
BOOST_THROW_EXCEPTION(
@@ -493,18 +488,23 @@ bool ContractCompiler::visit(FunctionDefinition const& _function)
m_context << swapInstruction(stackLayout.size() - stackLayout.back() - 1);
swap(stackLayout[stackLayout.back()], stackLayout.back());
}
- //@todo assert that everything is in place now
+ for (int i = 0; i < int(stackLayout.size()); ++i)
+ if (stackLayout[i] != i)
+ solAssert(false, "Invalid stack layout on cleanup.");
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters() + _function.returnParameters())
m_context.removeVariable(*variable);
- for (VariableDeclaration const* localVariable: _function.localVariables())
- m_context.removeVariable(*localVariable);
m_context.adjustStackOffset(-(int)c_returnValuesSize);
/// The constructor and the fallback function doesn't to jump out.
- if (!_function.isConstructor() && !_function.isFallback())
- m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
+ if (!_function.isConstructor())
+ {
+ solAssert(m_context.numberOfLocalVariables() == 0, "");
+ if (!_function.isFallback())
+ m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
+ }
+
return false;
}
@@ -669,14 +669,14 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
- m_breakTags.push_back(loopEnd);
+ m_breakTags.push_back({loopEnd, m_context.stackHeight()});
m_context << loopStart;
if (_whileStatement.isDoWhile())
{
eth::AssemblyItem condition = m_context.newTag();
- m_continueTags.push_back(condition);
+ m_continueTags.push_back({condition, m_context.stackHeight()});
_whileStatement.body().accept(*this);
@@ -687,7 +687,7 @@ bool ContractCompiler::visit(WhileStatement const& _whileStatement)
}
else
{
- m_continueTags.push_back(loopStart);
+ m_continueTags.push_back({loopStart, m_context.stackHeight()});
compileExpression(_whileStatement.condition());
m_context << Instruction::ISZERO;
m_context.appendConditionalJumpTo(loopEnd);
@@ -712,12 +712,14 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
eth::AssemblyItem loopStart = m_context.newTag();
eth::AssemblyItem loopEnd = m_context.newTag();
eth::AssemblyItem loopNext = m_context.newTag();
- m_continueTags.push_back(loopNext);
- m_breakTags.push_back(loopEnd);
+
+ storeStackHeight(&_forStatement);
if (_forStatement.initializationExpression())
_forStatement.initializationExpression()->accept(*this);
+ m_breakTags.push_back({loopEnd, m_context.stackHeight()});
+ m_continueTags.push_back({loopNext, m_context.stackHeight()});
m_context << loopStart;
// if there is no terminating condition in for, default is to always be true
@@ -737,11 +739,16 @@ bool ContractCompiler::visit(ForStatement const& _forStatement)
_forStatement.loopExpression()->accept(*this);
m_context.appendJumpTo(loopStart);
+
m_context << loopEnd;
m_continueTags.pop_back();
m_breakTags.pop_back();
+ // For the case where no break/return is executed:
+ // loop initialization variables have to be freed
+ popScopedVariables(&_forStatement);
+
checker.check();
return false;
}
@@ -750,7 +757,7 @@ bool ContractCompiler::visit(Continue const& _continueStatement)
{
CompilerContext::LocationSetter locationSetter(m_context, _continueStatement);
solAssert(!m_continueTags.empty(), "");
- m_context.appendJumpTo(m_continueTags.back());
+ CompilerUtils(m_context).popAndJump(m_continueTags.back().second, m_continueTags.back().first);
return false;
}
@@ -758,7 +765,7 @@ bool ContractCompiler::visit(Break const& _breakStatement)
{
CompilerContext::LocationSetter locationSetter(m_context, _breakStatement);
solAssert(!m_breakTags.empty(), "");
- m_context.appendJumpTo(m_breakTags.back());
+ CompilerUtils(m_context).popAndJump(m_breakTags.back().second, m_breakTags.back().first);
return false;
}
@@ -784,10 +791,8 @@ bool ContractCompiler::visit(Return const& _return)
for (auto const& retVariable: boost::adaptors::reverse(returnParameters))
CompilerUtils(m_context).moveToStackVariable(*retVariable);
}
- for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
- m_context << Instruction::POP;
- m_context.appendJumpTo(m_returnTags.back());
- m_context.adjustStackOffset(m_stackCleanupForReturn);
+
+ CompilerUtils(m_context).popAndJump(m_returnTags.back().second, m_returnTags.back().first);
return false;
}
@@ -810,8 +815,15 @@ bool ContractCompiler::visit(EmitStatement const& _emit)
bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclarationStatement)
{
- StackHeightChecker checker(m_context);
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
+
+ // Local variable slots are reserved when their declaration is visited,
+ // and freed in the end of their scope.
+ for (auto _decl: _variableDeclarationStatement.declarations())
+ if (_decl)
+ appendStackVariableInitialisation(*_decl);
+
+ StackHeightChecker checker(m_context);
if (Expression const* expression = _variableDeclarationStatement.initialValue())
{
CompilerUtils utils(m_context);
@@ -821,20 +833,19 @@ bool ContractCompiler::visit(VariableDeclarationStatement const& _variableDeclar
valueTypes = tupleType->components();
else
valueTypes = TypePointers{expression->annotation().type};
- auto const& assignments = _variableDeclarationStatement.annotation().assignments;
- solAssert(assignments.size() == valueTypes.size(), "");
- for (size_t i = 0; i < assignments.size(); ++i)
+ auto const& declarations = _variableDeclarationStatement.declarations();
+ solAssert(declarations.size() == valueTypes.size(), "");
+ for (size_t i = 0; i < declarations.size(); ++i)
{
- size_t j = assignments.size() - i - 1;
+ size_t j = declarations.size() - i - 1;
solAssert(!!valueTypes[j], "");
- VariableDeclaration const* varDecl = assignments[j];
- if (!varDecl)
- utils.popStackElement(*valueTypes[j]);
- else
+ if (VariableDeclaration const* varDecl = declarations[j].get())
{
utils.convertType(*valueTypes[j], *varDecl->annotation().type);
utils.moveToStackVariable(*varDecl);
}
+ else
+ utils.popStackElement(*valueTypes[j]);
}
}
checker.check();
@@ -861,6 +872,18 @@ bool ContractCompiler::visit(PlaceholderStatement const& _placeholderStatement)
return true;
}
+bool ContractCompiler::visit(Block const& _block)
+{
+ storeStackHeight(&_block);
+ return true;
+}
+
+void ContractCompiler::endVisit(Block const& _block)
+{
+ // Frees local variables declared in the scope of this block.
+ popScopedVariables(&_block);
+}
+
void ContractCompiler::appendMissingFunctions()
{
while (Declaration const* function = m_context.nextFunctionToCompile())
@@ -916,27 +939,19 @@ void ContractCompiler::appendModifierOrFunctionCode()
modifier.parameters()[i]->annotation().type
);
}
- for (VariableDeclaration const* localVariable: modifier.localVariables())
- {
- addedVariables.push_back(localVariable);
- appendStackVariableInitialisation(*localVariable);
- }
- stackSurplus =
- CompilerUtils::sizeOnStack(modifier.parameters()) +
- CompilerUtils::sizeOnStack(modifier.localVariables());
+ stackSurplus = CompilerUtils::sizeOnStack(modifier.parameters());
codeBlock = &modifier.body();
}
}
if (codeBlock)
{
- m_returnTags.push_back(m_context.newTag());
-
+ m_returnTags.push_back({m_context.newTag(), m_context.stackHeight()});
codeBlock->accept(*this);
solAssert(!m_returnTags.empty(), "");
- m_context << m_returnTags.back();
+ m_context << m_returnTags.back().first;
m_returnTags.pop_back();
CompilerUtils(m_context).popStackSlots(stackSurplus);
@@ -983,3 +998,20 @@ eth::AssemblyPointer ContractCompiler::cloneRuntime() const
a << u256(0x20) << u256(0) << Instruction::RETURN;
return make_shared<eth::Assembly>(a);
}
+
+void ContractCompiler::popScopedVariables(ASTNode const* _node)
+{
+ unsigned blockHeight = m_scopeStackHeight.at(m_modifierDepth).at(_node);
+ m_context.removeVariablesAboveStackHeight(blockHeight);
+ solAssert(m_context.stackHeight() >= blockHeight, "");
+ unsigned stackDiff = m_context.stackHeight() - blockHeight;
+ CompilerUtils(m_context).popStackSlots(stackDiff);
+ m_scopeStackHeight[m_modifierDepth].erase(_node);
+ if (m_scopeStackHeight[m_modifierDepth].size() == 0)
+ m_scopeStackHeight.erase(m_modifierDepth);
+}
+
+void ContractCompiler::storeStackHeight(ASTNode const* _node)
+{
+ m_scopeStackHeight[m_modifierDepth][_node] = m_context.stackHeight();
+}
diff --git a/libsolidity/codegen/ContractCompiler.h b/libsolidity/codegen/ContractCompiler.h
index 02a3452f..8516ec2c 100644
--- a/libsolidity/codegen/ContractCompiler.h
+++ b/libsolidity/codegen/ContractCompiler.h
@@ -109,6 +109,8 @@ private:
virtual bool visit(VariableDeclarationStatement const& _variableDeclarationStatement) override;
virtual bool visit(ExpressionStatement const& _expressionStatement) override;
virtual bool visit(PlaceholderStatement const&) override;
+ virtual bool visit(Block const& _block) override;
+ virtual void endVisit(Block const& _block) override;
/// Repeatedly visits all function which are referenced but which are not compiled yet.
void appendMissingFunctions();
@@ -123,19 +125,31 @@ private:
/// @returns the runtime assembly for clone contracts.
eth::AssemblyPointer cloneRuntime() const;
+ /// Frees the variables of a certain scope (to be used when leaving).
+ void popScopedVariables(ASTNode const* _node);
+
+ /// Sets the stack height for the visited loop.
+ void storeStackHeight(ASTNode const* _node);
+
bool const m_optimise;
/// Pointer to the runtime compiler in case this is a creation compiler.
ContractCompiler* m_runtimeCompiler = nullptr;
CompilerContext& m_context;
- std::vector<eth::AssemblyItem> m_breakTags; ///< tag to jump to for a "break" statement
- std::vector<eth::AssemblyItem> m_continueTags; ///< tag to jump to for a "continue" statement
- /// Tag to jump to for a "return" statement, needs to be stacked because of modifiers.
- std::vector<eth::AssemblyItem> m_returnTags;
+ /// Tag to jump to for a "break" statement and the stack height after freeing the local loop variables.
+ std::vector<std::pair<eth::AssemblyItem, unsigned>> m_breakTags;
+ /// Tag to jump to for a "continue" statement and the stack height after freeing the local loop variables.
+ std::vector<std::pair<eth::AssemblyItem, unsigned>> m_continueTags;
+ /// Tag to jump to for a "return" statement and the stack height after freeing the local function or modifier variables.
+ /// Needs to be stacked because of modifiers.
+ std::vector<std::pair<eth::AssemblyItem, unsigned>> m_returnTags;
unsigned m_modifierDepth = 0;
FunctionDefinition const* m_currentFunction = nullptr;
- unsigned m_stackCleanupForReturn = 0; ///< this number of stack elements need to be removed before jump to m_returnTag
+
// arguments for base constructors, filled in derived-to-base order
std::map<FunctionDefinition const*, ASTNode const*> const* m_baseArguments;
+
+ /// Stores the variables that were declared inside a specific scope, for each modifier depth.
+ std::map<unsigned, std::map<ASTNode const*, unsigned>> m_scopeStackHeight;
};
}
diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp
index 2e548e32..54518906 100644
--- a/libsolidity/codegen/ExpressionCompiler.cpp
+++ b/libsolidity/codegen/ExpressionCompiler.cpp
@@ -349,6 +349,10 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
case Token::Inc: // ++ (pre- or postfix)
case Token::Dec: // -- (pre- or postfix)
solAssert(!!m_currentLValue, "LValue not retrieved.");
+ solUnimplementedAssert(
+ _unaryOperation.annotation().type->category() != Type::Category::FixedPoint,
+ "Not yet implemented - FixedPointType."
+ );
m_currentLValue->retrieveValue(_unaryOperation.location());
if (!_unaryOperation.isPrefixOperation())
{
@@ -1210,63 +1214,52 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess)
switch (_memberAccess.expression().annotation().type->category())
{
case Type::Category::Contract:
- case Type::Category::Integer:
{
- bool alsoSearchInteger = false;
- if (_memberAccess.expression().annotation().type->category() == Type::Category::Contract)
+ ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
+ if (type.isSuper())
{
- ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
- if (type.isSuper())
- {
- solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
- utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
- dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
- type.contractDefinition()
- ));
- }
+ solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
+ utils().pushCombinedFunctionEntryLabel(m_context.superFunction(
+ dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
+ type.contractDefinition()
+ ));
+ }
+ // ordinary contract type
+ else if (Declaration const* declaration = _memberAccess.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
- {
- // ordinary contract type
- if (Declaration const* declaration = _memberAccess.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.");
- utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true);
- m_context << identifier;
- }
- else
- // not found in contract, search in members inherited from address
- alsoSearchInteger = true;
- }
+ solAssert(false, "Contract member is neither variable nor function.");
+ utils().convertType(type, IntegerType(160, IntegerType::Modifier::Address), true);
+ m_context << identifier;
}
else
- alsoSearchInteger = true;
-
- if (alsoSearchInteger)
+ solAssert(false, "Invalid member access in contract");
+ break;
+ }
+ case Type::Category::Integer:
+ {
+ if (member == "balance")
{
- if (member == "balance")
- {
- utils().convertType(
- *_memberAccess.expression().annotation().type,
- IntegerType(160, IntegerType::Modifier::Address),
- true
- );
- m_context << Instruction::BALANCE;
- }
- else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
- utils().convertType(
- *_memberAccess.expression().annotation().type,
- IntegerType(160, IntegerType::Modifier::Address),
- true
- );
- else
- solAssert(false, "Invalid member access to integer");
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(160, IntegerType::Modifier::Address),
+ true
+ );
+ m_context << Instruction::BALANCE;
}
+ else if ((set<string>{"send", "transfer", "call", "callcode", "delegatecall"}).count(member))
+ utils().convertType(
+ *_memberAccess.expression().annotation().type,
+ IntegerType(160, IntegerType::Modifier::Address),
+ true
+ );
+ else
+ solAssert(false, "Invalid member access to integer");
break;
}
case Type::Category::Function:
@@ -1647,12 +1640,12 @@ void ExpressionCompiler::appendOrdinaryBinaryOperatorCode(Token::Value _operator
void ExpressionCompiler::appendArithmeticOperatorCode(Token::Value _operator, Type const& _type)
{
- IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
- bool const c_isSigned = type.isSigned();
-
if (_type.category() == Type::Category::FixedPoint)
solUnimplemented("Not yet implemented - FixedPointType.");
+ IntegerType const& type = dynamic_cast<IntegerType const&>(_type);
+ bool const c_isSigned = type.isSigned();
+
switch (_operator)
{
case Token::Add:
@@ -1751,7 +1744,7 @@ void ExpressionCompiler::appendShiftOperatorCode(Token::Value _operator, Type co
{
if (c_valueSigned)
// In the following assembly snippet, xor_mask will be zero, if value_to_shift is positive.
- // Therefor xor'ing with xor_mask is the identity and the computation reduces to
+ // Therefore xor'ing with xor_mask is the identity and the computation reduces to
// div(value_to_shift, exp(2, shift_amount)), which is correct, since for positive values
// arithmetic right shift is dividing by a power of two (which, as a bitwise operation, results
// in discarding bits on the right and filling with zeros from the left).
@@ -1810,15 +1803,11 @@ void ExpressionCompiler::appendExternalFunctionCall(
if (_functionType.bound())
utils().moveToStackTop(gasValueSize, _functionType.selfType()->sizeOnStack());
- bool const v050 = m_context.experimentalFeatureActive(ExperimentalFeature::V050);
auto funKind = _functionType.kind();
bool returnSuccessCondition = funKind == FunctionType::Kind::BareCall || funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::BareDelegateCall;
bool isCallCode = funKind == FunctionType::Kind::BareCallCode || funKind == FunctionType::Kind::CallCode;
bool isDelegateCall = funKind == FunctionType::Kind::BareDelegateCall || funKind == FunctionType::Kind::DelegateCall;
- bool useStaticCall =
- _functionType.stateMutability() <= StateMutability::View &&
- v050 &&
- m_context.evmVersion().hasStaticCall();
+ bool useStaticCall = _functionType.stateMutability() <= StateMutability::View && m_context.evmVersion().hasStaticCall();
bool haveReturndatacopy = m_context.evmVersion().supportsReturndata();
unsigned retSize = 0;
@@ -1879,7 +1868,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
{
m_context << u256(0);
utils().fetchFreeMemoryPointer();
- // This touches too much, but that way we save some rounding arithmetics
+ // This touches too much, but that way we save some rounding arithmetic
m_context << u256(retSize) << Instruction::ADD << Instruction::MSTORE;
}
}
diff --git a/libsolidity/formal/SMTChecker.cpp b/libsolidity/formal/SMTChecker.cpp
index a4d9500b..e2a51267 100644
--- a/libsolidity/formal/SMTChecker.cpp
+++ b/libsolidity/formal/SMTChecker.cpp
@@ -95,7 +95,7 @@ bool SMTChecker::visit(FunctionDefinition const& _function)
void SMTChecker::endVisit(FunctionDefinition const&)
{
- // TOOD we could check for "reachability", i.e. satisfiability here.
+ // TODO we could check for "reachability", i.e. satisfiability here.
// We only handle local variables, so we clear at the beginning of the function.
// If we add storage variables, those should be cleared differently.
removeLocalVariables();
diff --git a/libsolidity/inlineasm/AsmParser.cpp b/libsolidity/inlineasm/AsmParser.cpp
index 7df9ab88..cd429a98 100644
--- a/libsolidity/inlineasm/AsmParser.cpp
+++ b/libsolidity/inlineasm/AsmParser.cpp
@@ -150,7 +150,7 @@ assembly::Statement Parser::parseStatement()
expectToken(Token::Comma);
elementary = parseElementaryOperation();
if (elementary.type() != typeid(assembly::Identifier))
- fatalParserError("Variable name expected in multiple assignemnt.");
+ fatalParserError("Variable name expected in multiple assignment.");
assignment.variableNames.emplace_back(boost::get<assembly::Identifier>(elementary));
}
while (currentToken() == Token::Comma);
diff --git a/libsolidity/interface/AssemblyStack.cpp b/libsolidity/interface/AssemblyStack.cpp
index a4b0265e..46fa1d6b 100644
--- a/libsolidity/interface/AssemblyStack.cpp
+++ b/libsolidity/interface/AssemblyStack.cpp
@@ -119,7 +119,7 @@ MachineAssemblyObject AssemblyStack::assemble(Machine _machine) const
julia::EVMAssembly assembly(true);
julia::CodeTransform(assembly, *m_analysisInfo, m_language == Language::Yul, true)(*m_parserResult);
object.bytecode = make_shared<eth::LinkerObject>(assembly.finalize());
- /// TOOD: fill out text representation
+ /// TODO: fill out text representation
return object;
}
case Machine::eWasm:
diff --git a/libsolidity/interface/CompilerStack.h b/libsolidity/interface/CompilerStack.h
index 4359c3fa..0578ac86 100644
--- a/libsolidity/interface/CompilerStack.h
+++ b/libsolidity/interface/CompilerStack.h
@@ -93,7 +93,7 @@ public:
m_errorList(),
m_errorReporter(m_errorList) {}
- /// @returns the list of errors that occured during parsing and type checking.
+ /// @returns the list of errors that occurred during parsing and type checking.
ErrorList const& errors() const { return m_errorReporter.errors(); }
/// @returns the current state.
diff --git a/libsolidity/interface/ErrorReporter.h b/libsolidity/interface/ErrorReporter.h
index d1a0030f..fd53587a 100644
--- a/libsolidity/interface/ErrorReporter.h
+++ b/libsolidity/interface/ErrorReporter.h
@@ -92,6 +92,12 @@ public:
void clear();
+ /// @returns true iff there is any error (ignores warnings).
+ bool hasErrors() const
+ {
+ return m_errorCount > 0;
+ }
+
private:
void error(Error::Type _type,
SourceLocation const& _location,
diff --git a/libsolidity/interface/Natspec.cpp b/libsolidity/interface/Natspec.cpp
index 7f7084ef..29a5b798 100644
--- a/libsolidity/interface/Natspec.cpp
+++ b/libsolidity/interface/Natspec.cpp
@@ -36,6 +36,10 @@ Json::Value Natspec::userDocumentation(ContractDefinition const& _contractDef)
Json::Value doc;
Json::Value methods(Json::objectValue);
+ string notice = extractDoc(_contractDef.annotation().docTags, "notice");
+ if (!notice.empty())
+ doc["notice"] = Json::Value(notice);
+
for (auto const& it: _contractDef.interfaceFunctions())
if (it.second->hasDeclaration())
if (auto const* f = dynamic_cast<FunctionDefinition const*>(&it.second->declaration()))
@@ -65,6 +69,9 @@ Json::Value Natspec::devDocumentation(ContractDefinition const& _contractDef)
auto title = extractDoc(_contractDef.annotation().docTags, "title");
if (!title.empty())
doc["title"] = title;
+ auto dev = extractDoc(_contractDef.annotation().docTags, "dev");
+ if (!dev.empty())
+ doc["details"] = Json::Value(dev);
for (auto const& it: _contractDef.interfaceFunctions())
{
diff --git a/libsolidity/interface/Natspec.h b/libsolidity/interface/Natspec.h
index 0701f821..6a827d3b 100644
--- a/libsolidity/interface/Natspec.h
+++ b/libsolidity/interface/Natspec.h
@@ -45,7 +45,7 @@ public:
/// @param _contractDef The contract definition
/// @return A JSON representation of the contract's user documentation
static Json::Value userDocumentation(ContractDefinition const& _contractDef);
- /// Genereates the Developer's documentation of the contract
+ /// Generates the Developer's documentation of the contract
/// @param _contractDef The contract definition
/// @return A JSON representation
/// of the contract's developer documentation
diff --git a/libsolidity/interface/StandardCompiler.h b/libsolidity/interface/StandardCompiler.h
index 11a0b4c2..2772394a 100644
--- a/libsolidity/interface/StandardCompiler.h
+++ b/libsolidity/interface/StandardCompiler.h
@@ -31,7 +31,7 @@ namespace solidity
{
/**
- * Standard JSON compiler interface, which expects a JSON input and returns a JSON ouput.
+ * Standard JSON compiler interface, which expects a JSON input and returns a JSON output.
* See docs/using-the-compiler#compiler-input-and-output-json-description.
*/
class StandardCompiler: boost::noncopyable
diff --git a/libsolidity/parsing/Parser.cpp b/libsolidity/parsing/Parser.cpp
index e2bd6fb4..0bee2a91 100644
--- a/libsolidity/parsing/Parser.cpp
+++ b/libsolidity/parsing/Parser.cpp
@@ -239,13 +239,10 @@ ASTPointer<ContractDefinition> Parser::parseContractDefinition(Token::Value _exp
Token::Value currentTokenValue = m_scanner->currentToken();
if (currentTokenValue == Token::RBrace)
break;
- else if (
- currentTokenValue == Token::Function ||
- (currentTokenValue == Token::Identifier && m_scanner->currentLiteral() == "constructor")
- )
+ else if (currentTokenValue == Token::Function || currentTokenValue == Token::Constructor)
// This can be a function or a state variable of function type (especially
// complicated to distinguish fallback function from function type state variable)
- subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable(name.get()));
+ subNodes.push_back(parseFunctionDefinitionOrFunctionTypeStateVariable());
else if (currentTokenValue == Token::Struct)
subNodes.push_back(parseStructDefinition());
else if (currentTokenValue == Token::Enum)
@@ -340,30 +337,31 @@ StateMutability Parser::parseStateMutability(Token::Value _token)
return stateMutability;
}
-Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(
- bool _forceEmptyName,
- bool _allowModifiers,
- ASTString const* _contractName
-)
+Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers)
{
RecursionGuard recursionGuard(*this);
FunctionHeaderParserResult result;
result.isConstructor = false;
- if (m_scanner->currentToken() == Token::Identifier && m_scanner->currentLiteral() == "constructor")
+ if (m_scanner->currentToken() == Token::Constructor)
result.isConstructor = true;
else if (m_scanner->currentToken() != Token::Function)
solAssert(false, "Function or constructor expected.");
m_scanner->next();
- if (result.isConstructor || _forceEmptyName || m_scanner->currentToken() == Token::LParen)
+ if (result.isConstructor)
result.name = make_shared<ASTString>();
+ else if (_forceEmptyName || m_scanner->currentToken() == Token::LParen)
+ result.name = make_shared<ASTString>();
+ else if (m_scanner->currentToken() == Token::Constructor)
+ fatalParserError(string(
+ "This function is named \"constructor\" but is not the constructor of the contract. "
+ "If you intend this to be a constructor, use \"constructor(...) { ... }\" without the \"function\" keyword to define it."
+ ));
else
result.name = expectIdentifierToken();
- if (!result.name->empty() && _contractName && *result.name == *_contractName)
- result.isConstructor = true;
VarDeclParserOptions options;
options.allowLocationSpecifier = true;
@@ -435,7 +433,7 @@ Parser::FunctionHeaderParserResult Parser::parseFunctionHeader(
return result;
}
-ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName)
+ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable()
{
RecursionGuard recursionGuard(*this);
ASTNodeFactory nodeFactory(*this);
@@ -443,7 +441,7 @@ ASTPointer<ASTNode> Parser::parseFunctionDefinitionOrFunctionTypeStateVariable(A
if (m_scanner->currentCommentLiteral() != "")
docstring = make_shared<ASTString>(m_scanner->currentCommentLiteral());
- FunctionHeaderParserResult header = parseFunctionHeader(false, true, _contractName);
+ FunctionHeaderParserResult header = parseFunctionHeader(false, true);
if (
header.isConstructor ||
diff --git a/libsolidity/parsing/Parser.h b/libsolidity/parsing/Parser.h
index 08653364..c906771a 100644
--- a/libsolidity/parsing/Parser.h
+++ b/libsolidity/parsing/Parser.h
@@ -74,12 +74,8 @@ private:
ASTPointer<InheritanceSpecifier> parseInheritanceSpecifier();
Declaration::Visibility parseVisibilitySpecifier(Token::Value _token);
StateMutability parseStateMutability(Token::Value _token);
- FunctionHeaderParserResult parseFunctionHeader(
- bool _forceEmptyName,
- bool _allowModifiers,
- ASTString const* _contractName = nullptr
- );
- ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable(ASTString const* _contractName);
+ FunctionHeaderParserResult parseFunctionHeader(bool _forceEmptyName, bool _allowModifiers);
+ ASTPointer<ASTNode> parseFunctionDefinitionOrFunctionTypeStateVariable();
ASTPointer<FunctionDefinition> parseFunctionDefinition(ASTString const* _contractName);
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<EnumDefinition> parseEnumDefinition();
diff --git a/libsolidity/parsing/Scanner.h b/libsolidity/parsing/Scanner.h
index 0adaa6fd..8a3011eb 100644
--- a/libsolidity/parsing/Scanner.h
+++ b/libsolidity/parsing/Scanner.h
@@ -223,7 +223,7 @@ private:
bool isSourcePastEndOfInput() const { return m_source.isPastEndOfInput(); }
TokenDesc m_skippedComment; // desc for current skipped comment
- TokenDesc m_nextSkippedComment; // desc for next skiped comment
+ TokenDesc m_nextSkippedComment; // desc for next skipped comment
TokenDesc m_currentToken; // desc for current token (as returned by Next())
TokenDesc m_nextToken; // desc for next token (one token look-ahead)
diff --git a/libsolidity/parsing/Token.h b/libsolidity/parsing/Token.h
index cb855cbe..7ce24e69 100644
--- a/libsolidity/parsing/Token.h
+++ b/libsolidity/parsing/Token.h
@@ -144,6 +144,7 @@ namespace solidity
K(Assembly, "assembly", 0) \
K(Break, "break", 0) \
K(Constant, "constant", 0) \
+ K(Constructor, "constructor", 0) \
K(Continue, "continue", 0) \
K(Contract, "contract", 0) \
K(Do, "do", 0) \