diff options
author | Christian <c@ethdev.com> | 2015-01-22 08:02:38 +0800 |
---|---|---|
committer | Christian <c@ethdev.com> | 2015-01-26 17:23:39 +0800 |
commit | 941c77c8fadd6195809cd2d572feb64478c4fb20 (patch) | |
tree | a4de3710aff5f3f2d9423565fd0e9ecc8708b5bf | |
parent | 19793dab093ae36b5f2b4d1cabfbf54bed3125b1 (diff) | |
download | dexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.tar.gz dexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.tar.zst dexon-solidity-941c77c8fadd6195809cd2d572feb64478c4fb20.zip |
Type resolution for function modifiers.
-rw-r--r-- | AST.cpp | 123 | ||||
-rwxr-xr-x | AST.h | 84 | ||||
-rw-r--r-- | ASTForward.h | 3 | ||||
-rw-r--r-- | ASTJsonConverter.cpp | 3 | ||||
-rw-r--r-- | ASTPrinter.cpp | 12 | ||||
-rw-r--r-- | ASTPrinter.h | 2 | ||||
-rw-r--r-- | ASTVisitor.h | 4 | ||||
-rw-r--r-- | AST_accept.h | 22 | ||||
-rw-r--r-- | NameAndTypeResolver.cpp | 18 | ||||
-rw-r--r-- | NameAndTypeResolver.h | 4 | ||||
-rw-r--r-- | Parser.cpp | 35 | ||||
-rw-r--r-- | Parser.h | 1 | ||||
-rw-r--r-- | Types.cpp | 32 | ||||
-rw-r--r-- | Types.h | 23 | ||||
-rw-r--r-- | grammar.txt | 6 |
15 files changed, 299 insertions, 73 deletions
@@ -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() @@ -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; }; /** @@ -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; @@ -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); @@ -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) @@ -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 |