Browse Source

Type system, not yet complete.

cl-refactor
Christian 10 years ago
parent
commit
2f9d821967
  1. 244
      libsolidity/AST.cpp
  2. 123
      libsolidity/AST.h
  3. 1
      libsolidity/ASTForward.h
  4. 2
      libsolidity/ASTPrinter.cpp
  5. 103
      libsolidity/NameAndTypeResolver.cpp
  6. 15
      libsolidity/NameAndTypeResolver.h
  7. 37
      libsolidity/Parser.cpp
  8. 6
      libsolidity/Parser.h
  9. 48
      libsolidity/Scope.cpp
  10. 24
      libsolidity/Scope.h
  11. 13
      libsolidity/Token.h
  12. 152
      libsolidity/Types.cpp
  13. 171
      libsolidity/Types.h
  14. 14
      test/solidityNameAndTypeResolution.cpp

244
libsolidity/AST.cpp

@ -20,6 +20,8 @@
* Solidity abstract syntax tree. * Solidity abstract syntax tree.
*/ */
#include <algorithm>
#include <libsolidity/AST.h> #include <libsolidity/AST.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
@ -66,8 +68,8 @@ void FunctionDefinition::accept(ASTVisitor& _visitor)
void VariableDeclaration::accept(ASTVisitor& _visitor) void VariableDeclaration::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this)) {
if (m_type) if (m_typeName)
m_type->accept(_visitor); m_typeName->accept(_visitor);
} }
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
@ -170,12 +172,6 @@ void VariableDefinition::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void Expression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Assignment::accept(ASTVisitor& _visitor) void Assignment::accept(ASTVisitor& _visitor)
{ {
if (_visitor.visit(*this)) { if (_visitor.visit(*this)) {
@ -228,12 +224,6 @@ void IndexAccess::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this); _visitor.endVisit(*this);
} }
void PrimaryExpression::accept(ASTVisitor& _visitor)
{
_visitor.visit(*this);
_visitor.endVisit(*this);
}
void Identifier::accept(ASTVisitor& _visitor) void Identifier::accept(ASTVisitor& _visitor)
{ {
_visitor.visit(*this); _visitor.visit(*this);
@ -252,4 +242,230 @@ void Literal::accept(ASTVisitor& _visitor)
_visitor.endVisit(*this); _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;
}
} } } }

123
libsolidity/AST.h

@ -22,6 +22,8 @@
#pragma once #pragma once
#include <boost/noncopyable.hpp>
#include <string> #include <string>
#include <vector> #include <vector>
#include <memory> #include <memory>
@ -29,13 +31,14 @@
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
#include <libsolidity/BaseTypes.h> #include <libsolidity/BaseTypes.h>
#include <libsolidity/Token.h> #include <libsolidity/Token.h>
#include <libsolidity/Types.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
class ASTVisitor; class ASTVisitor;
class ASTNode class ASTNode : private boost::noncopyable
{ {
public: public:
explicit ASTNode(Location const& _location) explicit ASTNode(Location const& _location)
@ -55,7 +58,18 @@ private:
Location m_location; 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: public:
ContractDefinition(Location const& _location, ContractDefinition(Location const& _location,
@ -63,7 +77,7 @@ public:
vecptr<StructDefinition> const& _definedStructs, vecptr<StructDefinition> const& _definedStructs,
vecptr<VariableDeclaration> const& _stateVariables, vecptr<VariableDeclaration> const& _stateVariables,
vecptr<FunctionDefinition> const& _definedFunctions) vecptr<FunctionDefinition> const& _definedFunctions)
: ASTNode(_location), m_name(_name), : Declaration(_location, _name),
m_definedStructs(_definedStructs), m_definedStructs(_definedStructs),
m_stateVariables(_stateVariables), m_stateVariables(_stateVariables),
m_definedFunctions(_definedFunctions) m_definedFunctions(_definedFunctions)
@ -71,30 +85,26 @@ public:
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getName() const { return *m_name; }
vecptr<StructDefinition> const& getDefinedStructs() { return m_definedStructs; } vecptr<StructDefinition> const& getDefinedStructs() { return m_definedStructs; }
vecptr<VariableDeclaration> const& getStateVariables() { return m_stateVariables; } vecptr<VariableDeclaration> const& getStateVariables() { return m_stateVariables; }
vecptr<FunctionDefinition> const& getDefinedFunctions() { return m_definedFunctions; } vecptr<FunctionDefinition> const& getDefinedFunctions() { return m_definedFunctions; }
private: private:
ptr<ASTString> m_name;
vecptr<StructDefinition> m_definedStructs; vecptr<StructDefinition> m_definedStructs;
vecptr<VariableDeclaration> m_stateVariables; vecptr<VariableDeclaration> m_stateVariables;
vecptr<FunctionDefinition> m_definedFunctions; vecptr<FunctionDefinition> m_definedFunctions;
}; };
class StructDefinition : public ASTNode class StructDefinition : public Declaration
{ {
public: public:
StructDefinition(Location const& _location, StructDefinition(Location const& _location,
ptr<ASTString> const& _name, ptr<ASTString> const& _name,
vecptr<VariableDeclaration> const& _members) vecptr<VariableDeclaration> const& _members)
: ASTNode(_location), m_name(_name), m_members(_members) : Declaration(_location, _name), m_members(_members)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getName() const { return *m_name; }
private: private:
ptr<ASTString> m_name;
vecptr<VariableDeclaration> m_members; vecptr<VariableDeclaration> m_members;
}; };
@ -114,7 +124,7 @@ private:
vecptr<VariableDeclaration> m_parameters; vecptr<VariableDeclaration> m_parameters;
}; };
class FunctionDefinition : public ASTNode class FunctionDefinition : public Declaration
{ {
public: public:
FunctionDefinition(Location const& _location, ptr<ASTString> const& _name, bool _isPublic, FunctionDefinition(Location const& _location, ptr<ASTString> const& _name, bool _isPublic,
@ -122,43 +132,47 @@ public:
bool _isDeclaredConst, bool _isDeclaredConst,
ptr<ParameterList> const& _returnParameters, ptr<ParameterList> const& _returnParameters,
ptr<Block> const& _body) 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_isDeclaredConst(_isDeclaredConst), m_returnParameters(_returnParameters),
m_body(_body) m_body(_body)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getName() const { return *m_name; }
bool isPublic() const { return m_isPublic; } bool isPublic() const { return m_isPublic; }
bool isDeclaredConst() const { return m_isDeclaredConst; } bool isDeclaredConst() const { return m_isDeclaredConst; }
vecptr<VariableDeclaration> const& getParameters() { return m_parameters->getParameters(); } vecptr<VariableDeclaration> const& getParameters() const { return m_parameters->getParameters(); }
bool hasReturnParameters() const { return m_returnParameters.get() != nullptr; } ParameterList& getParameterList() { return *m_parameters; }
vecptr<VariableDeclaration> const& getReturnParameters() { return m_returnParameters->getParameters(); } ptr<ParameterList> const& getReturnParameterList() const { return m_returnParameters; }
Block& getBody() { return *m_body; } Block& getBody() { return *m_body; }
private: private:
ptr<ASTString> m_name;
bool m_isPublic; bool m_isPublic;
ptr<ParameterList> m_parameters; ptr<ParameterList> m_parameters;
bool m_isDeclaredConst; 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; ptr<Block> m_body;
}; };
class VariableDeclaration : public ASTNode class VariableDeclaration : public Declaration
{ {
public: public:
VariableDeclaration(Location const& _location, VariableDeclaration(Location const& _location,
ptr<TypeName> const& _type, ptr<TypeName> const& _type,
ptr<ASTString> const& _name) ptr<ASTString> const& _name)
: ASTNode(_location), m_type(_type), m_name(_name) : Declaration(_location, _name), m_typeName(_type)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
TypeName* getTypeName() const { return m_type.get(); } bool isTypeGivenExplicitly() const { return m_typeName.get() != nullptr; }
const ASTString& getName() const { return *m_name; } 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: private:
ptr<TypeName> m_type; ///< can be empty ("var") ptr<TypeName> m_typeName; ///< can be empty ("var")
ptr<ASTString> m_name;
ptr<Type> m_type;
}; };
/// types /// types
@ -169,6 +183,8 @@ class TypeName : public ASTNode
public: public:
explicit TypeName(Location const& _location) : ASTNode(_location) {} explicit TypeName(Location const& _location) : ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> toType() = 0;
}; };
/// any pre-defined type that is not a mapping /// any pre-defined type that is not a mapping
@ -179,6 +195,7 @@ public:
: TypeName(_location), m_type(_type) : TypeName(_location), m_type(_type)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> toType() override { return Type::fromElementaryTypeName(m_type); }
Token::Value getType() const { return m_type; } Token::Value getType() const { return m_type; }
private: private:
@ -192,10 +209,15 @@ public:
: TypeName(_location), m_name(_name) : TypeName(_location), m_name(_name)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> toType() override { return Type::fromUserDefinedTypeName(*this); }
const ASTString& getName() const { return *m_name; } const ASTString& getName() const { return *m_name; }
void setReferencedStruct(StructDefinition& _referencedStruct) { m_referencedStruct = &_referencedStruct; }
StructDefinition const* getReferencedStruct() const { return m_referencedStruct; }
private: private:
ptr<ASTString> m_name; ptr<ASTString> m_name;
StructDefinition* m_referencedStruct;
}; };
class Mapping : public TypeName class Mapping : public TypeName
@ -206,6 +228,7 @@ public:
: TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) : TypeName(_location), m_keyType(_keyType), m_valueType(_valueType)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> toType() override { return Type::fromMapping(*this); }
private: private:
ptr<ElementaryTypeName> m_keyType; ptr<ElementaryTypeName> m_keyType;
ptr<TypeName> m_valueType; ptr<TypeName> m_valueType;
@ -221,6 +244,15 @@ class Statement : public ASTNode
public: public:
explicit Statement(Location const& _location) : ASTNode(_location) {} explicit Statement(Location const& _location) : ASTNode(_location) {}
virtual void accept(ASTVisitor& _visitor) override; 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 class Block : public Statement
@ -230,6 +262,8 @@ public:
: Statement(_location), m_statements(_statements) : Statement(_location), m_statements(_statements)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
vecptr<Statement> m_statements; vecptr<Statement> m_statements;
}; };
@ -243,6 +277,7 @@ public:
m_trueBody(_trueBody), m_falseBody(_falseBody) m_trueBody(_trueBody), m_falseBody(_falseBody)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<Expression> m_condition; ptr<Expression> m_condition;
ptr<Statement> m_trueBody; ptr<Statement> m_trueBody;
@ -264,6 +299,7 @@ public:
: BreakableStatement(_location), m_condition(_condition), m_body(_body) : BreakableStatement(_location), m_condition(_condition), m_body(_body)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<Expression> m_condition; ptr<Expression> m_condition;
ptr<Statement> m_body; ptr<Statement> m_body;
@ -274,6 +310,7 @@ class Continue : public Statement
public: public:
Continue(Location const& _location) : Statement(_location) {} Continue(Location const& _location) : Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
}; };
class Break : public Statement class Break : public Statement
@ -281,6 +318,7 @@ class Break : public Statement
public: public:
Break(Location const& _location) : Statement(_location) {} Break(Location const& _location) : Statement(_location) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
}; };
class Return : public Statement class Return : public Statement
@ -290,8 +328,13 @@ public:
: Statement(_location), m_expression(_expression) : Statement(_location), m_expression(_expression)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; }
private: private:
ptr<Expression> m_expression; //< value to return, optional ptr<Expression> m_expression; //< value to return, optional
ParameterList* m_returnParameters; //< extracted from the function declaration
}; };
class VariableDefinition : public Statement class VariableDefinition : public Statement
@ -302,6 +345,8 @@ public:
: Statement(_location), m_variable(_variable), m_value(_value) : Statement(_location), m_variable(_variable), m_value(_value)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<VariableDeclaration> m_variable; ptr<VariableDeclaration> m_variable;
ptr<Expression> m_value; ///< can be missing ptr<Expression> m_value; ///< can be missing
@ -311,7 +356,9 @@ class Expression : public Statement
{ {
public: public:
Expression(Location const& _location) : Statement(_location) {} 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) m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
Token::Value getAssignmentOperator() const { return m_assigmentOperator; } Token::Value getAssignmentOperator() const { return m_assigmentOperator; }
private: private:
@ -345,6 +393,7 @@ public:
m_subExpression(_subExpression), m_isPrefix(_isPrefix) m_subExpression(_subExpression), m_isPrefix(_isPrefix)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
bool isPrefixOperation() const { return m_isPrefix; } bool isPrefixOperation() const { return m_isPrefix; }
@ -362,12 +411,15 @@ public:
: Expression(_location), m_left(_left), m_operator(_operator), m_right(_right) : Expression(_location), m_left(_left), m_operator(_operator), m_right(_right)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
Token::Value getOperator() const { return m_operator; } Token::Value getOperator() const { return m_operator; }
private: private:
ptr<Expression> m_left; ptr<Expression> m_left;
Token::Value m_operator; Token::Value m_operator;
ptr<Expression> m_right; ptr<Expression> m_right;
ptr<Type> m_commonType;
}; };
/// Can be ordinary function call, type cast or struct construction. /// Can be ordinary function call, type cast or struct construction.
@ -379,6 +431,7 @@ public:
: Expression(_location), m_expression(_expression), m_arguments(_arguments) : Expression(_location), m_expression(_expression), m_arguments(_arguments)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<Expression> m_expression; ptr<Expression> m_expression;
vecptr<Expression> m_arguments; vecptr<Expression> m_arguments;
@ -393,6 +446,7 @@ public:
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
const ASTString& getMemberName() const { return *m_memberName; } const ASTString& getMemberName() const { return *m_memberName; }
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<Expression> m_expression; ptr<Expression> m_expression;
ptr<ASTString> m_memberName; ptr<ASTString> m_memberName;
@ -406,6 +460,7 @@ public:
: Expression(_location), m_base(_base), m_index(_index) : Expression(_location), m_base(_base), m_index(_index)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
private: private:
ptr<Expression> m_base; ptr<Expression> m_base;
ptr<Expression> m_index; ptr<Expression> m_index;
@ -415,7 +470,6 @@ class PrimaryExpression : public Expression
{ {
public: public:
PrimaryExpression(Location const& _location) : Expression(_location) {} PrimaryExpression(Location const& _location) : Expression(_location) {}
virtual void accept(ASTVisitor& _visitor) override;
}; };
class Identifier : public PrimaryExpression class Identifier : public PrimaryExpression
@ -424,27 +478,29 @@ public:
Identifier(Location const& _location, ptr<ASTString> const& _name) Identifier(Location const& _location, ptr<ASTString> const& _name)
: PrimaryExpression(_location), m_name(_name) {} : PrimaryExpression(_location), m_name(_name) {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
ASTString const& getName() const { return *m_name; } ASTString const& getName() const { return *m_name; }
void setReferencedObject(ASTNode& _referencedObject) { m_referencedObject = &_referencedObject; } void setReferencedDeclaration(Declaration& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
ASTNode* getReferencedVariable() { return m_referencedObject; } Declaration* getReferencedDeclaration() { return m_referencedDeclaration; }
private: private:
ptr<ASTString> m_name; ptr<ASTString> m_name;
//! Node the name refers to. Has to be a declaration of some sort. //! Declaration the name refers to.
ASTNode* m_referencedObject; Declaration* m_referencedDeclaration;
}; };
class ElementaryTypeNameExpression : public PrimaryExpression class ElementaryTypeNameExpression : public PrimaryExpression
{ {
public: public:
ElementaryTypeNameExpression(Location const& _location, Token::Value _type) ElementaryTypeNameExpression(Location const& _location, Token::Value _typeToken)
: PrimaryExpression(_location), m_type(_type) {} : PrimaryExpression(_location), m_typeToken(_typeToken) {}
virtual void accept(ASTVisitor& _visitor) override; 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: private:
Token::Value m_type; Token::Value m_typeToken;
}; };
class Literal : public PrimaryExpression class Literal : public PrimaryExpression
@ -454,6 +510,7 @@ public:
: PrimaryExpression(_location), m_token(_token), m_value(_value) : PrimaryExpression(_location), m_token(_token), m_value(_value)
{} {}
virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTVisitor& _visitor) override;
virtual ptr<Type> checkTypeRequirements() override;
Token::Value getToken() const { return m_token; } Token::Value getToken() const { return m_token; }
ASTString const& getValue() const { return *m_value; } ASTString const& getValue() const { return *m_value; }

1
libsolidity/ASTForward.h

@ -32,6 +32,7 @@ namespace dev {
namespace solidity { namespace solidity {
class ASTNode; class ASTNode;
class Declaration;
class ContractDefinition; class ContractDefinition;
class StructDefinition; class StructDefinition;
class ParameterList; class ParameterList;

2
libsolidity/ASTPrinter.cpp

@ -233,7 +233,7 @@ bool ASTPrinter::visit(Identifier& _node)
bool ASTPrinter::visit(ElementaryTypeNameExpression& _node) bool ASTPrinter::visit(ElementaryTypeNameExpression& _node)
{ {
writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getType())); writeLine(std::string("ElementaryTypeNameExpression ") + Token::String(_node.getTypeToken()));
printSourcePart(_node); printSourcePart(_node);
return goDeeper(); return goDeeper();
} }

103
libsolidity/NameAndTypeResolver.cpp

@ -31,10 +31,10 @@ namespace solidity {
class NameAndTypeResolver::ScopeHelper { class NameAndTypeResolver::ScopeHelper {
public: public:
ScopeHelper(NameAndTypeResolver& _resolver, ASTString const& _name, ASTNode& _declaration) ScopeHelper(NameAndTypeResolver& _resolver, Declaration& _declaration)
: m_resolver(_resolver) : m_resolver(_resolver)
{ {
m_resolver.registerName(_name, _declaration); m_resolver.registerDeclaration(_declaration);
m_resolver.enterNewSubScope(_declaration); m_resolver.enterNewSubScope(_declaration);
} }
~ScopeHelper() ~ScopeHelper()
@ -60,16 +60,15 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
void NameAndTypeResolver::handleContract(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()) for (ptr<VariableDeclaration> const& variable : _contract.getStateVariables())
registerName(variable->getName(), *variable); registerVariableDeclarationAndResolveType(*variable);
// @todo structs
for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions()) for (ptr<FunctionDefinition> const& function : _contract.getDefinedFunctions())
handleFunction(*function); handleFunction(*function);
// @todo resolve names used in mappings
} }
void NameAndTypeResolver::reset() void NameAndTypeResolver::reset()
@ -81,30 +80,20 @@ void NameAndTypeResolver::reset()
void NameAndTypeResolver::handleFunction(FunctionDefinition& _function) void NameAndTypeResolver::handleFunction(FunctionDefinition& _function)
{ {
ScopeHelper scopeHelper(*this, _function.getName(), _function); ScopeHelper scopeHelper(*this, _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());
}
void NameAndTypeResolver::handleFunctionBody(Block& _functionBody) registerVariablesInFunction(_function);
{ resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody());
registerVariablesInFunction(_functionBody); _function.getBody().checkTypeRequirements();
resolveReferencesInFunction(_functionBody);
} }
void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody) void NameAndTypeResolver::registerVariablesInFunction(FunctionDefinition& _function)
{ {
class VariableDeclarationFinder : public ASTVisitor { class VariableDeclarationFinder : public ASTVisitor {
public: public:
VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} VariableDeclarationFinder(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {}
virtual bool visit(VariableDeclaration& _variable) override { virtual bool visit(VariableDeclaration& _variable) override {
m_resolver.registerName(_variable.getName(), _variable); m_resolver.registerVariableDeclarationAndResolveType(_variable);
return false; return false;
} }
private: private:
@ -112,37 +101,85 @@ void NameAndTypeResolver::registerVariablesInFunction(Block& _functionBody)
}; };
VariableDeclarationFinder declarationFinder(*this); 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 { class ReferencesResolver : public ASTVisitor {
public: public:
ReferencesResolver(NameAndTypeResolver& _resolver) : m_resolver(_resolver) {} ReferencesResolver(NameAndTypeResolver& _resolver,
ParameterList& _returnParameters)
: m_resolver(_resolver), m_returnParameters(_returnParameters) {}
virtual bool visit(Identifier& _identifier) override { virtual bool visit(Identifier& _identifier) override {
ASTNode* node = m_resolver.getNameFromCurrentScope(_identifier.getName()); Declaration* declaration = m_resolver.getNameFromCurrentScope(_identifier.getName());
if (node == nullptr) if (declaration == nullptr)
throw std::exception(); // @todo throw std::exception(); // @todo
_identifier.setReferencedObject(*node); _identifier.setReferencedDeclaration(*declaration);
return false; return false;
} }
virtual bool visit(Return& _return) override {
_return.setFunctionReturnParameters(m_returnParameters);
return true;
}
private: private:
NameAndTypeResolver& m_resolver; NameAndTypeResolver& m_resolver;
ParameterList& m_returnParameters;
}; };
ReferencesResolver referencesResolver(*this); ReferencesResolver referencesResolver(*this, _returnParameters);
_functionBody.accept(referencesResolver); _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 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); return m_currentScope->resolveName(_name, _recursive);
} }

15
libsolidity/NameAndTypeResolver.h

@ -24,13 +24,15 @@
#include <map> #include <map>
#include <boost/noncopyable.hpp>
#include <libsolidity/Scope.h> #include <libsolidity/Scope.h>
#include <libsolidity/ASTVisitor.h> #include <libsolidity/ASTVisitor.h>
namespace dev { namespace dev {
namespace solidity { namespace solidity {
class NameAndTypeResolver class NameAndTypeResolver : private boost::noncopyable
{ {
public: public:
NameAndTypeResolver(); NameAndTypeResolver();
@ -43,12 +45,13 @@ private:
void handleContract(ContractDefinition& _contract); void handleContract(ContractDefinition& _contract);
void handleFunction(FunctionDefinition& _function); void handleFunction(FunctionDefinition& _function);
void handleFunctionBody(Block& _functionBody); void registerVariablesInFunction(FunctionDefinition& _function);
void registerVariablesInFunction(Block& _functionBody); void resolveReferencesInFunction(ParameterList& _returnParameters,
void resolveReferencesInFunction(Block& _functionBody); Block& _functionBody);
void registerName(ASTString const& _name, ASTNode& _declaration); void registerVariableDeclarationAndResolveType(VariableDeclaration& _variable);
ASTNode* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true); void registerDeclaration(Declaration& _declaration);
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
void enterNewSubScope(ASTNode& _node); void enterNewSubScope(ASTNode& _node);
void closeCurrentScope(); void closeCurrentScope();

37
libsolidity/Parser.cpp

@ -47,6 +47,8 @@ public:
void markEndPosition() { m_location.end = m_parser.getEndPosition(); } 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. /// Set the end position to the one of the given node.
void setEndPositionFromNode(const ptr<ASTNode>& _node) void setEndPositionFromNode(const ptr<ASTNode>& _node)
{ {
@ -104,7 +106,8 @@ ptr<ContractDefinition> Parser::parseContractDefinition()
structs.push_back(parseStructDefinition()); structs.push_back(parseStructDefinition());
} else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING || } else if (currentToken == Token::IDENTIFIER || currentToken == Token::MAPPING ||
Token::IsElementaryTypeName(currentToken)) { Token::IsElementaryTypeName(currentToken)) {
stateVariables.push_back(parseVariableDeclaration()); bool const allowVar = false;
stateVariables.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} else { } else {
throwExpectationError("Function, variable or struct declaration expected."); throwExpectationError("Function, variable or struct declaration expected.");
@ -135,6 +138,11 @@ ptr<FunctionDefinition> Parser::parseFunctionDefinition(bool _isPublic)
const bool permitEmptyParameterList = false; const bool permitEmptyParameterList = false;
m_scanner->next(); m_scanner->next();
returnParameters = parseParameterList(permitEmptyParameterList); 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(); ptr<Block> block = parseBlock();
nodeFactory.setEndPositionFromNode(block); nodeFactory.setEndPositionFromNode(block);
@ -151,7 +159,8 @@ ptr<StructDefinition> Parser::parseStructDefinition()
vecptr<VariableDeclaration> members; vecptr<VariableDeclaration> members;
expectToken(Token::LBRACE); expectToken(Token::LBRACE);
while (m_scanner->getCurrentToken() != Token::RBRACE) { while (m_scanner->getCurrentToken() != Token::RBRACE) {
members.push_back(parseVariableDeclaration()); bool const allowVar = false;
members.push_back(parseVariableDeclaration(allowVar));
expectToken(Token::SEMICOLON); expectToken(Token::SEMICOLON);
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -160,16 +169,16 @@ ptr<StructDefinition> Parser::parseStructDefinition()
return nodeFactory.createNode<StructDefinition>(name, members); return nodeFactory.createNode<StructDefinition>(name, members);
} }
ptr<VariableDeclaration> Parser::parseVariableDeclaration() ptr<VariableDeclaration> Parser::parseVariableDeclaration(bool _allowVar)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<TypeName> type = parseTypeName(); ptr<TypeName> type = parseTypeName(_allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken()); return nodeFactory.createNode<VariableDeclaration>(type, expectIdentifierToken());
} }
ptr<TypeName> Parser::parseTypeName() ptr<TypeName> Parser::parseTypeName(bool _allowVar)
{ {
ptr<TypeName> type; ptr<TypeName> type;
Token::Value token = m_scanner->getCurrentToken(); Token::Value token = m_scanner->getCurrentToken();
@ -177,7 +186,8 @@ ptr<TypeName> Parser::parseTypeName()
type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token); type = ASTNodeFactory(*this).createNode<ElementaryTypeName>(token);
m_scanner->next(); m_scanner->next();
} else if (token == Token::VAR) { } else if (token == Token::VAR) {
type = ASTNodeFactory(*this).createNode<TypeName>(); if (!_allowVar)
throwExpectationError("Expected explicit type name.");
m_scanner->next(); m_scanner->next();
} else if (token == Token::MAPPING) { } else if (token == Token::MAPPING) {
type = parseMapping(); type = parseMapping();
@ -206,24 +216,26 @@ ptr<Mapping> Parser::parseMapping()
m_scanner->next(); m_scanner->next();
expectToken(Token::ARROW); expectToken(Token::ARROW);
ptr<TypeName> valueType = parseTypeName(); bool const allowVar = false;
ptr<TypeName> valueType = parseTypeName(allowVar);
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
expectToken(Token::RPAREN); expectToken(Token::RPAREN);
return nodeFactory.createNode<Mapping>(keyType, valueType); return nodeFactory.createNode<Mapping>(keyType, valueType);
} }
ptr<ParameterList> Parser::parseParameterList(bool _permitEmpty) ptr<ParameterList> Parser::parseParameterList(bool _allowEmpty)
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
vecptr<VariableDeclaration> parameters; vecptr<VariableDeclaration> parameters;
expectToken(Token::LPAREN); expectToken(Token::LPAREN);
if (!_permitEmpty || m_scanner->getCurrentToken() != Token::RPAREN) { if (!_allowEmpty || m_scanner->getCurrentToken() != Token::RPAREN) {
parameters.push_back(parseVariableDeclaration()); bool const allowVar = false;
parameters.push_back(parseVariableDeclaration(allowVar));
while (m_scanner->getCurrentToken() != Token::RPAREN) { while (m_scanner->getCurrentToken() != Token::RPAREN) {
expectToken(Token::COMMA); expectToken(Token::COMMA);
parameters.push_back(parseVariableDeclaration()); parameters.push_back(parseVariableDeclaration(allowVar));
} }
} }
nodeFactory.markEndPosition(); nodeFactory.markEndPosition();
@ -328,7 +340,8 @@ ptr<WhileStatement> Parser::parseWhileStatement()
ptr<VariableDefinition> Parser::parseVariableDefinition() ptr<VariableDefinition> Parser::parseVariableDefinition()
{ {
ASTNodeFactory nodeFactory(*this); ASTNodeFactory nodeFactory(*this);
ptr<VariableDeclaration> variable = parseVariableDeclaration(); bool const allowVar = true;
ptr<VariableDeclaration> variable = parseVariableDeclaration(allowVar);
ptr<Expression> value; ptr<Expression> value;
if (m_scanner->getCurrentToken() == Token::ASSIGN) { if (m_scanner->getCurrentToken() == Token::ASSIGN) {
m_scanner->next(); m_scanner->next();

6
libsolidity/Parser.h

@ -47,10 +47,10 @@ private:
ptr<ContractDefinition> parseContractDefinition(); ptr<ContractDefinition> parseContractDefinition();
ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic); ptr<FunctionDefinition> parseFunctionDefinition(bool _isPublic);
ptr<StructDefinition> parseStructDefinition(); ptr<StructDefinition> parseStructDefinition();
ptr<VariableDeclaration> parseVariableDeclaration(); ptr<VariableDeclaration> parseVariableDeclaration(bool _allowVar);
ptr<TypeName> parseTypeName(); ptr<TypeName> parseTypeName(bool _allowVar);
ptr<Mapping> parseMapping(); ptr<Mapping> parseMapping();
ptr<ParameterList> parseParameterList(bool _permitEmpty = true); ptr<ParameterList> parseParameterList(bool _allowEmpty = true);
ptr<Block> parseBlock(); ptr<Block> parseBlock();
ptr<Statement> parseStatement(); ptr<Statement> parseStatement();
ptr<IfStatement> parseIfStatement(); ptr<IfStatement> parseIfStatement();

48
libsolidity/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
libsolidity/Scope.h

@ -24,6 +24,8 @@
#include <map> #include <map>
#include <boost/noncopyable.hpp>
#include <libsolidity/ASTForward.h> #include <libsolidity/ASTForward.h>
namespace dev { namespace dev {
@ -33,29 +35,15 @@ class Scope
{ {
public: public:
explicit Scope(Scope* _outerScope = nullptr) : m_outerScope(_outerScope) {} 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. /// it was not yet declared.
bool registerName(ASTString const& _name, ASTNode& _declaration) bool registerDeclaration(Declaration& _declaration);
{ Declaration* resolveName(ASTString const& _name, bool _recursive = false) const;
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;
}
Scope* getOuterScope() const { return m_outerScope; } Scope* getOuterScope() const { return m_outerScope; }
private: private:
Scope* m_outerScope; Scope* m_outerScope;
std::map<ASTString, ASTNode*> m_declaredNames; std::map<ASTString, Declaration*> m_declarations;
}; };
} } } }

13
libsolidity/Token.h

@ -93,6 +93,7 @@ namespace solidity {
T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \ T(INIT_CONST, "=init_const", 2) /* AST-use only. */ \
T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \ T(INIT_CONST_LEGACY, "=init_const_legacy", 2) /* AST-use only. */ \
T(ASSIGN, "=", 2) \ 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_OR, "|=", 2) \
T(ASSIGN_BIT_XOR, "^=", 2) \ T(ASSIGN_BIT_XOR, "^=", 2) \
T(ASSIGN_BIT_AND, "&=", 2) \ T(ASSIGN_BIT_AND, "&=", 2) \
@ -117,7 +118,6 @@ namespace solidity {
T(SHL, "<<", 11) \ T(SHL, "<<", 11) \
T(SAR, ">>", 11) \ T(SAR, ">>", 11) \
T(SHR, ">>>", 11) \ T(SHR, ">>>", 11) \
T(ROR, "rotate right", 11) /* only used by Crankshaft */ \
T(ADD, "+", 12) \ T(ADD, "+", 12) \
T(SUB, "-", 12) \ T(SUB, "-", 12) \
T(MUL, "*", 13) \ T(MUL, "*", 13) \
@ -181,7 +181,9 @@ namespace solidity {
K(WHILE, "while", 0) \ K(WHILE, "while", 0) \
K(WITH, "with", 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(INT, "int", 0) \
K(INT32, "int32", 0) \ K(INT32, "int32", 0) \
K(INT64, "int64", 0) \ K(INT64, "int64", 0) \
@ -274,7 +276,7 @@ public:
} }
static bool IsTruncatingBinaryOp(Value op) { static bool IsTruncatingBinaryOp(Value op) {
return BIT_OR <= op && op <= ROR; return BIT_OR <= op && op <= SHR;
} }
static bool IsCompareOp(Value op) { 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) { static bool IsBitOp(Value op) {
return (BIT_OR <= op && op <= SHR) || op == BIT_NOT; return (BIT_OR <= op && op <= SHR) || op == BIT_NOT;
} }

152
libsolidity/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;
}
} }

171
libsolidity/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;
};
} }

14
test/solidityNameAndTypeResolution.cpp

@ -49,7 +49,7 @@ BOOST_AUTO_TEST_CASE(smoke_test)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 stateVariable1;\n" " uint256 stateVariable1;\n"
" function fun(uint256 arg1) { var x = 2; uint256 y = 3; x = 1; }" " function fun(uint256 arg1) { var x; uint256 y; }"
"}\n"; "}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
@ -66,8 +66,8 @@ BOOST_AUTO_TEST_CASE(double_stateVariable_declaration)
BOOST_AUTO_TEST_CASE(double_function_declaration) BOOST_AUTO_TEST_CASE(double_function_declaration)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function fun() { var x = 2; }\n" " function fun() { var x; }\n"
" function fun() { var y = 9; }\n" " function fun() { var x; }\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
} }
@ -75,7 +75,7 @@ BOOST_AUTO_TEST_CASE(double_function_declaration)
BOOST_AUTO_TEST_CASE(double_variable_declaration) BOOST_AUTO_TEST_CASE(double_variable_declaration)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" function f() { uint256 x = 9; if (true) { uint256 x = 2;} x = 3; }\n" " function f() { uint256 x; if (true) { uint256 x; } }\n"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
} }
@ -84,7 +84,7 @@ BOOST_AUTO_TEST_CASE(name_shadowing)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 variable;\n" " uint256 variable;\n"
" function f() { uint8 variable = 2; }" " function f() { uint32 variable ; }"
"}\n"; "}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
@ -93,7 +93,7 @@ BOOST_AUTO_TEST_CASE(name_references)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 variable;\n" " uint256 variable;\n"
" function f() { variable = 2; f(); test; }" " function f(uint256 arg) returns (uint out) { f(variable); test; out; }"
"}\n"; "}\n";
BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text)); BOOST_CHECK_NO_THROW(parseTextAndResolveNames(text));
} }
@ -102,7 +102,7 @@ BOOST_AUTO_TEST_CASE(undeclared_name)
{ {
char const* text = "contract test {\n" char const* text = "contract test {\n"
" uint256 variable;\n" " uint256 variable;\n"
" function f() { notfound = 2; }" " function f(uint256 arg) { f(notfound); }"
"}\n"; "}\n";
BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception); BOOST_CHECK_THROW(parseTextAndResolveNames(text), std::exception);
} }

Loading…
Cancel
Save