mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Type system, not yet complete.
This commit is contained in:
parent
bdac5c7b4b
commit
89b794f1dc
244
AST.cpp
244
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;
|
||||
}
|
||||
|
||||
} }
|
||||
|
123
AST.h
123
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; }
|
||||
|
@ -32,6 +32,7 @@ namespace dev {
|
||||
namespace solidity {
|
||||
|
||||
class ASTNode;
|
||||
class Declaration;
|
||||
class ContractDefinition;
|
||||
class StructDefinition;
|
||||
class ParameterList;
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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);
|
||||
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());
|
||||
registerVariablesInFunction(_function);
|
||||
resolveReferencesInFunction(*_function.getReturnParameterList(), _function.getBody());
|
||||
_function.getBody().checkTypeRequirements();
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::handleFunctionBody(Block& _functionBody)
|
||||
{
|
||||
registerVariablesInFunction(_functionBody);
|
||||
resolveReferencesInFunction(_functionBody);
|
||||
}
|
||||
|
||||
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, _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;
|
||||
};
|
||||
|
||||
ReferencesResolver referencesResolver(*this);
|
||||
_functionBody.accept(referencesResolver);
|
||||
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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
37
Parser.cpp
37
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();
|
||||
|
6
Parser.h
6
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();
|
||||
|
48
Scope.cpp
Normal file
48
Scope.cpp
Normal file
@ -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
Scope.h
24
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;
|
||||
};
|
||||
|
||||
} }
|
||||
|
13
Token.h
13
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;
|
||||
}
|
||||
|
152
Types.cpp
Normal file
152
Types.cpp
Normal file
@ -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
Types.h
Normal file
171
Types.h
Normal file
@ -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;
|
||||
};
|
||||
|
||||
|
||||
} }
|
Loading…
Reference in New Issue
Block a user