aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2015-01-22 08:02:38 +0800
committerChristian <c@ethdev.com>2015-01-26 17:23:39 +0800
commit941c77c8fadd6195809cd2d572feb64478c4fb20 (patch)
treea4de3710aff5f3f2d9423565fd0e9ecc8708b5bf
parent19793dab093ae36b5f2b4d1cabfbf54bed3125b1 (diff)
downloaddexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.tar.gz
dexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.tar.zst
dexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.zip
Type resolution for function modifiers.
-rw-r--r--AST.cpp123
-rwxr-xr-xAST.h84
-rw-r--r--ASTForward.h3
-rw-r--r--ASTJsonConverter.cpp3
-rw-r--r--ASTPrinter.cpp12
-rw-r--r--ASTPrinter.h2
-rw-r--r--ASTVisitor.h4
-rw-r--r--AST_accept.h22
-rw-r--r--NameAndTypeResolver.cpp18
-rw-r--r--NameAndTypeResolver.h4
-rw-r--r--Parser.cpp35
-rw-r--r--Parser.h1
-rw-r--r--Types.cpp32
-rw-r--r--Types.h23
-rw-r--r--grammar.txt6
15 files changed, 299 insertions, 73 deletions
diff --git a/AST.cpp b/AST.cpp
index a51bbd0c..85d7db6e 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -41,10 +41,15 @@ TypeError ASTNode::createTypeError(string const& _description) const
return TypeError() << errinfo_sourceLocation(getLocation()) << errinfo_comment(_description);
}
+TypePointer ContractDefinition::getType(ContractDefinition const* _currentContract) const
+{
+ return make_shared<TypeType>(make_shared<ContractType>(*this), _currentContract);
+}
+
void ContractDefinition::checkTypeRequirements()
{
- for (ASTPointer<InheritanceSpecifier> const& base: getBaseContracts())
- base->checkTypeRequirements();
+ for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: getBaseContracts())
+ baseSpecifier->checkTypeRequirements();
checkIllegalOverrides();
@@ -53,6 +58,9 @@ void ContractDefinition::checkTypeRequirements()
BOOST_THROW_EXCEPTION(constructor->getReturnParameterList()->createTypeError(
"Non-empty \"returns\" directive for constructor."));
+ for (ASTPointer<ModifierDefinition> const& modifier: getFunctionModifiers())
+ modifier->checkTypeRequirements();
+
for (ASTPointer<FunctionDefinition> const& function: getDefinedFunctions())
function->checkTypeRequirements();
@@ -89,15 +97,22 @@ FunctionDefinition const* ContractDefinition::getConstructor() const
void ContractDefinition::checkIllegalOverrides() const
{
+ // TODO unify this at a later point. for this we need to put the constness and the access specifier
+ // into the types
map<string, FunctionDefinition const*> functions;
+ map<string, ModifierDefinition const*> modifiers;
// We search from derived to base, so the stored item causes the error.
for (ContractDefinition const* contract: getLinearizedBaseContracts())
+ {
for (ASTPointer<FunctionDefinition> const& function: contract->getDefinedFunctions())
{
if (function->isConstructor())
- continue; // constructors can neither be overriden nor override anything
- FunctionDefinition const*& override = functions[function->getName()];
+ continue; // constructors can neither be overridden nor override anything
+ string const& name = function->getName();
+ if (modifiers.count(name))
+ BOOST_THROW_EXCEPTION(modifiers[name]->createTypeError("Override changes function to modifier."));
+ FunctionDefinition const*& override = functions[name];
if (!override)
override = function.get();
else if (override->isPublic() != function->isPublic() ||
@@ -105,6 +120,18 @@ void ContractDefinition::checkIllegalOverrides() const
FunctionType(*override) != FunctionType(*function))
BOOST_THROW_EXCEPTION(override->createTypeError("Override changes extended function signature."));
}
+ for (ASTPointer<ModifierDefinition> const& modifier: contract->getFunctionModifiers())
+ {
+ string const& name = modifier->getName();
+ if (functions.count(name))
+ BOOST_THROW_EXCEPTION(functions[name]->createTypeError("Override changes modifier to function."));
+ ModifierDefinition const*& override = modifiers[name];
+ if (!override)
+ override = modifier.get();
+ else if (ModifierType(*override) != ModifierType(*modifier))
+ BOOST_THROW_EXCEPTION(override->createTypeError("Override changes modifier signature."));
+ }
+ }
}
vector<pair<FixedHash<4>, FunctionDefinition const*>> const& ContractDefinition::getInterfaceFunctionList() const
@@ -141,6 +168,11 @@ void InheritanceSpecifier::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in constructer call."));
}
+TypePointer StructDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<TypeType>(make_shared<StructType>(*this));
+}
+
void StructDefinition::checkMemberTypes() const
{
for (ASTPointer<VariableDeclaration> const& member: getMembers())
@@ -169,11 +201,18 @@ void StructDefinition::checkRecursion() const
}
}
+TypePointer FunctionDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<FunctionType>(*this);
+}
+
void FunctionDefinition::checkTypeRequirements()
{
for (ASTPointer<VariableDeclaration> const& var: getParameters() + getReturnParameters())
if (!var->getType()->canLiveOutsideStorage())
BOOST_THROW_EXCEPTION(var->createTypeError("Type is required to live outside storage."));
+ for (ASTPointer<ModifierInvocation> const& modifier: m_functionModifiers)
+ modifier->checkTypeRequirements();
m_body->checkTypeRequirements();
}
@@ -183,11 +222,40 @@ string FunctionDefinition::getCanonicalSignature() const
return getName() + FunctionType(*this).getCanonicalSignature();
}
+Declaration::LValueType VariableDeclaration::getLValueType() const
+{
+ if (dynamic_cast<FunctionDefinition const*>(getScope()))
+ return Declaration::LValueType::LOCAL;
+ else
+ return Declaration::LValueType::STORAGE;
+}
+
+TypePointer ModifierDefinition::getType(ContractDefinition const*) const
+{
+ return make_shared<ModifierType>(*this);
+}
+
void ModifierDefinition::checkTypeRequirements()
{
m_body->checkTypeRequirements();
}
+void ModifierInvocation::checkTypeRequirements()
+{
+ m_modifierName->checkTypeRequirements();
+ for (ASTPointer<Expression> const& argument: m_arguments)
+ argument->checkTypeRequirements();
+
+ ModifierDefinition const* modifier = dynamic_cast<ModifierDefinition const*>(m_modifierName->getReferencedDeclaration());
+ solAssert(modifier, "Function modifier not found.");
+ vector<ASTPointer<VariableDeclaration>> const& parameters = modifier->getParameters();
+ if (parameters.size() != m_arguments.size())
+ BOOST_THROW_EXCEPTION(createTypeError("Wrong argument count for modifier invocation."));
+ for (size_t i = 0; i < m_arguments.size(); ++i)
+ if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType()))
+ BOOST_THROW_EXCEPTION(createTypeError("Invalid type for argument in modifier invocation."));
+}
+
void Block::checkTypeRequirements()
{
for (shared_ptr<Statement> const& statement: m_statements)
@@ -399,7 +467,7 @@ void MemberAccess::checkTypeRequirements()
BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not "
"visible in " + type.toString()));
//@todo later, this will not always be STORAGE
- m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE;
+ m_lvalue = type.getCategory() == Type::Category::STRUCT ? Declaration::LValueType::STORAGE : Declaration::LValueType::NONE;
}
void IndexAccess::checkTypeRequirements()
@@ -411,52 +479,17 @@ void IndexAccess::checkTypeRequirements()
MappingType const& type = dynamic_cast<MappingType const&>(*m_base->getType());
m_index->expectType(*type.getKeyType());
m_type = type.getValueType();
- m_lvalue = LValueType::STORAGE;
+ m_lvalue = Declaration::LValueType::STORAGE;
}
void Identifier::checkTypeRequirements()
{
solAssert(m_referencedDeclaration, "Identifier not resolved.");
- VariableDeclaration const* variable = dynamic_cast<VariableDeclaration const*>(m_referencedDeclaration);
- if (variable)
- {
- if (!variable->getType())
- BOOST_THROW_EXCEPTION(createTypeError("Variable referenced before type could be determined."));
- m_type = variable->getType();
- m_lvalue = variable->isLocalVariable() ? LValueType::LOCAL : LValueType::STORAGE;
- return;
- }
- //@todo can we unify these with TypeName::toType()?
- StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(m_referencedDeclaration);
- if (structDef)
- {
- // note that we do not have a struct type here
- m_type = make_shared<TypeType>(make_shared<StructType>(*structDef));
- return;
- }
- FunctionDefinition const* functionDef = dynamic_cast<FunctionDefinition const*>(m_referencedDeclaration);
- if (functionDef)
- {
- // a function reference is not a TypeType, because calling a TypeType converts to the type.
- // Calling a function (e.g. function(12), otherContract.function(34)) does not do a type
- // conversion.
- m_type = make_shared<FunctionType>(*functionDef);
- return;
- }
- ContractDefinition const* contractDef = dynamic_cast<ContractDefinition const*>(m_referencedDeclaration);
- if (contractDef)
- {
- m_type = make_shared<TypeType>(make_shared<ContractType>(*contractDef), m_currentContract);
- return;
- }
- MagicVariableDeclaration const* magicVariable = dynamic_cast<MagicVariableDeclaration const*>(m_referencedDeclaration);
- if (magicVariable)
- {
- m_type = magicVariable->getType();
- return;
- }
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Declaration reference of unknown/forbidden type."));
+ m_lvalue = m_referencedDeclaration->getLValueType();
+ m_type = m_referencedDeclaration->getType(m_currentContract);
+ if (!m_type)
+ BOOST_THROW_EXCEPTION(createTypeError("Declaration referenced before type could be determined."));
}
void ElementaryTypeNameExpression::checkTypeRequirements()
diff --git a/AST.h b/AST.h
index e48e45d3..ded30e9c 100755
--- a/AST.h
+++ b/AST.h
@@ -132,6 +132,8 @@ private:
class Declaration: public ASTNode
{
public:
+ enum class LValueType { NONE, LOCAL, STORAGE };
+
Declaration(Location const& _location, ASTPointer<ASTString> const& _name):
ASTNode(_location), m_name(_name), m_scope(nullptr) {}
@@ -142,6 +144,13 @@ public:
Declaration const* getScope() const { return m_scope; }
void setScope(Declaration const* _scope) { m_scope = _scope; }
+ /// @returns the type of expressions referencing this declaration.
+ /// The current contract has to be given since this context can change the type, especially of
+ /// contract types.
+ virtual TypePointer getType(ContractDefinition const* m_currentContract = nullptr) const = 0;
+ /// @returns the lvalue type of expressions referencing this declaration
+ virtual LValueType getLValueType() const { return LValueType::NONE; }
+
private:
ASTPointer<ASTString> m_name;
Declaration const* m_scope;
@@ -178,8 +187,11 @@ public:
std::vector<ASTPointer<InheritanceSpecifier>> const& getBaseContracts() const { return m_baseContracts; }
std::vector<ASTPointer<StructDefinition>> const& getDefinedStructs() const { return m_definedStructs; }
std::vector<ASTPointer<VariableDeclaration>> const& getStateVariables() const { return m_stateVariables; }
+ std::vector<ASTPointer<ModifierDefinition>> const& getFunctionModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<FunctionDefinition>> const& getDefinedFunctions() const { return m_definedFunctions; }
+ virtual TypePointer getType(ContractDefinition const* m_currentContract) const override;
+
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
/// and calls checkTypeRequirements on all its functions.
void checkTypeRequirements();
@@ -248,6 +260,8 @@ public:
std::vector<ASTPointer<VariableDeclaration>> const& getMembers() const { return m_members; }
+ virtual TypePointer getType(ContractDefinition const*) const override;
+
/// Checks that the members do not include any recursive structs and have valid types
/// (e.g. no functions).
void checkValidityOfMembers() const;
@@ -279,7 +293,20 @@ private:
std::vector<ASTPointer<VariableDeclaration>> m_parameters;
};
-class FunctionDefinition: public Declaration
+/**
+ * Abstract class that is added to each AST node that can store local variables.
+ */
+class VariableScope
+{
+public:
+ void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
+ std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
+
+private:
+ std::vector<VariableDeclaration const*> m_localVariables;
+};
+
+class FunctionDefinition: public Declaration, public VariableScope
{
public:
FunctionDefinition(Location const& _location, ASTPointer<ASTString> const& _name,
@@ -288,11 +315,13 @@ public:
ASTPointer<ASTString> const& _documentation,
ASTPointer<ParameterList> const& _parameters,
bool _isDeclaredConst,
+ std::vector<ASTPointer<ModifierInvocation>> const& _modifiers,
ASTPointer<ParameterList> const& _returnParameters,
ASTPointer<Block> const& _body):
Declaration(_location, _name), m_isPublic(_isPublic), m_isConstructor(_isConstructor),
m_parameters(_parameters),
m_isDeclaredConst(_isDeclaredConst),
+ m_functionModifiers(_modifiers),
m_returnParameters(_returnParameters),
m_body(_body),
m_documentation(_documentation)
@@ -304,6 +333,7 @@ public:
bool isPublic() const { return m_isPublic; }
bool isConstructor() const { return m_isConstructor; }
bool isDeclaredConst() const { return m_isDeclaredConst; }
+ std::vector<ASTPointer<ModifierInvocation>> const& getModifiers() const { return m_functionModifiers; }
std::vector<ASTPointer<VariableDeclaration>> const& getParameters() const { return m_parameters->getParameters(); }
ParameterList const& getParameterList() const { return *m_parameters; }
std::vector<ASTPointer<VariableDeclaration>> const& getReturnParameters() const { return m_returnParameters->getParameters(); }
@@ -313,8 +343,7 @@ public:
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
- void addLocalVariable(VariableDeclaration const& _localVariable) { m_localVariables.push_back(&_localVariable); }
- std::vector<VariableDeclaration const*> const& getLocalVariables() const { return m_localVariables; }
+ virtual TypePointer getType(ContractDefinition const*) const override;
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
void checkTypeRequirements();
@@ -329,11 +358,10 @@ private:
bool m_isConstructor;
ASTPointer<ParameterList> m_parameters;
bool m_isDeclaredConst;
+ std::vector<ASTPointer<ModifierInvocation>> m_functionModifiers;
ASTPointer<ParameterList> m_returnParameters;
ASTPointer<Block> m_body;
ASTPointer<ASTString> m_documentation;
-
- std::vector<VariableDeclaration const*> m_localVariables;
};
/**
@@ -353,10 +381,10 @@ public:
/// Returns the declared or inferred type. Can be an empty pointer if no type was explicitly
/// declared and there is no assignment to the variable that fixes the type.
- std::shared_ptr<Type const> const& getType() const { return m_type; }
+ TypePointer getType(ContractDefinition const* = nullptr) const { return m_type; }
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
- bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(getScope()); }
+ virtual LValueType getLValueType() const override;
private:
ASTPointer<TypeName> m_typeName; ///< can be empty ("var")
@@ -367,7 +395,7 @@ private:
/**
* Definition of a function modifier.
*/
-class ModifierDefinition: public Declaration
+class ModifierDefinition: public Declaration, public VariableScope
{
public:
ModifierDefinition(Location const& _location,
@@ -385,6 +413,8 @@ public:
ParameterList const& getParameterList() const { return *m_parameters; }
Block const& getBody() const { return *m_body; }
+ virtual TypePointer getType(ContractDefinition const* = nullptr) const override;
+
/// @return A shared pointer of an ASTString.
/// Can contain a nullptr in which case indicates absence of documentation
ASTPointer<ASTString> const& getDocumentation() const { return m_documentation; }
@@ -398,6 +428,29 @@ private:
};
/**
+ * Invocation/usage of a modifier in a function header.
+ */
+class ModifierInvocation: public ASTNode
+{
+public:
+ ModifierInvocation(Location const& _location, ASTPointer<Identifier> const& _name,
+ std::vector<ASTPointer<Expression>> _arguments):
+ ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
+
+ virtual void accept(ASTVisitor& _visitor) override;
+ virtual void accept(ASTConstVisitor& _visitor) const override;
+
+ ASTPointer<Identifier> const& getName() const { return m_modifierName; }
+ std::vector<ASTPointer<Expression>> const& getArguments() const { return m_arguments; }
+
+ void checkTypeRequirements();
+
+private:
+ ASTPointer<Identifier> m_modifierName;
+ std::vector<ASTPointer<Expression>> m_arguments;
+};
+
+/**
* Pseudo AST node that is used as declaration for "this", "msg", "tx", "block" and the global
* functions when such an identifier is encountered. Will never have a valid location in the source code.
*/
@@ -411,7 +464,7 @@ public:
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
- std::shared_ptr<Type const> const& getType() const { return m_type; }
+ virtual TypePointer getType(ContractDefinition const* = nullptr) const override { return m_type; }
private:
std::shared_ptr<Type const> m_type;
@@ -737,16 +790,13 @@ private:
*/
class Expression: public ASTNode
{
-protected:
- enum class LValueType { NONE, LOCAL, STORAGE };
-
public:
- Expression(Location const& _location): ASTNode(_location), m_lvalue(LValueType::NONE), m_lvalueRequested(false) {}
+ Expression(Location const& _location): ASTNode(_location) {}
virtual void checkTypeRequirements() = 0;
std::shared_ptr<Type const> const& getType() const { return m_type; }
- bool isLValue() const { return m_lvalue != LValueType::NONE; }
- bool isLocalLValue() const { return m_lvalue == LValueType::LOCAL; }
+ bool isLValue() const { return m_lvalue != Declaration::LValueType::NONE; }
+ bool isLocalLValue() const { return m_lvalue == Declaration::LValueType::LOCAL; }
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
/// is implicitly convertible to @a _expectedType. If not, throw exception.
@@ -763,9 +813,9 @@ protected:
std::shared_ptr<Type const> m_type;
//! If this expression is an lvalue (i.e. something that can be assigned to) and is stored
//! locally or in storage. This is set during calls to @a checkTypeRequirements()
- LValueType m_lvalue;
+ Declaration::LValueType m_lvalue = Declaration::LValueType::NONE;
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
- bool m_lvalueRequested;
+ bool m_lvalueRequested = false;
};
/// Assignment, can also be a compound assignment.
diff --git a/ASTForward.h b/ASTForward.h
index b86f18cf..aa5cd49c 100644
--- a/ASTForward.h
+++ b/ASTForward.h
@@ -44,6 +44,7 @@ class ParameterList;
class FunctionDefinition;
class VariableDeclaration;
class ModifierDefinition;
+class ModifierInvocation;
class MagicVariableDeclaration;
class TypeName;
class ElementaryTypeName;
@@ -74,6 +75,8 @@ class Identifier;
class ElementaryTypeNameExpression;
class Literal;
+class VariableScope;
+
// Used as pointers to AST nodes, to be replaced by more clever pointers, e.g. pointers which do
// not do reference counting but point to a special memory area that is completely released
// explicitly.
diff --git a/ASTJsonConverter.cpp b/ASTJsonConverter.cpp
index 04ddee0a..d9332990 100644
--- a/ASTJsonConverter.cpp
+++ b/ASTJsonConverter.cpp
@@ -118,9 +118,10 @@ bool ASTJsonConverter::visit(FunctionDefinition const& _node)
bool ASTJsonConverter::visit(VariableDeclaration const& _node)
{
+ bool isLocalVariable = (_node.getLValueType() == VariableDeclaration::LValueType::LOCAL);
addJsonNode("VariableDeclaration",
{ make_pair("name", _node.getName()),
- make_pair("local", boost::lexical_cast<std::string>(_node.isLocalVariable()))},
+ make_pair("local", boost::lexical_cast<std::string>(isLocalVariable))},
true);
return true;
}
diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp
index 86cd028a..85bc8825 100644
--- a/ASTPrinter.cpp
+++ b/ASTPrinter.cpp
@@ -94,6 +94,13 @@ bool ASTPrinter::visit(ModifierDefinition const& _node)
return goDeeper();
}
+bool ASTPrinter::visit(ModifierInvocation const& _node)
+{
+ writeLine("ModifierInvocation \"" + _node.getName()->getName() + "\"");
+ printSourcePart(_node);
+ return goDeeper();
+}
+
bool ASTPrinter::visit(TypeName const& _node)
{
writeLine("TypeName");
@@ -341,6 +348,11 @@ void ASTPrinter::endVisit(ModifierDefinition const&)
m_indentation--;
}
+void ASTPrinter::endVisit(ModifierInvocation const&)
+{
+ m_indentation--;
+}
+
void ASTPrinter::endVisit(TypeName const&)
{
m_indentation--;
diff --git a/ASTPrinter.h b/ASTPrinter.h
index ef5c5164..7f267bdf 100644
--- a/ASTPrinter.h
+++ b/ASTPrinter.h
@@ -49,6 +49,7 @@ public:
bool visit(FunctionDefinition const& _node) override;
bool visit(VariableDeclaration const& _node) override;
bool visit(ModifierDefinition const& _node) override;
+ bool visit(ModifierInvocation const& _node) override;
bool visit(TypeName const& _node) override;
bool visit(ElementaryTypeName const& _node) override;
bool visit(UserDefinedTypeName const& _node) override;
@@ -85,6 +86,7 @@ public:
void endVisit(FunctionDefinition const&) override;
void endVisit(VariableDeclaration const&) override;
void endVisit(ModifierDefinition const&) override;
+ void endVisit(ModifierInvocation const&) override;
void endVisit(TypeName const&) override;
void endVisit(ElementaryTypeName const&) override;
void endVisit(UserDefinedTypeName const&) override;
diff --git a/ASTVisitor.h b/ASTVisitor.h
index 94bb9e0b..ecab00c3 100644
--- a/ASTVisitor.h
+++ b/ASTVisitor.h
@@ -50,6 +50,7 @@ public:
virtual bool visit(FunctionDefinition&) { return true; }
virtual bool visit(VariableDeclaration&) { return true; }
virtual bool visit(ModifierDefinition&) { return true; }
+ virtual bool visit(ModifierInvocation&) { return true; }
virtual bool visit(TypeName&) { return true; }
virtual bool visit(ElementaryTypeName&) { return true; }
virtual bool visit(UserDefinedTypeName&) { return true; }
@@ -88,6 +89,7 @@ public:
virtual void endVisit(FunctionDefinition&) { }
virtual void endVisit(VariableDeclaration&) { }
virtual void endVisit(ModifierDefinition&) { }
+ virtual void endVisit(ModifierInvocation&) { }
virtual void endVisit(TypeName&) { }
virtual void endVisit(ElementaryTypeName&) { }
virtual void endVisit(UserDefinedTypeName&) { }
@@ -130,6 +132,7 @@ public:
virtual bool visit(FunctionDefinition const&) { return true; }
virtual bool visit(VariableDeclaration const&) { return true; }
virtual bool visit(ModifierDefinition const&) { return true; }
+ virtual bool visit(ModifierInvocation const&) { return true; }
virtual bool visit(TypeName const&) { return true; }
virtual bool visit(ElementaryTypeName const&) { return true; }
virtual bool visit(UserDefinedTypeName const&) { return true; }
@@ -168,6 +171,7 @@ public:
virtual void endVisit(FunctionDefinition const&) { }
virtual void endVisit(VariableDeclaration const&) { }
virtual void endVisit(ModifierDefinition const&) { }
+ virtual void endVisit(ModifierInvocation const&) { }
virtual void endVisit(TypeName const&) { }
virtual void endVisit(ElementaryTypeName const&) { }
virtual void endVisit(UserDefinedTypeName const&) { }
diff --git a/AST_accept.h b/AST_accept.h
index 89786d6f..481b150b 100644
--- a/AST_accept.h
+++ b/AST_accept.h
@@ -144,6 +144,7 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@@ -156,6 +157,7 @@ void FunctionDefinition::accept(ASTConstVisitor& _visitor) const
m_parameters->accept(_visitor);
if (m_returnParameters)
m_returnParameters->accept(_visitor);
+ listAccept(m_functionModifiers, _visitor);
m_body->accept(_visitor);
}
_visitor.endVisit(*this);
@@ -197,6 +199,26 @@ void ModifierDefinition::accept(ASTConstVisitor& _visitor) const
_visitor.endVisit(*this);
}
+void ModifierInvocation::accept(ASTVisitor& _visitor)
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
+void ModifierInvocation::accept(ASTConstVisitor& _visitor) const
+{
+ if (_visitor.visit(*this))
+ {
+ m_modifierName->accept(_visitor);
+ listAccept(m_arguments, _visitor);
+ }
+ _visitor.endVisit(*this);
+}
+
void TypeName::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp
index ba5ca134..ea3b43c2 100644
--- a/NameAndTypeResolver.cpp
+++ b/NameAndTypeResolver.cpp
@@ -60,6 +60,11 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
ReferencesResolver resolver(*structDef, *this, &_contract, nullptr);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, &_contract, nullptr);
+ for (ASTPointer<ModifierDefinition> const& modifier: _contract.getFunctionModifiers())
+ {
+ m_currentScope = &m_scopes[modifier.get()];
+ ReferencesResolver resolver(*modifier, *this, &_contract, nullptr);
+ }
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
{
m_currentScope = &m_scopes[function.get()];
@@ -226,6 +231,19 @@ void DeclarationRegistrationHelper::endVisit(FunctionDefinition&)
closeCurrentScope();
}
+bool DeclarationRegistrationHelper::visit(ModifierDefinition& _modifier)
+{
+ registerDeclaration(_modifier, true);
+ m_currentFunction = &_modifier;
+ return true;
+}
+
+void DeclarationRegistrationHelper::endVisit(ModifierDefinition&)
+{
+ m_currentFunction = nullptr;
+ closeCurrentScope();
+}
+
void DeclarationRegistrationHelper::endVisit(VariableDefinition& _variableDefinition)
{
// Register the local variables with the function
diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h
index f97c7ae5..ba327a59 100644
--- a/NameAndTypeResolver.h
+++ b/NameAndTypeResolver.h
@@ -100,6 +100,8 @@ private:
void endVisit(StructDefinition& _struct);
bool visit(FunctionDefinition& _function);
void endVisit(FunctionDefinition& _function);
+ bool visit(ModifierDefinition& _modifier);
+ void endVisit(ModifierDefinition& _modifier);
void endVisit(VariableDefinition& _variableDefinition);
bool visit(VariableDeclaration& _declaration);
@@ -109,7 +111,7 @@ private:
std::map<ASTNode const*, DeclarationContainer>& m_scopes;
Declaration const* m_currentScope;
- FunctionDefinition* m_currentFunction;
+ VariableScope* m_currentFunction;
};
/**
diff --git a/Parser.cpp b/Parser.cpp
index a72a5dba..baddbad8 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -192,10 +192,18 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
ASTPointer<ASTString> name(expectIdentifierToken());
ASTPointer<ParameterList> parameters(parseParameterList());
bool isDeclaredConst = false;
- if (m_scanner->getCurrentToken() == Token::CONST)
+ vector<ASTPointer<ModifierInvocation>> modifiers;
+ while (true)
{
- isDeclaredConst = true;
- m_scanner->next();
+ if (m_scanner->getCurrentToken() == Token::CONST)
+ {
+ isDeclaredConst = true;
+ m_scanner->next();
+ }
+ else if (m_scanner->getCurrentToken() == Token::IDENTIFIER)
+ modifiers.push_back(parseModifierInvocation());
+ else
+ break;
}
ASTPointer<ParameterList> returnParameters;
if (m_scanner->getCurrentToken() == Token::RETURNS)
@@ -215,8 +223,8 @@ ASTPointer<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic, A
nodeFactory.setEndPositionFromNode(block);
bool const c_isConstructor = (_contractName && *name == *_contractName);
return nodeFactory.createNode<FunctionDefinition>(name, _isPublic, c_isConstructor, docstring,
- parameters,
- isDeclaredConst, returnParameters, block);
+ parameters, isDeclaredConst, modifiers,
+ returnParameters, block);
}
ASTPointer<StructDefinition> Parser::parseStructDefinition()
@@ -272,6 +280,23 @@ ASTPointer<ModifierDefinition> Parser::parseModifierDefinition()
return nodeFactory.createNode<ModifierDefinition>(name, docstring, parameters, block);
}
+ASTPointer<ModifierInvocation> Parser::parseModifierInvocation()
+{
+ ASTNodeFactory nodeFactory(*this);
+ ASTPointer<Identifier> name = ASTNodeFactory(*this).createNode<Identifier>(expectIdentifierToken());
+ vector<ASTPointer<Expression>> arguments;
+ if (m_scanner->getCurrentToken() == Token::LPAREN)
+ {
+ m_scanner->next();
+ arguments = parseFunctionCallArguments();
+ nodeFactory.markEndPosition();
+ expectToken(Token::RPAREN);
+ }
+ else
+ nodeFactory.setEndPositionFromNode(name);
+ return nodeFactory.createNode<ModifierInvocation>(name, arguments);
+}
+
ASTPointer<TypeName> Parser::parseTypeName(bool _allowVar)
{
ASTPointer<TypeName> type;
diff --git a/Parser.h b/Parser.h
index cccf3b3b..f85c3186 100644
--- a/Parser.h
+++ b/Parser.h
@@ -54,6 +54,7 @@ private:
ASTPointer<StructDefinition> parseStructDefinition();
ASTPointer<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ASTPointer<ModifierDefinition> parseModifierDefinition();
+ ASTPointer<ModifierInvocation> parseModifierInvocation();
ASTPointer<TypeName> parseTypeName(bool _allowVar);
ASTPointer<Mapping> parseMapping();
ASTPointer<ParameterList> parseParameterList(bool _allowEmpty = true);
diff --git a/Types.cpp b/Types.cpp
index 2446c513..c75cecfa 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -724,6 +724,38 @@ MemberList const& TypeType::getMembers() const
return *m_members;
}
+ModifierType::ModifierType(const ModifierDefinition& _modifier)
+{
+ TypePointers params;
+ params.reserve(_modifier.getParameters().size());
+ for (ASTPointer<VariableDeclaration> const& var: _modifier.getParameters())
+ params.push_back(var->getType());
+ swap(params, m_parameterTypes);
+}
+
+bool ModifierType::operator==(Type const& _other) const
+{
+ if (_other.getCategory() != getCategory())
+ return false;
+ ModifierType const& other = dynamic_cast<ModifierType const&>(_other);
+
+ if (m_parameterTypes.size() != other.m_parameterTypes.size())
+ return false;
+ auto typeCompare = [](TypePointer const& _a, TypePointer const& _b) -> bool { return *_a == *_b; };
+
+ if (!equal(m_parameterTypes.cbegin(), m_parameterTypes.cend(),
+ other.m_parameterTypes.cbegin(), typeCompare))
+ return false;
+ return true;
+}
+
+string ModifierType::toString() const
+{
+ string name = "modifier (";
+ for (auto it = m_parameterTypes.begin(); it != m_parameterTypes.end(); ++it)
+ name += (*it)->toString() + (it + 1 == m_parameterTypes.end() ? "" : ",");
+ return name + ")";
+}
MagicType::MagicType(MagicType::Kind _kind):
m_kind(_kind)
diff --git a/Types.h b/Types.h
index e6c99fe3..935c39eb 100644
--- a/Types.h
+++ b/Types.h
@@ -75,7 +75,7 @@ class Type: private boost::noncopyable, public std::enable_shared_from_this<Type
public:
enum class Category
{
- INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MAGIC
+ INTEGER, INTEGER_CONSTANT, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE, MODIFIER, MAGIC
};
///@{
@@ -464,6 +464,27 @@ private:
/**
+ * The type of a function modifier. Not used for anything for now.
+ */
+class ModifierType: public Type
+{
+public:
+ virtual Category getCategory() const override { return Category::MODIFIER; }
+ explicit ModifierType(ModifierDefinition const& _modifier);
+
+ virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); }
+ virtual bool canBeStored() const override { return false; }
+ virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); }
+ virtual bool canLiveOutsideStorage() const override { return false; }
+ virtual bool operator==(Type const& _other) const override;
+ virtual std::string toString() const override;
+
+private:
+ TypePointers m_parameterTypes;
+};
+
+
+/**
* Special type for magic variables (block, msg, tx), similar to a struct but without any reference
* (it always references a global singleton by name).
*/
diff --git a/grammar.txt b/grammar.txt
index 11d99854..b97dac5d 100644
--- a/grammar.txt
+++ b/grammar.txt
@@ -1,14 +1,14 @@
ContractDefinition = 'contract' Identifier
( 'is' InheritanceSpecifier (',' InheritanceSpecifier )* )?
'{' ContractPart* '}'
-ContractPart = VariableDeclaration ';' | StructDefinition |
+ContractPart = VariableDeclaration ';' | StructDefinition | ModifierDefinition |
FunctionDefinition | 'public:' | 'private:'
InheritanceSpecifier = Identifier ( '(' Expression ( ',' Expression )* ')' )?
StructDefinition = 'struct' Identifier '{'
( VariableDeclaration (';' VariableDeclaration)* )? '}
-
-FunctionDefinition = 'function' Identifier ParameterList 'const'?
+ModifierDefinition = 'modifier' Identifier ParameterList? Block
+FunctionDefinition = 'function' Identifier ParameterList ( Identifier | 'constant' )*
( 'returns' ParameterList )? Block
ParameterList = '(' ( VariableDeclaration (',' VariableDeclaration)* )? ')'
// semantic restriction: mappings and structs (recursively) containing mappings