aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AST.cpp244
-rw-r--r--AST.h123
-rw-r--r--ASTForward.h1
-rw-r--r--ASTPrinter.cpp2
-rw-r--r--NameAndTypeResolver.cpp103
-rw-r--r--NameAndTypeResolver.h15
-rw-r--r--Parser.cpp37
-rw-r--r--Parser.h6
-rw-r--r--Scope.cpp48
-rw-r--r--Scope.h24
-rw-r--r--Token.h13
-rw-r--r--Types.cpp152
-rw-r--r--Types.h171
13 files changed, 816 insertions, 123 deletions
diff --git a/AST.cpp b/AST.cpp
index e0775c6c..325ef50b 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -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;
+}
+
} }
diff --git a/AST.h b/AST.h
index cbb78087..c1509b85 100644
--- a/AST.h
+++ b/AST.h
@@ -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();
diff --git a/Parser.cpp b/Parser.cpp
index eb171cbc..ab72bf7f 100644
--- a/Parser.cpp
+++ b/Parser.cpp
@@ -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();
diff --git a/Parser.h b/Parser.h
index 88369052..84d20cf8 100644
--- a/Parser.h
+++ b/Parser.h
@@ -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;
+}
+
+} }
diff --git a/Scope.h b/Scope.h
index be2514c4..e3b024ec 100644
--- a/Scope.h
+++ b/Scope.h
@@ -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;
};
} }
diff --git a/Token.h b/Token.h
index 2ff5067b..d1db4134 100644
--- a/Token.h
+++ b/Token.h
@@ -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;
+};
+
+
+} }