diff options
-rw-r--r-- | AST.cpp | 244 | ||||
-rw-r--r-- | AST.h | 123 | ||||
-rw-r--r-- | ASTForward.h | 1 | ||||
-rw-r--r-- | ASTPrinter.cpp | 2 | ||||
-rw-r--r-- | NameAndTypeResolver.cpp | 103 | ||||
-rw-r--r-- | NameAndTypeResolver.h | 15 | ||||
-rw-r--r-- | Parser.cpp | 37 | ||||
-rw-r--r-- | Parser.h | 6 | ||||
-rw-r--r-- | Scope.cpp | 48 | ||||
-rw-r--r-- | Scope.h | 24 | ||||
-rw-r--r-- | Token.h | 13 | ||||
-rw-r--r-- | Types.cpp | 152 | ||||
-rw-r--r-- | Types.h | 171 |
13 files changed, 816 insertions, 123 deletions
@@ -20,6 +20,8 @@ * Solidity abstract syntax tree. */ +#include <algorithm> + #include <libsolidity/AST.h> #include <libsolidity/ASTVisitor.h> @@ -66,8 +68,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { - if (m_type) - m_type->accept(_visitor); + if (m_typeName) + m_typeName->accept(_visitor); } _visitor.endVisit(*this); } @@ -170,12 +172,6 @@ void VariableDefinition::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -void Expression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void Assignment::accept(ASTVisitor& _visitor) { if (_visitor.visit(*this)) { @@ -228,12 +224,6 @@ void IndexAccess::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } -void PrimaryExpression::accept(ASTVisitor& _visitor) -{ - _visitor.visit(*this); - _visitor.endVisit(*this); -} - void Identifier::accept(ASTVisitor& _visitor) { _visitor.visit(*this); @@ -252,4 +242,230 @@ void Literal::accept(ASTVisitor& _visitor) _visitor.endVisit(*this); } +void Statement::expectType(Expression& _expression, const Type& _expectedType) +{ + if (!_expression.checkTypeRequirements()->isImplicitlyConvertibleTo(_expectedType)) + throw std::exception(); // @todo +} + +ptr<Type> Block::checkTypeRequirements() +{ + for (ptr<Statement> const& statement : m_statements) + statement->checkTypeRequirements(); + return ptr<Type>(); +} + +ptr<Type> IfStatement::checkTypeRequirements() +{ + expectType(*m_condition, BoolType()); + m_trueBody->checkTypeRequirements(); + if (m_falseBody) m_falseBody->checkTypeRequirements(); + return ptr<Type>(); +} + +ptr<Type> WhileStatement::checkTypeRequirements() +{ + expectType(*m_condition, BoolType()); + m_body->checkTypeRequirements(); + return ptr<Type>(); +} + +ptr<Type> Continue::checkTypeRequirements() +{ + return ptr<Type>(); +} + +ptr<Type> Break::checkTypeRequirements() +{ + return ptr<Type>(); +} + +ptr<Type> Return::checkTypeRequirements() +{ + BOOST_ASSERT(m_returnParameters != nullptr); + if (m_returnParameters->getParameters().size() != 1) + throw std::exception(); // @todo + // this could later be changed such that the paramaters type is an anonymous struct type, + // but for now, we only allow one return parameter + + expectType(*m_expression, *m_returnParameters->getParameters().front()->getType()); + return ptr<Type>(); +} + +ptr<Type> VariableDefinition::checkTypeRequirements() +{ + // Variables can be declared without type (with "var"), in which case the first assignment + // setsthe type. + // Note that assignments before the first declaration are legal because of the special scoping + // rules inherited from JavaScript. + if (m_value) { + if (m_variable->getType()) { + expectType(*m_value, *m_variable->getType()); + } else { + // no type declared and no previous assignment, infer the type + m_variable->setType(m_value->checkTypeRequirements()); + } + } + return ptr<Type>(); +} + +ptr<Type> Assignment::checkTypeRequirements() +{ + //@todo lefthandside actually has to be assignable + // add a feature to the type system to check that + expectType(*m_rightHandSide, *m_leftHandSide->checkTypeRequirements()); + m_type = m_leftHandSide->getType(); + if (m_assigmentOperator != Token::ASSIGN) { + // complex assignment + if (!m_type->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_assigmentOperator))) + throw std::exception(); + } + return m_type; +} + +ptr<Type> UnaryOperation::checkTypeRequirements() +{ + // INC, DEC, NOT, BIT_NOT, DELETE + m_type = m_subExpression->checkTypeRequirements(); + if (m_type->acceptsUnaryOperator(m_operator)) + throw std::exception(); + return m_type; +} + +ptr<Type> BinaryOperation::checkTypeRequirements() +{ + m_right->checkTypeRequirements(); + m_left->checkTypeRequirements(); + + if (m_right->getType()->isImplicitlyConvertibleTo(*m_left->getType())) + m_commonType = m_left->getType(); + else if (m_left->getType()->isImplicitlyConvertibleTo(*m_right->getType())) + m_commonType = m_right->getType(); + else + throw std::exception(); + + if (Token::IsCompareOp(m_operator)) { + m_type = std::make_shared<BoolType>(); + } else { + BOOST_ASSERT(Token::IsBinaryOp(m_operator)); + m_type = m_commonType; + if (!m_commonType->acceptsBinaryOperator(Token::AssignmentToBinaryOp(m_operator))) + throw std::exception(); + } + return m_type; +} + +ptr<Type> FunctionCall::checkTypeRequirements() +{ + m_expression->checkTypeRequirements(); + for (ptr<Expression> const& argument : m_arguments) + argument->checkTypeRequirements(); + + ptr<Type> expressionType = m_expression->getType(); + Type::Category const category = expressionType->getCategory(); + if (category == Type::Category::TYPE) { + TypeType* type = dynamic_cast<TypeType*>(expressionType.get()); + BOOST_ASSERT(type != nullptr); + //@todo for structs, we have to check the number of arguments to be equal to the + // number of non-mapping members + if (m_arguments.size() != 1) + throw std::exception(); + if (!m_arguments.front()->getType()->isExplicitlyConvertibleTo(*type->getActualType())) + throw std::exception(); + m_type = type->getActualType(); + } else if (category == Type::Category::FUNCTION) { + //@todo would be nice to create a struct type from the arguments + // and then ask if that is implicitly convertible to the struct represented by the + // function parameters + FunctionType* function = dynamic_cast<FunctionType*>(expressionType.get()); + BOOST_ASSERT(function != nullptr); + FunctionDefinition const& fun = function->getFunction(); + vecptr<VariableDeclaration> const& parameters = fun.getParameters(); + if (parameters.size() != m_arguments.size()) + throw std::exception(); + for (size_t i = 0; i < m_arguments.size(); ++i) { + if (!m_arguments[i]->getType()->isImplicitlyConvertibleTo(*parameters[i]->getType())) + throw std::exception(); + } + + // @todo actually the return type should be an anonymous struct, + // but we change it to the type of the first return value until we have structs + if (fun.getReturnParameterList()->getParameters().empty()) + m_type = std::make_shared<VoidType>(); + else + m_type = fun.getReturnParameterList()->getParameters().front()->getType(); + } else { + throw std::exception(); // type does not support invocation + } + return m_type; +} + +ptr<Type> MemberAccess::checkTypeRequirements() +{ + BOOST_ASSERT(false); // not yet implemented + // m_type = ; + return m_type; +} + +ptr<Type> IndexAccess::checkTypeRequirements() +{ + BOOST_ASSERT(false); // not yet implemented + // m_type = ; + return m_type; +} + +ptr<Type> Identifier::checkTypeRequirements() +{ + BOOST_ASSERT(m_referencedDeclaration != nullptr); + //@todo these dynamic casts here are not really nice... + // is i useful to have an AST visitor here? + // or can this already be done in NameAndTypeResolver? + // the only problem we get there is that in + // var x; + // x = 2; + // var y = x; + // the type of x is not yet determined. + VariableDeclaration* variable = dynamic_cast<VariableDeclaration*>(m_referencedDeclaration); + if (variable != nullptr) { + if (variable->getType().get() == nullptr) + throw std::exception(); // variable used before type could be determined + m_type = variable->getType(); + return m_type; + } + //@todo can we unify these with TypeName::toType()? + StructDefinition* structDef = dynamic_cast<StructDefinition*>(m_referencedDeclaration); + if (structDef != nullptr) { + // note that we do not have a struct type here + m_type = std::make_shared<TypeType>(std::make_shared<StructType>(*structDef)); + return m_type; + } + FunctionDefinition* functionDef = dynamic_cast<FunctionDefinition*>(m_referencedDeclaration); + if (functionDef != nullptr) { + // 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 = std::make_shared<FunctionType>(*functionDef); + return m_type; + } + ContractDefinition* contractDef = dynamic_cast<ContractDefinition*>(m_referencedDeclaration); + if (contractDef != nullptr) { + m_type = std::make_shared<TypeType>(std::make_shared<ContractType>(*contractDef)); + return m_type; + } + throw std::exception(); // declaration reference of unknown/forbidden type + return m_type; +} + +ptr<Type> ElementaryTypeNameExpression::checkTypeRequirements() +{ + m_type = std::make_shared<TypeType>(Type::fromElementaryTypeName(m_typeToken)); + return m_type; +} + +ptr<Type> Literal::checkTypeRequirements() +{ + m_type = Type::forLiteral(*this); + return m_type; +} + } } @@ -22,6 +22,8 @@ #pragma once +#include <boost/noncopyable.hpp> + #include <string> #include <vector> #include <memory> @@ -29,13 +31,14 @@ #include <libsolidity/ASTForward.h> #include <libsolidity/BaseTypes.h> #include <libsolidity/Token.h> +#include <libsolidity/Types.h> namespace dev { namespace solidity { class ASTVisitor; -class ASTNode +class ASTNode : private boost::noncopyable { public: explicit ASTNode(Location const& _location) @@ -55,7 +58,18 @@ private: Location m_location; }; -class ContractDefinition : public ASTNode +class Declaration : public ASTNode +{ +public: + Declaration(Location const& _location, ptr<ASTString> const& _name) + : ASTNode(_location), m_name(_name) {} + + const ASTString& getName() const { return *m_name; } +private: + ptr<ASTString> m_name; +}; + +class ContractDefinition : public Declaration { public: ContractDefinition(Location const& _location, @@ -63,7 +77,7 @@ public: vecptr<StructDefinition> const& _definedStructs, vecptr<VariableDeclaration> const& _stateVariables, vecptr<FunctionDefinition> const& _definedFunctions) - : ASTNode(_location), m_name(_name), + : Declaration(_location, _name), m_definedStructs(_definedStructs), m_stateVariables(_stateVariables), m_definedFunctions(_definedFunctions) @@ -71,30 +85,26 @@ public: virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } vecptr<StructDefinition> const& getDefinedStructs() { return m_definedStructs; } vecptr<VariableDeclaration> const& getStateVariables() { return m_stateVariables; } vecptr<FunctionDefinition> const& getDefinedFunctions() { return m_definedFunctions; } private: - ptr<ASTString> m_name; vecptr<StructDefinition> m_definedStructs; vecptr<VariableDeclaration> m_stateVariables; vecptr<FunctionDefinition> m_definedFunctions; }; -class StructDefinition : public ASTNode +class StructDefinition : public Declaration { public: StructDefinition(Location const& _location, ptr<ASTString> const& _name, vecptr<VariableDeclaration> const& _members) - : ASTNode(_location), m_name(_name), m_members(_members) + : Declaration(_location, _name), m_members(_members) {} virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } private: - ptr<ASTString> m_name; vecptr<VariableDeclaration> m_members; }; @@ -114,7 +124,7 @@ private: vecptr<VariableDeclaration> m_parameters; }; -class FunctionDefinition : public ASTNode +class FunctionDefinition : public Declaration { public: FunctionDefinition(Location const& _location, ptr<ASTString> const& _name, bool _isPublic, @@ -122,43 +132,47 @@ public: bool _isDeclaredConst, ptr<ParameterList> const& _returnParameters, ptr<Block> const& _body) - : ASTNode(_location), m_name(_name), m_isPublic(_isPublic), m_parameters(_parameters), + : Declaration(_location, _name), m_isPublic(_isPublic), m_parameters(_parameters), m_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; - const ASTString& getName() const { return *m_name; } bool isPublic() const { return m_isPublic; } bool isDeclaredConst() const { return m_isDeclaredConst; } - vecptr<VariableDeclaration> const& getParameters() { return m_parameters->getParameters(); } - bool hasReturnParameters() const { return m_returnParameters.get() != nullptr; } - vecptr<VariableDeclaration> const& getReturnParameters() { return m_returnParameters->getParameters(); } + vecptr<VariableDeclaration> const& getParameters() const { return m_parameters->getParameters(); } + ParameterList& getParameterList() { return *m_parameters; } + ptr<ParameterList> const& getReturnParameterList() const { return m_returnParameters; } Block& getBody() { return *m_body; } private: - ptr<ASTString> m_name; bool m_isPublic; ptr<ParameterList> m_parameters; bool m_isDeclaredConst; - ptr<ParameterList> m_returnParameters; //< either "null"pointer or pointer to non-empty parameter list + ptr<ParameterList> m_returnParameters; ptr<Block> m_body; }; -class VariableDeclaration : public ASTNode +class VariableDeclaration : public Declaration { public: VariableDeclaration(Location const& _location, ptr<TypeName> const& _type, ptr<ASTString> const& _name) - : ASTNode(_location), m_type(_type), m_name(_name) + : Declaration(_location, _name), m_typeName(_type) {} virtual void accept(ASTVisitor& _visitor) override; - TypeName* getTypeName() const { return m_type.get(); } - const ASTString& getName() const { return *m_name; } + bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; } + TypeName* getTypeName() const { return m_typeName.get(); } + + //! 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. + ptr<Type> const& getType() const { return m_type; } + void setType(ptr<Type> const& _type) { m_type = _type; } private: - ptr<TypeName> m_type; ///< can be empty ("var") - ptr<ASTString> m_name; + ptr<TypeName> m_typeName; ///< can be empty ("var") + + ptr<Type> m_type; }; /// types @@ -169,6 +183,8 @@ class TypeName : public ASTNode public: explicit TypeName(Location const& _location) : ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; + + virtual ptr<Type> toType() = 0; }; /// any pre-defined type that is not a mapping @@ -179,6 +195,7 @@ public: : TypeName(_location), m_type(_type) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); } Token::Value getType() const { return m_type; } private: @@ -192,10 +209,15 @@ public: : TypeName(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); } const ASTString& getName() const { return *m_name; } + void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; } + StructDefinition const* getReferencedStruct() const { return m_referencedStruct; } private: ptr<ASTString> m_name; + + StructDefinition* m_referencedStruct; }; class Mapping : public TypeName @@ -206,6 +228,7 @@ public: : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> toType() override { return Type::fromMapping(*this); } private: ptr<ElementaryTypeName> m_keyType; ptr<TypeName> m_valueType; @@ -221,6 +244,15 @@ class Statement : public ASTNode public: explicit Statement(Location const& _location) : ASTNode(_location) {} virtual void accept(ASTVisitor& _visitor) override; + + //! Check all type requirements, throws exception if some requirement is not met. + //! For expressions, this also returns the inferred type of the expression. For other + //! statements, returns the empty pointer. + virtual ptr<Type> checkTypeRequirements() = 0; +protected: + //! Check that the inferred type for _expression is _expectedType or at least implicitly + //! convertible to _expectedType. If not, throw exception. + void expectType(Expression& _expression, Type const& _expectedType); }; class Block : public Statement @@ -230,6 +262,8 @@ public: : Statement(_location), m_statements(_statements) {} virtual void accept(ASTVisitor& _visitor) override; + + virtual ptr<Type> checkTypeRequirements() override; private: vecptr<Statement> m_statements; }; @@ -243,6 +277,7 @@ public: m_trueBody(_trueBody), m_falseBody(_falseBody) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; private: ptr<Expression> m_condition; ptr<Statement> m_trueBody; @@ -264,6 +299,7 @@ public: : BreakableStatement(_location), m_condition(_condition), m_body(_body) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; private: ptr<Expression> m_condition; ptr<Statement> m_body; @@ -274,6 +310,7 @@ class Continue : public Statement public: Continue(Location const& _location) : Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; }; class Break : public Statement @@ -281,6 +318,7 @@ class Break : public Statement public: Break(Location const& _location) : Statement(_location) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; }; class Return : public Statement @@ -290,8 +328,13 @@ public: : Statement(_location), m_expression(_expression) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; + + void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } private: ptr<Expression> m_expression; //< value to return, optional + + ParameterList* m_returnParameters; //< extracted from the function declaration }; class VariableDefinition : public Statement @@ -302,6 +345,8 @@ public: : Statement(_location), m_variable(_variable), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; + private: ptr<VariableDeclaration> m_variable; ptr<Expression> m_value; ///< can be missing @@ -311,7 +356,9 @@ class Expression : public Statement { public: Expression(Location const& _location) : Statement(_location) {} - virtual void accept(ASTVisitor& _visitor) override; + ptr<Type> const& getType() { return m_type; } +protected: + ptr<Type> m_type; }; /// @} @@ -328,6 +375,7 @@ public: m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; Token::Value getAssignmentOperator() const { return m_assigmentOperator; } private: @@ -345,6 +393,7 @@ public: m_subExpression(_subExpression), m_isPrefix(_isPrefix) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } bool isPrefixOperation() const { return m_isPrefix; } @@ -362,12 +411,15 @@ public: : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; Token::Value getOperator() const { return m_operator; } private: ptr<Expression> m_left; Token::Value m_operator; ptr<Expression> m_right; + + ptr<Type> m_commonType; }; /// Can be ordinary function call, type cast or struct construction. @@ -379,6 +431,7 @@ public: : Expression(_location), m_expression(_expression), m_arguments(_arguments) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; private: ptr<Expression> m_expression; vecptr<Expression> m_arguments; @@ -393,6 +446,7 @@ public: {} virtual void accept(ASTVisitor& _visitor) override; const ASTString& getMemberName() const { return *m_memberName; } + virtual ptr<Type> checkTypeRequirements() override; private: ptr<Expression> m_expression; ptr<ASTString> m_memberName; @@ -406,6 +460,7 @@ public: : Expression(_location), m_base(_base), m_index(_index) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; private: ptr<Expression> m_base; ptr<Expression> m_index; @@ -415,7 +470,6 @@ class PrimaryExpression : public Expression { public: PrimaryExpression(Location const& _location) : Expression(_location) {} - virtual void accept(ASTVisitor& _visitor) override; }; class Identifier : public PrimaryExpression @@ -424,27 +478,29 @@ public: Identifier(Location const& _location, ptr<ASTString> const& _name) : PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; ASTString const& getName() const { return *m_name; } - void setReferencedObject(ASTNode& _referencedObject) { m_referencedObject = &_referencedObject; } - ASTNode* getReferencedVariable() { return m_referencedObject; } + void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + Declaration* getReferencedDeclaration() { return m_referencedDeclaration; } private: ptr<ASTString> m_name; - //! Node the name refers to. Has to be a declaration of some sort. - ASTNode* m_referencedObject; + //! Declaration the name refers to. + Declaration* m_referencedDeclaration; }; class ElementaryTypeNameExpression : public PrimaryExpression { public: - ElementaryTypeNameExpression(Location const& _location, Token::Value _type) - : PrimaryExpression(_location), m_type(_type) {} + ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken) + : PrimaryExpression(_location), m_typeToken(_typeToken) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; - Token::Value getType() const { return m_type; } + Token::Value getTypeToken() const { return m_typeToken; } private: - Token::Value m_type; + Token::Value m_typeToken; }; class Literal : public PrimaryExpression @@ -454,6 +510,7 @@ public: : PrimaryExpression(_location), m_token(_token), m_value(_value) {} virtual void accept(ASTVisitor& _visitor) override; + virtual ptr<Type> checkTypeRequirements() override; Token::Value getToken() const { return m_token; } ASTString const& getValue() const { return *m_value; } diff --git a/ASTForward.h b/ASTForward.h index e3876795..4963776b 100644 --- a/ASTForward.h +++ b/ASTForward.h @@ -32,6 +32,7 @@ namespace dev { namespace solidity { class ASTNode; +class Declaration; class ContractDefinition; class StructDefinition; class ParameterList; diff --git a/ASTPrinter.cpp b/ASTPrinter.cpp index 7e57732b..bbaa2e0a 100644 --- a/ASTPrinter.cpp +++ b/ASTPrinter.cpp @@ -233,7 +233,7 @@ bool ASTPrinter::visit(Identifier& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) { - writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType())); + writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getTypeToken())); printSourcePart(_node); return goDeeper(); } diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp index c3932ca9..33b550eb 100644 --- a/NameAndTypeResolver.cpp +++ b/NameAndTypeResolver.cpp @@ -31,10 +31,10 @@ namespace solidity { class NameAndTypeResolver::ScopeHelper { public: - ScopeHelper(NameAndTypeResolver& _resolver, ASTString const& _name, ASTNode& _declaration) + ScopeHelper(NameAndTypeResolver& _resolver, Declaration& _declaration) : m_resolver(_resolver) { - m_resolver.registerName(_name, _declaration); + m_resolver.registerDeclaration(_declaration); m_resolver.enterNewSubScope(_declaration); } ~ScopeHelper() @@ -60,16 +60,15 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) void NameAndTypeResolver::handleContract(ContractDefinition& _contract) { - ScopeHelper scopeHelper(*this, _contract.getName(), _contract); + ScopeHelper scopeHelper(*this, _contract); + + // @todo structs (definition and usage) for (ptr<VariableDeclaration> const& variable : _contract.getStateVariables()) - registerName(variable->getName(), *variable); - // @todo structs + registerVariableDeclarationAndResolveType(*variable); for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) handleFunction(*function); - - // @todo resolve names used in mappings } void NameAndTypeResolver::reset() @@ -81,30 +80,20 @@ void NameAndTypeResolver::reset() void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) { - ScopeHelper scopeHelper(*this, _function.getName(), _function); - - // @todo resolve names used in mappings - for (ptr<VariableDeclaration> const& variable : _function.getParameters()) - registerName(variable->getName(), *variable); - if (_function.hasReturnParameters()) - for (ptr<VariableDeclaration> const& variable : _function.getReturnParameters()) - registerName(variable->getName(), *variable); - handleFunctionBody(_function.getBody()); -} + ScopeHelper scopeHelper(*this, _function); -void NameAndTypeResolver::handleFunctionBody(Block& _functionBody) -{ - registerVariablesInFunction(_functionBody); - resolveReferencesInFunction(_functionBody); + registerVariablesInFunction(_function); + resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody()); + _function.getBody().checkTypeRequirements(); } -void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) +void NameAndTypeResolver::registerVariablesInFunction(FunctionDefinition& _function) { class VariableDeclarationFinder : public ASTVisitor { public: VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} virtual bool visit(VariableDeclaration& _variable) override { - m_resolver.registerName(_variable.getName(), _variable); + m_resolver.registerVariableDeclarationAndResolveType(_variable); return false; } private: @@ -112,37 +101,85 @@ void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) }; VariableDeclarationFinder declarationFinder(*this); - _functionBody.accept(declarationFinder); + _function.accept(declarationFinder); } -void NameAndTypeResolver::resolveReferencesInFunction(Block& _functionBody) +void NameAndTypeResolver::resolveReferencesInFunction(ParameterList& _returnParameters, + Block& _functionBody) { class ReferencesResolver : public ASTVisitor { public: - ReferencesResolver(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} + ReferencesResolver(NameAndTypeResolver& _resolver, + ParameterList& _returnParameters) + : m_resolver(_resolver), m_returnParameters(_returnParameters) {} virtual bool visit(Identifier& _identifier) override { - ASTNode* node = m_resolver.getNameFromCurrentScope(_identifier.getName()); - if (node == nullptr) + Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName()); + if (declaration == nullptr) throw std::exception(); // @todo - _identifier.setReferencedObject(*node); + _identifier.setReferencedDeclaration(*declaration); return false; } + virtual bool visit(Return& _return) override { + _return.setFunctionReturnParameters(m_returnParameters); + return true; + } private: NameAndTypeResolver& m_resolver; + ParameterList& m_returnParameters; }; - ReferencesResolver referencesResolver(*this); + ReferencesResolver referencesResolver(*this, _returnParameters); _functionBody.accept(referencesResolver); } +void NameAndTypeResolver::registerVariableDeclarationAndResolveType(VariableDeclaration& _variable) +{ + registerDeclaration(_variable); + TypeName* typeName = _variable.getTypeName(); + if (typeName == nullptr) // unknown type, to be resolved by first assignment + return; + + // walk the AST to resolve user defined type references + // (walking is necessory because of mappings) + // @todo this could probably also be done at an earlier stage where we anyway + // walk the AST + + class UserDefinedTypeNameResolver : public ASTVisitor { + public: + UserDefinedTypeNameResolver(NameAndTypeResolver& _resolver) + : m_resolver(_resolver) {} + virtual bool visit(UserDefinedTypeName& _typeName) override { + Declaration* declaration = m_resolver.getNameFromCurrentScope(_typeName.getName()); + if (declaration == nullptr) + throw std::exception(); // @todo + StructDefinition* referencedStruct = dynamic_cast<StructDefinition*>(declaration); + if (referencedStruct == nullptr) + throw std::exception(); // @todo we only allow structs as user defined types (later also contracts) + _typeName.setReferencedStruct(*referencedStruct); + return false; + } + virtual bool visit(Mapping&) override { + // @todo + return true; + } + private: + NameAndTypeResolver& m_resolver; + }; + + UserDefinedTypeNameResolver resolver(*this); + _variable.accept(resolver); + + _variable.setType(typeName->toType()); +} + -void NameAndTypeResolver::registerName(ASTString const& _name, ASTNode& _declaration) +void NameAndTypeResolver::registerDeclaration(Declaration& _declaration) { - if (!m_currentScope->registerName(_name, _declaration)) + if (!m_currentScope->registerDeclaration(_declaration)) throw std::exception(); // @todo } -ASTNode* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) +Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name, bool _recursive) { return m_currentScope->resolveName(_name, _recursive); } diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h index 42efd57a..036c3fba 100644 --- a/NameAndTypeResolver.h +++ b/NameAndTypeResolver.h @@ -24,13 +24,15 @@ #include <map> +#include <boost/noncopyable.hpp> + #include <libsolidity/Scope.h> #include <libsolidity/ASTVisitor.h> namespace dev { namespace solidity { -class NameAndTypeResolver +class NameAndTypeResolver : private boost::noncopyable { public: NameAndTypeResolver(); @@ -43,12 +45,13 @@ private: void handleContract(ContractDefinition& _contract); void handleFunction(FunctionDefinition& _function); - void handleFunctionBody(Block& _functionBody); - void registerVariablesInFunction(Block& _functionBody); - void resolveReferencesInFunction(Block& _functionBody); + void registerVariablesInFunction(FunctionDefinition& _function); + void resolveReferencesInFunction(ParameterList& _returnParameters, + Block& _functionBody); - void registerName(ASTString const& _name, ASTNode& _declaration); - ASTNode* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); + void registerVariableDeclarationAndResolveType(VariableDeclaration& _variable); + void registerDeclaration(Declaration& _declaration); + Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); void enterNewSubScope(ASTNode& _node); void closeCurrentScope(); @@ -47,6 +47,8 @@ public: void markEndPosition() { m_location.end = m_parser.getEndPosition(); } + void setLocationEmpty() { m_location.end = m_location.start; } + /// Set the end position to the one of the given node. void setEndPositionFromNode(const ptr<ASTNode>& _node) { @@ -104,7 +106,8 @@ ptr<ContractDefinition> Parser::parseContractDefinition() structs.push_back(parseStructDefinition()); } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || Token::IsElementaryTypeName(currentToken)) { - stateVariables.push_back(parseVariableDeclaration()); + bool const allowVar = false; + stateVariables.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); } else { throwExpectationError("Function, variable or struct declaration expected."); @@ -135,6 +138,11 @@ ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic) const bool permitEmptyParameterList = false; m_scanner->next(); returnParameters = parseParameterList(permitEmptyParameterList); + } else { + // create an empty parameter list at a zero-length location + ASTNodeFactory nodeFactory(*this); + nodeFactory.setLocationEmpty(); + returnParameters = nodeFactory.createNode<ParameterList>(vecptr<VariableDeclaration>()); } ptr<Block> block = parseBlock(); nodeFactory.setEndPositionFromNode(block); @@ -151,7 +159,8 @@ ptr<StructDefinition> Parser::parseStructDefinition() vecptr<VariableDeclaration> members; expectToken(Token::LBRACE); while (m_scanner->getCurrentToken() != Token::RBRACE) { - members.push_back(parseVariableDeclaration()); + bool const allowVar = false; + members.push_back(parseVariableDeclaration(allowVar)); expectToken(Token::SEMICOLON); } nodeFactory.markEndPosition(); @@ -160,16 +169,16 @@ ptr<StructDefinition> Parser::parseStructDefinition() return nodeFactory.createNode<StructDefinition>(name, members); } -ptr<VariableDeclaration> Parser::parseVariableDeclaration() +ptr<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar) { ASTNodeFactory nodeFactory(*this); - ptr<TypeName> type = parseTypeName(); + ptr<TypeName> type = parseTypeName(_allowVar); nodeFactory.markEndPosition(); return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); } -ptr<TypeName> Parser::parseTypeName() +ptr<TypeName> Parser::parseTypeName(bool _allowVar) { ptr<TypeName> type; Token::Value token = m_scanner->getCurrentToken(); @@ -177,7 +186,8 @@ ptr<TypeName> Parser::parseTypeName() type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); m_scanner->next(); } else if (token == Token::VAR) { - type = ASTNodeFactory(*this).createNode<TypeName>(); + if (!_allowVar) + throwExpectationError("Expected explicit type name."); m_scanner->next(); } else if (token == Token::MAPPING) { type = parseMapping(); @@ -206,24 +216,26 @@ ptr<Mapping> Parser::parseMapping() m_scanner->next(); expectToken(Token::ARROW); - ptr<TypeName> valueType = parseTypeName(); + bool const allowVar = false; + ptr<TypeName> valueType = parseTypeName(allowVar); nodeFactory.markEndPosition(); expectToken(Token::RPAREN); return nodeFactory.createNode<Mapping>(keyType, valueType); } -ptr<ParameterList> Parser::parseParameterList(bool _permitEmpty) +ptr<ParameterList> Parser::parseParameterList(bool _allowEmpty) { ASTNodeFactory nodeFactory(*this); vecptr<VariableDeclaration> parameters; expectToken(Token::LPAREN); - if (!_permitEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { - parameters.push_back(parseVariableDeclaration()); + if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { + bool const allowVar = false; + parameters.push_back(parseVariableDeclaration(allowVar)); while (m_scanner->getCurrentToken() != Token::RPAREN) { expectToken(Token::COMMA); - parameters.push_back(parseVariableDeclaration()); + parameters.push_back(parseVariableDeclaration(allowVar)); } } nodeFactory.markEndPosition(); @@ -328,7 +340,8 @@ ptr<WhileStatement> Parser::parseWhileStatement() ptr<VariableDefinition> Parser::parseVariableDefinition() { ASTNodeFactory nodeFactory(*this); - ptr<VariableDeclaration> variable = parseVariableDeclaration(); + bool const allowVar = true; + ptr<VariableDeclaration> variable = parseVariableDeclaration(allowVar); ptr<Expression> value; if (m_scanner->getCurrentToken() == Token::ASSIGN) { m_scanner->next(); @@ -47,10 +47,10 @@ private: ptr<ContractDefinition> parseContractDefinition(); ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ptr<StructDefinition> parseStructDefinition(); - ptr<VariableDeclaration> parseVariableDeclaration(); - ptr<TypeName> parseTypeName(); + ptr<VariableDeclaration> parseVariableDeclaration(bool _allowVar); + ptr<TypeName> parseTypeName(bool _allowVar); ptr<Mapping> parseMapping(); - ptr<ParameterList> parseParameterList(bool _permitEmpty = true); + ptr<ParameterList> parseParameterList(bool _allowEmpty = true); ptr<Block> parseBlock(); ptr<Statement> parseStatement(); ptr<IfStatement> parseIfStatement(); diff --git a/Scope.cpp b/Scope.cpp new file mode 100644 index 00000000..27298f87 --- /dev/null +++ b/Scope.cpp @@ -0,0 +1,48 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Scope - object that holds declaration of names. + */ + +#include <libsolidity/Scope.h> +#include <libsolidity/AST.h> + +namespace dev { +namespace solidity { + + +bool Scope::registerDeclaration(Declaration& _declaration) +{ + if (m_declarations.find(_declaration.getName()) != m_declarations.end()) + return false; + m_declarations[_declaration.getName()] = &_declaration; + return true; +} + +Declaration*Scope::resolveName(ASTString const& _name, bool _recursive) const +{ + auto result = m_declarations.find(_name); + if (result != m_declarations.end()) + return result->second; + if (_recursive && m_outerScope != nullptr) + return m_outerScope->resolveName(_name, true); + return nullptr; +} + +} } @@ -24,6 +24,8 @@ #include <map> +#include <boost/noncopyable.hpp> + #include <libsolidity/ASTForward.h> namespace dev { @@ -33,29 +35,15 @@ class Scope { public: explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {} - /// Registers the name _name in the scope unless it is already declared. Returns true iff + /// Registers the declaration in the scope unless its name is already declared. Returns true iff /// it was not yet declared. - bool registerName(ASTString const& _name, ASTNode& _declaration) - { - if (m_declaredNames.find(_name) != m_declaredNames.end()) - return false; - m_declaredNames[_name] = &_declaration; - return true; - } - ASTNode* resolveName(ASTString const& _name, bool _recursive = false) const - { - auto result = m_declaredNames.find(_name); - if (result != m_declaredNames.end()) - return result->second; - if (_recursive && m_outerScope != nullptr) - return m_outerScope->resolveName(_name, true); - return nullptr; - } + bool registerDeclaration(Declaration& _declaration); + Declaration* resolveName(ASTString const& _name, bool _recursive = false) const; Scope* getOuterScope() const { return m_outerScope; } private: Scope* m_outerScope; - std::map<ASTString, ASTNode*> m_declaredNames; + std::map<ASTString, Declaration*> m_declarations; }; } } @@ -93,6 +93,7 @@ namespace solidity { T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ T(ASSIGN, "=", 2) \ + /* The following have to be in exactly the same order as the simple binary operators*/ \ T(ASSIGN_BIT_OR, "|=", 2) \ T(ASSIGN_BIT_XOR, "^=", 2) \ T(ASSIGN_BIT_AND, "&=", 2) \ @@ -117,7 +118,6 @@ namespace solidity { T(SHL, "<<", 11) \ T(SAR, ">>", 11) \ T(SHR, ">>>", 11) \ - T(ROR, "rotate right", 11) /* only used by Crankshaft */ \ T(ADD, "+", 12) \ T(SUB, "-", 12) \ T(MUL, "*", 13) \ @@ -181,7 +181,9 @@ namespace solidity { K(WHILE, "while", 0) \ K(WITH, "with", 0) \ \ - /* type keywords, keep them in this order, keep int as first keyword TODO more to be added */ \ + /* type keywords, keep them in this order, keep int as first keyword + * the implementation in Types.cpp has to be synced to this here + * TODO more to be added */ \ K(INT, "int", 0) \ K(INT32, "int32", 0) \ K(INT64, "int64", 0) \ @@ -274,7 +276,7 @@ public: } static bool IsTruncatingBinaryOp(Value op) { - return BIT_OR <= op && op <= ROR; + return BIT_OR <= op && op <= SHR; } static bool IsCompareOp(Value op) { @@ -332,6 +334,11 @@ public: } } + static Value AssignmentToBinaryOp(Value op) { + BOOST_ASSERT(IsAssignmentOp(op) && op != ASSIGN); + return Token::Value(op + (BIT_OR - ASSIGN_BIT_OR)); + } + static bool IsBitOp(Value op) { return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; } diff --git a/Types.cpp b/Types.cpp new file mode 100644 index 00000000..ee0a653f --- /dev/null +++ b/Types.cpp @@ -0,0 +1,152 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity data types + */ + +#include <libsolidity/Types.h> +#include <libsolidity/AST.h> + +namespace dev { +namespace solidity { + +ptr<Type> Type::fromElementaryTypeName(Token::Value _typeToken) +{ + if (Token::INT <= _typeToken && _typeToken <= Token::HASH256) { + int offset = _typeToken - Token::INT; + int bits = offset % 5; + if (bits == 0) + bits = 256; + else + bits = (1 << (bits - 1)) * 32; + int modifier = offset / 5; + return std::make_shared<IntegerType>(bits, + modifier == 0 ? IntegerType::Modifier::UNSIGNED : + modifier == 1 ? IntegerType::Modifier::SIGNED : + IntegerType::Modifier::HASH); + } else if (_typeToken == Token::ADDRESS) { + return std::make_shared<IntegerType>(0, IntegerType::Modifier::ADDRESS); + } else if (_typeToken == Token::BOOL) { + return std::make_shared<BoolType>(); + } else { + BOOST_ASSERT(false); + // @todo add other tyes + } +} + +ptr<Type> Type::fromUserDefinedTypeName(const UserDefinedTypeName& _typeName) +{ + return std::make_shared<StructType>(*_typeName.getReferencedStruct()); +} + +ptr<Type> Type::fromMapping(const Mapping&) +{ + BOOST_ASSERT(false); //@todo not yet implemented + return ptr<Type>(); +} + +ptr<Type> Type::forLiteral(const Literal& _literal) +{ + switch (_literal.getToken()) { + case Token::TRUE_LITERAL: + case Token::FALSE_LITERAL: + return std::make_shared<BoolType>(); + case Token::NUMBER: + return IntegerType::smallestTypeForLiteral(_literal.getValue()); + case Token::STRING_LITERAL: + return ptr<Type>(); // @todo + default: + return ptr<Type>(); + } +} + +ptr<IntegerType> IntegerType::smallestTypeForLiteral(const std::string&) +{ + //@todo + return std::make_shared<IntegerType>(256, Modifier::UNSIGNED); +} + +IntegerType::IntegerType(int _bits, IntegerType::Modifier _modifier) + : m_bits(_bits), m_modifier(_modifier) +{ + BOOST_ASSERT(_bits > 0 && _bits <= 256 && _bits % 8 == 0); + if (isAddress()) + _bits = 160; +} + +bool IntegerType::isImplicitlyConvertibleTo(Type const& _convertTo) const +{ + if (_convertTo.getCategory() != Category::INTEGER) + return false; + IntegerType const& convertTo = dynamic_cast<IntegerType const&>(_convertTo); + if (convertTo.m_bits < m_bits) + return false; + if (isAddress()) + return convertTo.isAddress(); + else if (isHash()) + return convertTo.isHash(); + else if (isSigned()) + return convertTo.isSigned(); + else + return !convertTo.isSigned() || convertTo.m_bits > m_bits; +} + +bool IntegerType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + // @todo + return false; +} + +bool IntegerType::acceptsBinaryOperator(Token::Value _operator) const +{ + //@todo + return true; +} + +bool IntegerType::acceptsUnaryOperator(Token::Value _operator) const +{ + //@todo + return true; +} + +bool BoolType::isExplicitlyConvertibleTo(const Type& _convertTo) const +{ + //@todo conversion to integer is fine, but not to address + //@todo this is an example of explicit conversions being not transitive (though implicit should) + return isImplicitlyConvertibleTo(_convertTo); +} + +bool ContractType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != Category::CONTRACT) + return false; + ContractType const& convertTo = dynamic_cast<ContractType const&>(_convertTo); + return &m_contract == &convertTo.m_contract; +} + +bool StructType::isImplicitlyConvertibleTo(const Type& _convertTo) const +{ + if (_convertTo.getCategory() != Category::STRUCT) + return false; + StructType const& convertTo = dynamic_cast<StructType const&>(_convertTo); + return &m_struct == &convertTo.m_struct; +} + + +} } diff --git a/Types.h b/Types.h new file mode 100644 index 00000000..50d00a54 --- /dev/null +++ b/Types.h @@ -0,0 +1,171 @@ +/* + This file is part of cpp-ethereum. + + cpp-ethereum is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + cpp-ethereum is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>. +*/ +/** + * @author Christian <c@ethdev.com> + * @date 2014 + * Solidity data types + */ + +#pragma once + +#include <memory> +#include <string> +#include <boost/noncopyable.hpp> +#include <boost/assert.hpp> + +#include <libsolidity/Token.h> + +namespace dev { +namespace solidity { + +// AST forward declarations +class ContractDefinition; +class FunctionDefinition; +class StructDefinition; +class Literal; +class ElementaryTypeName; +class UserDefinedTypeName; +class Mapping; + +template <typename T> +using ptr = std::shared_ptr<T>; + +// @todo realMxN, string<N>, mapping + +class Type : private boost::noncopyable +{ +public: + enum class Category { + INTEGER, BOOL, REAL, STRING, CONTRACT, STRUCT, FUNCTION, MAPPING, VOID, TYPE + }; + + //! factory functions that convert an AST TypeName to a Type. + static ptr<Type> fromElementaryTypeName(Token::Value _typeToken); + static ptr<Type> fromUserDefinedTypeName(UserDefinedTypeName const& _typeName); + static ptr<Type> fromMapping(Mapping const& _typeName); + + static ptr<Type> forLiteral(Literal const& _literal); + + virtual Category getCategory() const = 0; + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const { return false; } + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const { return isImplicitlyConvertibleTo(_convertTo); } + virtual bool acceptsBinaryOperator(Token::Value _operator) const { return false; } + virtual bool acceptsUnaryOperator(Token::Value _operator) const { return false; } +}; + +class IntegerType : public Type +{ +public: + enum class Modifier { + UNSIGNED, SIGNED, HASH, ADDRESS + }; + virtual Category getCategory() const { return Category::INTEGER; } + + static ptr<IntegerType> smallestTypeForLiteral(std::string const& _literal); + + explicit IntegerType(int _bits, Modifier _modifier = Modifier::UNSIGNED); + + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const override; + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool acceptsBinaryOperator(Token::Value _operator) const override; + virtual bool acceptsUnaryOperator(Token::Value _operator) const override; + + int getNumBits() const { return m_bits; } + bool isHash() const { return m_modifier == Modifier::HASH || m_modifier == Modifier::ADDRESS; } + bool isAddress() const { return m_modifier == Modifier::ADDRESS; } + int isSigned() const { return m_modifier == Modifier::SIGNED; } +private: + int m_bits; + Modifier m_modifier; +}; + +class BoolType : public Type +{ +public: + virtual Category getCategory() const { return Category::BOOL; } + virtual bool isImplicitlyConvertibleTo(const Type& _convertTo) const override + { return _convertTo.getCategory() == Category::BOOL; } + virtual bool isExplicitlyConvertibleTo(const Type& _convertTo) const override; + virtual bool acceptsBinaryOperator(Token::Value _operator) const override + { return _operator == Token::AND || _operator == Token::OR; } + virtual bool acceptsUnaryOperator(Token::Value _operator) const override + { return _operator == Token::NOT || _operator == Token::DELETE; } +}; + +class ContractType : public Type +{ +public: + virtual Category getCategory() const { return Category::CONTRACT; } + ContractType(ContractDefinition const& _contract) : m_contract(_contract) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; +private: + ContractDefinition const& m_contract; +}; + +class StructType : public Type +{ +public: + virtual Category getCategory() const { return Category::STRUCT; } + StructType(StructDefinition const& _struct) : m_struct(_struct) {} + virtual bool isImplicitlyConvertibleTo(Type const& _convertTo) const; + virtual bool acceptsUnaryOperator(Token::Value _operator) const override + { return _operator == Token::DELETE; } +private: + StructDefinition const& m_struct; +}; + +class FunctionType : public Type +{ +public: + virtual Category getCategory() const { return Category::FUNCTION; } + FunctionType(FunctionDefinition const& _function) : m_function(_function) {} + + FunctionDefinition const& getFunction() const { return m_function; } +private: + FunctionDefinition const& m_function; +}; + +class MappingType : public Type +{ +public: + virtual Category getCategory() const { return Category::MAPPING; } + MappingType() {} +private: + //@todo +}; + +//@todo should be changed into "empty anonymous struct" +class VoidType : public Type +{ +public: + virtual Category getCategory() const { return Category::VOID; } + VoidType() {} +}; + +class TypeType : public Type +{ +public: + virtual Category getCategory() const { return Category::TYPE; } + TypeType(ptr<Type> const& _actualType) : m_actualType(_actualType) {} + + ptr<Type> const& getActualType() { return m_actualType; } +private: + ptr<Type> m_actualType; +}; + + +} } |