mirror of
https://github.com/ethereum/solidity
synced 2023-10-03 13:03:40 +00:00
Merge pull request #85 from chriseth/warnings
Support mulitple errors and warnings.
This commit is contained in:
commit
efdea76d5e
1059
libsolidity/AST.cpp
1059
libsolidity/AST.cpp
File diff suppressed because it is too large
Load Diff
@ -33,6 +33,7 @@
|
||||
#include <libsolidity/Token.h>
|
||||
#include <libsolidity/Types.h>
|
||||
#include <libsolidity/Exceptions.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -51,9 +52,8 @@ class ASTConstVisitor;
|
||||
class ASTNode: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit ASTNode(SourceLocation const& _location): m_location(_location) {}
|
||||
|
||||
virtual ~ASTNode() {}
|
||||
explicit ASTNode(SourceLocation const& _location);
|
||||
virtual ~ASTNode();
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) = 0;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const = 0;
|
||||
@ -77,6 +77,9 @@ public:
|
||||
/// the given description
|
||||
TypeError createTypeError(std::string const& _description) const;
|
||||
|
||||
///@todo make this const-safe by providing a different way to access the annotation
|
||||
virtual ASTAnnotation& annotation() const;
|
||||
|
||||
///@{
|
||||
///@name equality operators
|
||||
/// Equality relies on the fact that nodes cannot be copied.
|
||||
@ -84,6 +87,10 @@ public:
|
||||
bool operator!=(ASTNode const& _other) const { return !operator==(_other); }
|
||||
///@}
|
||||
|
||||
protected:
|
||||
/// Annotation - is specialised in derived classes, is created upon request (because of polymorphism).
|
||||
mutable ASTAnnotation* m_annotation = nullptr;
|
||||
|
||||
private:
|
||||
SourceLocation m_location;
|
||||
};
|
||||
@ -151,12 +158,14 @@ public:
|
||||
Declaration const* scope() const { return m_scope; }
|
||||
void setScope(Declaration const* _scope) { m_scope = _scope; }
|
||||
|
||||
virtual bool isLValue() const { return false; }
|
||||
virtual bool isPartOfExternalInterface() const { return false; }
|
||||
|
||||
/// @returns the type of expressions referencing this declaration.
|
||||
/// The current contract has to be given since this context can change the type, especially of
|
||||
/// contract types.
|
||||
/// This can only be called once types of variable declarations have already been resolved.
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract = nullptr) const = 0;
|
||||
virtual bool isLValue() const { return false; }
|
||||
virtual bool isPartOfExternalInterface() const { return false; }
|
||||
|
||||
protected:
|
||||
virtual Visibility defaultVisibility() const { return Visibility::Public; }
|
||||
@ -205,8 +214,7 @@ public:
|
||||
explicit ImplementationOptional(bool _implemented): m_implemented(_implemented) {}
|
||||
|
||||
/// @return whether this node is fully implemented or not
|
||||
bool isFullyImplemented() const { return m_implemented; }
|
||||
void setFullyImplemented(bool _implemented) { m_implemented = _implemented; }
|
||||
bool isImplemented() const { return m_implemented; }
|
||||
|
||||
protected:
|
||||
bool m_implemented;
|
||||
@ -219,7 +227,7 @@ protected:
|
||||
* document order. It first visits all struct declarations, then all variable declarations and
|
||||
* finally all function declarations.
|
||||
*/
|
||||
class ContractDefinition: public Declaration, public Documented, public ImplementationOptional
|
||||
class ContractDefinition: public Declaration, public Documented
|
||||
{
|
||||
public:
|
||||
ContractDefinition(
|
||||
@ -237,7 +245,6 @@ public:
|
||||
):
|
||||
Declaration(_location, _name),
|
||||
Documented(_documentation),
|
||||
ImplementationOptional(true),
|
||||
m_baseContracts(_baseContracts),
|
||||
m_definedStructs(_definedStructs),
|
||||
m_definedEnums(_definedEnums),
|
||||
@ -261,24 +268,14 @@ public:
|
||||
std::vector<ASTPointer<EventDefinition>> const& interfaceEvents() const;
|
||||
bool isLibrary() const { return m_isLibrary; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
/// Checks that there are no illegal overrides, that the constructor does not have a "returns"
|
||||
/// and calls checkTypeRequirements on all its functions.
|
||||
void checkTypeRequirements();
|
||||
|
||||
/// @returns a map of canonical function signatures to FunctionDefinitions
|
||||
/// as intended for use by the ABI.
|
||||
std::map<FixedHash<4>, FunctionTypePointer> interfaceFunctions() const;
|
||||
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
|
||||
|
||||
/// @returns a list of the inheritable members of this contract
|
||||
std::vector<Declaration const*> const& inheritableMembers() const;
|
||||
|
||||
/// List of all (direct and indirect) base contracts in order from derived to base, including
|
||||
/// the contract itself. Available after name resolution
|
||||
std::vector<ContractDefinition const*> const& linearizedBaseContracts() const { return m_linearizedBaseContracts; }
|
||||
void setLinearizedBaseContracts(std::vector<ContractDefinition const*> const& _bases) { m_linearizedBaseContracts = _bases; }
|
||||
|
||||
/// Returns the constructor or nullptr if no constructor was specified.
|
||||
FunctionDefinition const* constructor() const;
|
||||
/// Returns the fallback function or nullptr if no fallback function was specified.
|
||||
@ -290,21 +287,11 @@ public:
|
||||
std::string const& devDocumentation() const;
|
||||
void setDevDocumentation(std::string const& _devDocumentation);
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
virtual ContractDefinitionAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
/// Checks that two functions defined in this contract with the same name have different
|
||||
/// arguments and that there is at most one constructor.
|
||||
void checkDuplicateFunctions() const;
|
||||
void checkIllegalOverrides() const;
|
||||
void checkAbstractFunctions();
|
||||
void checkAbstractConstructors();
|
||||
/// Checks that different functions with external visibility end up having different
|
||||
/// external argument types (i.e. different signature).
|
||||
void checkExternalTypeClashes() const;
|
||||
/// Checks that all requirements for a library are fulfilled if this is a library.
|
||||
void checkLibraryRequirements() const;
|
||||
|
||||
std::vector<std::pair<FixedHash<4>, FunctionTypePointer>> const& interfaceFunctionList() const;
|
||||
|
||||
std::vector<ASTPointer<InheritanceSpecifier>> m_baseContracts;
|
||||
std::vector<ASTPointer<StructDefinition>> m_definedStructs;
|
||||
std::vector<ASTPointer<EnumDefinition>> m_definedEnums;
|
||||
@ -327,18 +314,19 @@ private:
|
||||
class InheritanceSpecifier: public ASTNode
|
||||
{
|
||||
public:
|
||||
InheritanceSpecifier(SourceLocation const& _location, ASTPointer<Identifier> const& _baseName,
|
||||
std::vector<ASTPointer<Expression>> _arguments):
|
||||
InheritanceSpecifier(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<Identifier> const& _baseName,
|
||||
std::vector<ASTPointer<Expression>> _arguments
|
||||
):
|
||||
ASTNode(_location), m_baseName(_baseName), m_arguments(_arguments) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
ASTPointer<Identifier> const& name() const { return m_baseName; }
|
||||
Identifier const& name() const { return *m_baseName; }
|
||||
std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
|
||||
|
||||
void checkTypeRequirements();
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_baseName;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
@ -359,32 +347,27 @@ public:
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> const& members() const { return m_members; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const*) const override;
|
||||
|
||||
/// Checks that the members do not include any recursive structs and have valid types
|
||||
/// (e.g. no functions).
|
||||
void checkValidityOfMembers() const;
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
private:
|
||||
void checkMemberTypes() const;
|
||||
void checkRecursion() const;
|
||||
|
||||
std::vector<ASTPointer<VariableDeclaration>> m_members;
|
||||
};
|
||||
|
||||
class EnumDefinition: public Declaration
|
||||
{
|
||||
public:
|
||||
EnumDefinition(SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
std::vector<ASTPointer<EnumValue>> const& _members):
|
||||
EnumDefinition(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _name,
|
||||
std::vector<ASTPointer<EnumValue>> const& _members
|
||||
):
|
||||
Declaration(_location, _name), m_members(_members) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
std::vector<ASTPointer<EnumValue>> const& members() const { return m_members; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const*) const override;
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
private:
|
||||
std::vector<ASTPointer<EnumValue>> m_members;
|
||||
@ -395,14 +378,14 @@ private:
|
||||
*/
|
||||
class EnumValue: public Declaration
|
||||
{
|
||||
public:
|
||||
EnumValue(SourceLocation const& _location,
|
||||
ASTPointer<ASTString> const& _name):
|
||||
public:
|
||||
EnumValue(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
|
||||
Declaration(_location, _name) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual TypePointer type(ContractDefinition const* = nullptr) const override;
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -491,17 +474,15 @@ public:
|
||||
{
|
||||
return Declaration::isVisibleInContract() && !isConstructor() && !name().empty();
|
||||
}
|
||||
virtual TypePointer type(ContractDefinition const*) const override;
|
||||
virtual bool isPartOfExternalInterface() const override { return isPublic() && !m_isConstructor && !name().empty(); }
|
||||
|
||||
/// Checks that all parameters have allowed types and calls checkTypeRequirements on the body.
|
||||
void checkTypeRequirements();
|
||||
|
||||
/// @returns the external signature of the function
|
||||
/// That consists of the name of the function followed by the types of the
|
||||
/// arguments separated by commas all enclosed in parentheses without any spaces.
|
||||
std::string externalSignature() const;
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
private:
|
||||
bool m_isConstructor;
|
||||
bool m_isDeclaredConst;
|
||||
@ -540,28 +521,29 @@ public:
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
TypeName* typeName() { return m_typeName.get(); }
|
||||
TypeName* typeName() const { return m_typeName.get(); }
|
||||
ASTPointer<Expression> const& value() const { return m_value; }
|
||||
|
||||
/// 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.
|
||||
TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; }
|
||||
void setType(std::shared_ptr<Type const> const& _type) { m_type = _type; }
|
||||
|
||||
virtual bool isLValue() const override;
|
||||
virtual bool isPartOfExternalInterface() const override { return isPublic(); }
|
||||
|
||||
void checkTypeRequirements();
|
||||
bool isLocalVariable() const { return !!dynamic_cast<FunctionDefinition const*>(scope()); }
|
||||
/// @returns true if this variable is a parameter or return parameter of a function.
|
||||
bool isCallableParameter() const;
|
||||
/// @returns true if this variable is a parameter (not return parameter) of an external function.
|
||||
bool isExternalCallableParameter() const;
|
||||
/// @returns true if the type of the variable does not need to be specified, i.e. it is declared
|
||||
/// in the body of a function or modifier.
|
||||
bool canHaveAutoType() const;
|
||||
bool isStateVariable() const { return m_isStateVariable; }
|
||||
bool isIndexed() const { return m_isIndexed; }
|
||||
bool isConstant() const { return m_isConstant; }
|
||||
Location referenceLocation() const { return m_location; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
virtual VariableDeclarationAnnotation& annotation() const override;
|
||||
|
||||
protected:
|
||||
Visibility defaultVisibility() const override { return Visibility::Internal; }
|
||||
|
||||
@ -572,8 +554,6 @@ private:
|
||||
bool m_isIndexed; ///< Whether this is an indexed variable (used by events).
|
||||
bool m_isConstant; ///< Whether the variable is a compile-time constant.
|
||||
Location m_location; ///< Location of the variable if it is of reference type.
|
||||
|
||||
std::shared_ptr<Type const> m_type; ///< derived type, initially empty
|
||||
};
|
||||
|
||||
/**
|
||||
@ -600,9 +580,7 @@ public:
|
||||
|
||||
Block const& body() const { return *m_body; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* = nullptr) const override;
|
||||
|
||||
void checkTypeRequirements();
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
private:
|
||||
ASTPointer<Block> m_body;
|
||||
@ -614,8 +592,11 @@ private:
|
||||
class ModifierInvocation: public ASTNode
|
||||
{
|
||||
public:
|
||||
ModifierInvocation(SourceLocation const& _location, ASTPointer<Identifier> const& _name,
|
||||
std::vector<ASTPointer<Expression>> _arguments):
|
||||
ModifierInvocation(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<Identifier> const& _name,
|
||||
std::vector<ASTPointer<Expression>> _arguments
|
||||
):
|
||||
ASTNode(_location), m_modifierName(_name), m_arguments(_arguments) {}
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
@ -624,9 +605,6 @@ public:
|
||||
ASTPointer<Identifier> const& name() const { return m_modifierName; }
|
||||
std::vector<ASTPointer<Expression>> const& arguments() const { return m_arguments; }
|
||||
|
||||
/// @param _bases is the list of base contracts for base constructor calls. For modifiers an empty vector should be passed.
|
||||
void checkTypeRequirements(std::vector<ContractDefinition const*> const& _bases);
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_modifierName;
|
||||
std::vector<ASTPointer<Expression>> m_arguments;
|
||||
@ -656,12 +634,7 @@ public:
|
||||
|
||||
bool isAnonymous() const { return m_anonymous; }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* = nullptr) const override
|
||||
{
|
||||
return std::make_shared<FunctionType>(*this);
|
||||
}
|
||||
|
||||
void checkTypeRequirements();
|
||||
virtual TypePointer type(ContractDefinition const* m_currentContract) const override;
|
||||
|
||||
private:
|
||||
bool m_anonymous = false;
|
||||
@ -681,7 +654,7 @@ public:
|
||||
virtual void accept(ASTConstVisitor&) const override { BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||
<< errinfo_comment("MagicVariableDeclaration used inside real AST.")); }
|
||||
|
||||
virtual TypePointer type(ContractDefinition const* = nullptr) const override { return m_type; }
|
||||
virtual TypePointer type(ContractDefinition const*) const override { return m_type; }
|
||||
|
||||
private:
|
||||
std::shared_ptr<Type const> m_type;
|
||||
@ -700,10 +673,7 @@ public:
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
/// Retrieve the element of the type hierarchy this node refers to. Can return an empty shared
|
||||
/// pointer until the types have been resolved using the @ref NameAndTypeResolver.
|
||||
/// If it returns an empty shared pointer after that, this indicates that the type was not found.
|
||||
virtual std::shared_ptr<Type const> toType() = 0;
|
||||
virtual TypeNameAnnotation& annotation() const override;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -720,7 +690,6 @@ public:
|
||||
}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual std::shared_ptr<Type const> toType() override { return Type::fromElementaryTypeName(m_type); }
|
||||
|
||||
Token::Value typeName() const { return m_type; }
|
||||
|
||||
@ -735,19 +704,16 @@ class UserDefinedTypeName: public TypeName
|
||||
{
|
||||
public:
|
||||
UserDefinedTypeName(SourceLocation const& _location, ASTPointer<ASTString> const& _name):
|
||||
TypeName(_location), m_name(_name), m_referencedDeclaration(nullptr) {}
|
||||
TypeName(_location), m_name(_name) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual std::shared_ptr<Type const> toType() override { return Type::fromUserDefinedTypeName(*this); }
|
||||
|
||||
ASTString const& name() const { return *m_name; }
|
||||
void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; }
|
||||
Declaration const* referencedDeclaration() const { return m_referencedDeclaration; }
|
||||
|
||||
virtual UserDefinedTypeNameAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<ASTString> m_name;
|
||||
|
||||
Declaration const* m_referencedDeclaration;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -761,7 +727,6 @@ public:
|
||||
TypeName(_location), m_keyType(_keyType), m_valueType(_valueType) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual TypePointer toType() override { return Type::fromMapping(*m_keyType, *m_valueType); }
|
||||
|
||||
ElementaryTypeName const& keyType() const { return *m_keyType; }
|
||||
TypeName const& valueType() const { return *m_valueType; }
|
||||
@ -782,7 +747,6 @@ public:
|
||||
TypeName(_location), m_baseType(_baseType), m_length(_length) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual std::shared_ptr<Type const> toType() override { return Type::fromArrayTypeName(*m_baseType, m_length.get()); }
|
||||
|
||||
TypeName const& baseType() const { return *m_baseType; }
|
||||
Expression const* length() const { return m_length.get(); }
|
||||
@ -805,11 +769,6 @@ class Statement: public ASTNode
|
||||
{
|
||||
public:
|
||||
explicit Statement(SourceLocation const& _location): ASTNode(_location) {}
|
||||
|
||||
/// Check all type requirements, throws exception if some requirement is not met.
|
||||
/// This includes checking that operators are applicable to their arguments but also that
|
||||
/// the number of function call arguments matches the number of formal parameters and so forth.
|
||||
virtual void checkTypeRequirements() = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -823,8 +782,6 @@ public:
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
private:
|
||||
std::vector<ASTPointer<Statement>> m_statements;
|
||||
};
|
||||
@ -840,8 +797,6 @@ public:
|
||||
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
|
||||
virtual void checkTypeRequirements() override { }
|
||||
};
|
||||
|
||||
/**
|
||||
@ -857,7 +812,6 @@ public:
|
||||
m_condition(_condition), m_trueBody(_trueBody), m_falseBody(_falseBody) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
Expression const& condition() const { return *m_condition; }
|
||||
Statement const& trueStatement() const { return *m_trueBody; }
|
||||
@ -887,7 +841,6 @@ public:
|
||||
BreakableStatement(_location), m_condition(_condition), m_body(_body) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
Expression const& condition() const { return *m_condition; }
|
||||
Statement const& body() const { return *m_body; }
|
||||
@ -903,19 +856,21 @@ private:
|
||||
class ForStatement: public BreakableStatement
|
||||
{
|
||||
public:
|
||||
ForStatement(SourceLocation const& _location,
|
||||
ASTPointer<Statement> const& _initExpression,
|
||||
ASTPointer<Expression> const& _conditionExpression,
|
||||
ASTPointer<ExpressionStatement> const& _loopExpression,
|
||||
ASTPointer<Statement> const& _body):
|
||||
ForStatement(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<Statement> const& _initExpression,
|
||||
ASTPointer<Expression> const& _conditionExpression,
|
||||
ASTPointer<ExpressionStatement> const& _loopExpression,
|
||||
ASTPointer<Statement> const& _body
|
||||
):
|
||||
BreakableStatement(_location),
|
||||
m_initExpression(_initExpression),
|
||||
m_condExpression(_conditionExpression),
|
||||
m_loopExpression(_loopExpression),
|
||||
m_body(_body) {}
|
||||
m_body(_body)
|
||||
{}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
Statement const* initializationExpression() const { return m_initExpression.get(); }
|
||||
Expression const* condition() const { return m_condExpression.get(); }
|
||||
@ -939,7 +894,6 @@ public:
|
||||
Continue(SourceLocation const& _location): Statement(_location) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override {}
|
||||
};
|
||||
|
||||
class Break: public Statement
|
||||
@ -948,27 +902,22 @@ public:
|
||||
Break(SourceLocation const& _location): Statement(_location) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override {}
|
||||
};
|
||||
|
||||
class Return: public Statement
|
||||
{
|
||||
public:
|
||||
Return(SourceLocation const& _location, ASTPointer<Expression> _expression):
|
||||
Statement(_location), m_expression(_expression), m_returnParameters(nullptr) {}
|
||||
Statement(_location), m_expression(_expression) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
void setFunctionReturnParameters(ParameterList const* _parameters) { m_returnParameters = _parameters; }
|
||||
ParameterList const* functionReturnParameters() const { return m_returnParameters; }
|
||||
Expression const* expression() const { return m_expression.get(); }
|
||||
|
||||
virtual ReturnAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_expression; ///< value to return, optional
|
||||
|
||||
/// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver.
|
||||
ParameterList const* m_returnParameters;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -980,7 +929,6 @@ public:
|
||||
Throw(SourceLocation const& _location): Statement(_location) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override {};
|
||||
};
|
||||
|
||||
/**
|
||||
@ -995,7 +943,6 @@ public:
|
||||
Statement(_location), m_variable(_variable) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
VariableDeclaration const& declaration() const { return *m_variable; }
|
||||
Expression const* expression() const { return m_variable->value().get(); }
|
||||
@ -1014,7 +961,6 @@ public:
|
||||
Statement(_location), m_expression(_expression) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements() override;
|
||||
|
||||
Expression const& expression() const { return *m_expression; }
|
||||
|
||||
@ -1036,32 +982,8 @@ class Expression: public ASTNode
|
||||
{
|
||||
public:
|
||||
Expression(SourceLocation const& _location): ASTNode(_location) {}
|
||||
/// Performs type checking after which m_type should be set.
|
||||
/// @arg _argumentTypes if set, provides the argument types for the case that this expression
|
||||
/// is used in the context of a call, used for function overload resolution.
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) = 0;
|
||||
|
||||
std::shared_ptr<Type const> const& type() const { return m_type; }
|
||||
bool isLValue() const { return m_isLValue; }
|
||||
|
||||
/// Helper function, infer the type via @ref checkTypeRequirements and then check that it
|
||||
/// is implicitly convertible to @a _expectedType. If not, throw exception.
|
||||
void expectType(Type const& _expectedType);
|
||||
/// Checks that this expression is an lvalue and also registers that an address and
|
||||
/// not a value is generated during compilation. Can be called after checkTypeRequirements()
|
||||
/// by an enclosing expression.
|
||||
void requireLValue();
|
||||
/// Returns true if @a requireLValue was previously called on this expression.
|
||||
bool lvalueRequested() const { return m_lvalueRequested; }
|
||||
|
||||
protected:
|
||||
//! Inferred type of the expression, only filled after a call to checkTypeRequirements().
|
||||
std::shared_ptr<Type const> m_type;
|
||||
//! If this expression is an lvalue (i.e. something that can be assigned to).
|
||||
//! This is set during calls to @a checkTypeRequirements()
|
||||
bool m_isLValue = false;
|
||||
//! Whether the outer expression requested the address (true) or the value (false) of this expression.
|
||||
bool m_lvalueRequested = false;
|
||||
ExpressionAnnotation& annotation() const override;
|
||||
};
|
||||
|
||||
/// Assignment, can also be a compound assignment.
|
||||
@ -1069,16 +991,21 @@ protected:
|
||||
class Assignment: public Expression
|
||||
{
|
||||
public:
|
||||
Assignment(SourceLocation const& _location, ASTPointer<Expression> const& _leftHandSide,
|
||||
Token::Value _assignmentOperator, ASTPointer<Expression> const& _rightHandSide):
|
||||
Expression(_location), m_leftHandSide(_leftHandSide),
|
||||
m_assigmentOperator(_assignmentOperator), m_rightHandSide(_rightHandSide)
|
||||
Assignment(
|
||||
SourceLocation const& _location,
|
||||
ASTPointer<Expression> const& _leftHandSide,
|
||||
Token::Value _assignmentOperator,
|
||||
ASTPointer<Expression> const& _rightHandSide
|
||||
):
|
||||
Expression(_location),
|
||||
m_leftHandSide(_leftHandSide),
|
||||
m_assigmentOperator(_assignmentOperator),
|
||||
m_rightHandSide(_rightHandSide)
|
||||
{
|
||||
solAssert(Token::isAssignmentOp(_assignmentOperator), "");
|
||||
}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Expression const& leftHandSide() const { return *m_leftHandSide; }
|
||||
Token::Value assignmentOperator() const { return m_assigmentOperator; }
|
||||
@ -1106,7 +1033,6 @@ public:
|
||||
}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Token::Value getOperator() const { return m_operator; }
|
||||
bool isPrefixOperation() const { return m_isPrefix; }
|
||||
@ -1133,21 +1059,17 @@ public:
|
||||
}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Expression const& leftExpression() const { return *m_left; }
|
||||
Expression const& rightExpression() const { return *m_right; }
|
||||
Token::Value getOperator() const { return m_operator; }
|
||||
Type const& commonType() const { return *m_commonType; }
|
||||
|
||||
BinaryOperationAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_left;
|
||||
Token::Value m_operator;
|
||||
ASTPointer<Expression> m_right;
|
||||
|
||||
/// The common type that is used for the operation, not necessarily the result type (e.g. for
|
||||
/// comparisons, this is always bool).
|
||||
std::shared_ptr<Type const> m_commonType;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1161,17 +1083,12 @@ public:
|
||||
Expression(_location), m_expression(_expression), m_arguments(_arguments), m_names(_names) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Expression const& expression() const { return *m_expression; }
|
||||
std::vector<ASTPointer<Expression const>> arguments() const { return {m_arguments.begin(), m_arguments.end()}; }
|
||||
std::vector<ASTPointer<ASTString>> const& names() const { return m_names; }
|
||||
|
||||
/// @returns true if this is not an actual function call, but an explicit type conversion.
|
||||
/// Returns false for struct constructor calls.
|
||||
bool isTypeConversion() const;
|
||||
/// @return true if this is a constructor call for a struct, i.e. StructName(...).
|
||||
bool isStructConstructorCall() const;
|
||||
virtual FunctionCallAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_expression;
|
||||
@ -1189,15 +1106,11 @@ public:
|
||||
Expression(_location), m_contractName(_contractName) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
/// Returns the referenced contract. Can only be called after type checking.
|
||||
ContractDefinition const* contract() const { solAssert(m_contract, ""); return m_contract; }
|
||||
Identifier const& contractName() const { return *m_contractName; }
|
||||
|
||||
private:
|
||||
ASTPointer<Identifier> m_contractName;
|
||||
|
||||
ContractDefinition const* m_contract = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1213,18 +1126,12 @@ public:
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
Expression const& expression() const { return *m_expression; }
|
||||
ASTString const& memberName() const { return *m_memberName; }
|
||||
/// @returns the declaration referenced by this expression. Might return nullptr even if the
|
||||
/// expression is valid, e.g. if the member does not correspond to an AST node.
|
||||
Declaration const* referencedDeclaration() const { return m_referencedDeclaration; }
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
virtual MemberAccessAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<Expression> m_expression;
|
||||
ASTPointer<ASTString> m_memberName;
|
||||
|
||||
/// Pointer to the referenced declaration, this is sometimes needed to resolve function over
|
||||
/// loads in the type-checking phase.
|
||||
Declaration const* m_referencedDeclaration = nullptr;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1238,7 +1145,6 @@ public:
|
||||
Expression(_location), m_base(_base), m_index(_index) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Expression const& baseExpression() const { return *m_base; }
|
||||
Expression const* indexExpression() const { return m_index.get(); }
|
||||
@ -1268,44 +1174,13 @@ public:
|
||||
PrimaryExpression(_location), m_name(_name) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
ASTString const& name() const { return *m_name; }
|
||||
|
||||
void setReferencedDeclaration(
|
||||
Declaration const& _referencedDeclaration,
|
||||
ContractDefinition const* _currentContract = nullptr
|
||||
)
|
||||
{
|
||||
m_referencedDeclaration = &_referencedDeclaration;
|
||||
m_contractScope = _currentContract;
|
||||
}
|
||||
Declaration const& referencedDeclaration() const;
|
||||
|
||||
/// Stores a set of possible declarations referenced by this identifier. Has to be resolved
|
||||
/// providing argument types using overloadResolution before the referenced declaration
|
||||
/// is accessed.
|
||||
void setOverloadedDeclarations(std::vector<Declaration const*> const& _declarations)
|
||||
{
|
||||
m_overloadedDeclarations = _declarations;
|
||||
}
|
||||
|
||||
/// Tries to find exactly one of the possible referenced declarations provided the given
|
||||
/// argument types in a call context.
|
||||
void overloadResolution(TypePointers const& _argumentTypes);
|
||||
|
||||
ContractDefinition const* contractScope() const { return m_contractScope; }
|
||||
virtual IdentifierAnnotation& annotation() const override;
|
||||
|
||||
private:
|
||||
ASTPointer<ASTString> m_name;
|
||||
|
||||
/// Declaration the name refers to.
|
||||
Declaration const* m_referencedDeclaration = nullptr;
|
||||
/// Stores a reference to the current contract. This is needed because types of base contracts
|
||||
/// change depending on the context.
|
||||
ContractDefinition const* m_contractScope = nullptr;
|
||||
/// A vector of overloaded declarations, right now only FunctionDefinition has overloaded declarations.
|
||||
std::vector<Declaration const*> m_overloadedDeclarations;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1323,7 +1198,6 @@ public:
|
||||
}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Token::Value typeToken() const { return m_typeToken; }
|
||||
|
||||
@ -1357,7 +1231,6 @@ public:
|
||||
PrimaryExpression(_location), m_token(_token), m_value(_value), m_subDenomination(_sub) {}
|
||||
virtual void accept(ASTVisitor& _visitor) override;
|
||||
virtual void accept(ASTConstVisitor& _visitor) const override;
|
||||
virtual void checkTypeRequirements(TypePointers const* _argumentTypes) override;
|
||||
|
||||
Token::Value token() const { return m_token; }
|
||||
/// @returns the non-parsed value of the literal
|
||||
|
28
libsolidity/ASTAnnotations.cpp
Normal file
28
libsolidity/ASTAnnotations.cpp
Normal file
@ -0,0 +1,28 @@
|
||||
/*
|
||||
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 2015
|
||||
* Object containing the type and other annotations for the AST nodes.
|
||||
*/
|
||||
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
123
libsolidity/ASTAnnotations.h
Normal file
123
libsolidity/ASTAnnotations.h
Normal file
@ -0,0 +1,123 @@
|
||||
/*
|
||||
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 2015
|
||||
* Object containing the type and other annotations for the AST nodes.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <libsolidity/ASTForward.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class Type;
|
||||
using TypePointer = std::shared_ptr<Type const>;
|
||||
|
||||
struct ASTAnnotation
|
||||
{
|
||||
virtual ~ASTAnnotation() {}
|
||||
};
|
||||
|
||||
struct ContractDefinitionAnnotation: ASTAnnotation
|
||||
{
|
||||
/// Whether all functions are implemented.
|
||||
bool isFullyImplemented = true;
|
||||
/// List of all (direct and indirect) base contracts in order from derived to
|
||||
/// base, including the contract itself.
|
||||
std::vector<ContractDefinition const*> linearizedBaseContracts;
|
||||
};
|
||||
|
||||
struct VariableDeclarationAnnotation: ASTAnnotation
|
||||
{
|
||||
/// Type of variable (type of identifier referencing this variable).
|
||||
TypePointer type;
|
||||
};
|
||||
|
||||
struct ReturnAnnotation: ASTAnnotation
|
||||
{
|
||||
/// Reference to the return parameters of the function.
|
||||
ParameterList const* functionReturnParameters = nullptr;
|
||||
};
|
||||
|
||||
struct TypeNameAnnotation: ASTAnnotation
|
||||
{
|
||||
/// Type declared by this type name, i.e. type of a variable where this type name is used.
|
||||
/// Set during reference resolution stage.
|
||||
TypePointer type;
|
||||
};
|
||||
|
||||
struct UserDefinedTypeNameAnnotation: TypeNameAnnotation
|
||||
{
|
||||
/// Referenced declaration, set during reference resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
};
|
||||
|
||||
struct ExpressionAnnotation: ASTAnnotation
|
||||
{
|
||||
/// Inferred type of the expression.
|
||||
TypePointer type;
|
||||
/// Whether it is an LValue (i.e. something that can be assigned to).
|
||||
bool isLValue = false;
|
||||
/// Whether the expression is used in a context where the LValue is actually required.
|
||||
bool lValueRequested = false;
|
||||
/// Types of arguments if the expression is a function that is called - used
|
||||
/// for overload resolution.
|
||||
std::shared_ptr<std::vector<TypePointer>> argumentTypes;
|
||||
};
|
||||
|
||||
struct IdentifierAnnotation: ExpressionAnnotation
|
||||
{
|
||||
/// Stores a reference to the current contract.
|
||||
/// This is needed because types of base contracts change depending on the context.
|
||||
ContractDefinition const* contractScope = nullptr;
|
||||
/// Referenced declaration, set at latest during overload resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
/// List of possible declarations it could refer to.
|
||||
std::vector<Declaration const*> overloadedDeclarations;
|
||||
};
|
||||
|
||||
struct MemberAccessAnnotation: ExpressionAnnotation
|
||||
{
|
||||
/// Referenced declaration, set at latest during overload resolution stage.
|
||||
Declaration const* referencedDeclaration = nullptr;
|
||||
};
|
||||
|
||||
struct BinaryOperationAnnotation: ExpressionAnnotation
|
||||
{
|
||||
/// The common type that is used for the operation, not necessarily the result type (which
|
||||
/// e.g. for comparisons is bool).
|
||||
TypePointer commonType;
|
||||
};
|
||||
|
||||
struct FunctionCallAnnotation: ExpressionAnnotation
|
||||
{
|
||||
/// Whether this is an explicit type conversion.
|
||||
bool isTypeConversion = false;
|
||||
/// Whether this is a struct constructor call.
|
||||
bool isStructConstructorCall = false;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -232,19 +232,19 @@ bool ASTJsonConverter::visit(UnaryOperation const& _node)
|
||||
|
||||
bool ASTJsonConverter::visit(BinaryOperation const& _node)
|
||||
{
|
||||
addJsonNode("BinaryOperation",
|
||||
{ make_pair("operator", Token::toString(_node.getOperator())),
|
||||
make_pair("type", type(_node))},
|
||||
true);
|
||||
addJsonNode("BinaryOperation", {
|
||||
make_pair("operator", Token::toString(_node.getOperator())),
|
||||
make_pair("type", type(_node))
|
||||
}, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ASTJsonConverter::visit(FunctionCall const& _node)
|
||||
{
|
||||
addJsonNode("FunctionCall",
|
||||
{ make_pair("type_conversion", boost::lexical_cast<std::string>(_node.isTypeConversion())),
|
||||
make_pair("type", type(_node)) },
|
||||
true);
|
||||
addJsonNode("FunctionCall", {
|
||||
make_pair("type_conversion", boost::lexical_cast<std::string>(_node.annotation().isTypeConversion)),
|
||||
make_pair("type", type(_node))
|
||||
}, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -441,7 +441,7 @@ void ASTJsonConverter::process()
|
||||
|
||||
string ASTJsonConverter::type(Expression const& _expression)
|
||||
{
|
||||
return (_expression.type()) ? _expression.type()->toString() : "Unknown";
|
||||
return _expression.annotation().type ? _expression.annotation().type->toString() : "Unknown";
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
#include <libsolidity/Exceptions.h>
|
||||
#include <libsolidity/Utils.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
#include <json/json.h>
|
||||
|
||||
namespace dev
|
||||
@ -41,7 +42,7 @@ class ASTJsonConverter: public ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// Create a converter to JSON for the given abstract syntax tree.
|
||||
ASTJsonConverter(ASTNode const& _ast);
|
||||
explicit ASTJsonConverter(ASTNode const& _ast);
|
||||
/// Output the json representation of the AST to _stream.
|
||||
void print(std::ostream& _stream);
|
||||
Json::Value const& json();
|
||||
|
@ -62,7 +62,7 @@ bool ASTPrinter::visit(ContractDefinition const& _node)
|
||||
|
||||
bool ASTPrinter::visit(InheritanceSpecifier const& _node)
|
||||
{
|
||||
writeLine("InheritanceSpecifier \"" + _node.name()->name() + "\"");
|
||||
writeLine("InheritanceSpecifier \"" + _node.name().name() + "\"");
|
||||
printSourcePart(_node);
|
||||
return goDeeper();
|
||||
}
|
||||
@ -530,8 +530,8 @@ void ASTPrinter::printSourcePart(ASTNode const& _node)
|
||||
|
||||
void ASTPrinter::printType(Expression const& _expression)
|
||||
{
|
||||
if (_expression.type())
|
||||
*m_ostream << indentation() << " Type: " << _expression.type()->toString() << "\n";
|
||||
if (_expression.annotation().type)
|
||||
*m_ostream << indentation() << " Type: " << _expression.annotation().type->toString() << "\n";
|
||||
else
|
||||
*m_ostream << indentation() << " Type unknown.\n";
|
||||
}
|
||||
|
@ -147,12 +147,6 @@ void StructDefinition::accept(ASTConstVisitor& _visitor) const
|
||||
_visitor.endVisit(*this);
|
||||
}
|
||||
|
||||
void StructDefinition::checkValidityOfMembers() const
|
||||
{
|
||||
checkMemberTypes();
|
||||
checkRecursion();
|
||||
}
|
||||
|
||||
void ParameterList::accept(ASTVisitor& _visitor)
|
||||
{
|
||||
if (_visitor.visit(*this))
|
||||
|
@ -53,7 +53,7 @@ void Compiler::compileContract(
|
||||
std::map<const ContractDefinition*, eth::Assembly const*> const& _contracts
|
||||
)
|
||||
{
|
||||
m_context = CompilerContext(); // clear it just in case
|
||||
m_context = CompilerContext();
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetterRunTime(m_context, _contract);
|
||||
initializeContext(_contract, _contracts);
|
||||
@ -105,9 +105,9 @@ void Compiler::initializeContext(
|
||||
map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
||||
)
|
||||
{
|
||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
||||
m_context.setCompiledContracts(_compiledContracts);
|
||||
m_context.setInheritanceHierarchy(_contract.linearizedBaseContracts());
|
||||
m_context.setInheritanceHierarchy(_contract.annotation().linearizedBaseContracts);
|
||||
CompilerUtils(m_context).initialiseFreeMemoryPointer();
|
||||
registerStateVariables(_contract);
|
||||
m_context.resetVisitedNodes(&_contract);
|
||||
}
|
||||
@ -115,14 +115,14 @@ void Compiler::initializeContext(
|
||||
void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
||||
{
|
||||
// Determine the arguments that are used for the base constructors.
|
||||
std::vector<ContractDefinition const*> const& bases = _contract.linearizedBaseContracts();
|
||||
std::vector<ContractDefinition const*> const& bases = _contract.annotation().linearizedBaseContracts;
|
||||
for (ContractDefinition const* contract: bases)
|
||||
{
|
||||
if (FunctionDefinition const* constructor = contract->constructor())
|
||||
for (auto const& modifier: constructor->modifiers())
|
||||
{
|
||||
auto baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
&modifier->name()->referencedDeclaration());
|
||||
modifier->name()->annotation().referencedDeclaration);
|
||||
if (baseContract)
|
||||
if (m_baseArguments.count(baseContract->constructor()) == 0)
|
||||
m_baseArguments[baseContract->constructor()] = &modifier->arguments();
|
||||
@ -131,7 +131,8 @@ void Compiler::appendInitAndConstructorCode(ContractDefinition const& _contract)
|
||||
for (ASTPointer<InheritanceSpecifier> const& base: contract->baseContracts())
|
||||
{
|
||||
ContractDefinition const* baseContract = dynamic_cast<ContractDefinition const*>(
|
||||
&base->name()->referencedDeclaration());
|
||||
base->name().annotation().referencedDeclaration
|
||||
);
|
||||
solAssert(baseContract, "");
|
||||
|
||||
if (m_baseArguments.count(baseContract->constructor()) == 0)
|
||||
@ -187,13 +188,13 @@ void Compiler::appendConstructor(FunctionDefinition const& _constructor)
|
||||
{
|
||||
unsigned argumentSize = 0;
|
||||
for (ASTPointer<VariableDeclaration> const& var: _constructor.parameters())
|
||||
if (var->type()->isDynamicallySized())
|
||||
if (var->annotation().type->isDynamicallySized())
|
||||
{
|
||||
argumentSize = 0;
|
||||
break;
|
||||
}
|
||||
else
|
||||
argumentSize += var->type()->calldataEncodedSize();
|
||||
argumentSize += var->annotation().type->calldataEncodedSize();
|
||||
|
||||
CompilerUtils(m_context).fetchFreeMemoryPointer();
|
||||
if (argumentSize == 0)
|
||||
@ -418,7 +419,7 @@ bool Compiler::visit(FunctionDefinition const& _function)
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.parameters())
|
||||
{
|
||||
m_context.addVariable(*variable, parametersSize);
|
||||
parametersSize -= variable->type()->sizeOnStack();
|
||||
parametersSize -= variable->annotation().type->sizeOnStack();
|
||||
}
|
||||
|
||||
for (ASTPointer<VariableDeclaration const> const& variable: _function.returnParameters())
|
||||
@ -594,9 +595,9 @@ bool Compiler::visit(Return const& _return)
|
||||
//@todo modifications are needed to make this work with functions returning multiple values
|
||||
if (Expression const* expression = _return.expression())
|
||||
{
|
||||
solAssert(_return.functionReturnParameters(), "Invalid return parameters pointer.");
|
||||
VariableDeclaration const& firstVariable = *_return.functionReturnParameters()->parameters().front();
|
||||
compileExpression(*expression, firstVariable.type());
|
||||
solAssert(_return.annotation().functionReturnParameters, "Invalid return parameters pointer.");
|
||||
VariableDeclaration const& firstVariable = *_return.annotation().functionReturnParameters->parameters().front();
|
||||
compileExpression(*expression, firstVariable.annotation().type);
|
||||
CompilerUtils(m_context).moveToStackVariable(firstVariable);
|
||||
}
|
||||
for (unsigned i = 0; i < m_stackCleanupForReturn; ++i)
|
||||
@ -619,7 +620,7 @@ bool Compiler::visit(VariableDeclarationStatement const& _variableDeclarationSta
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _variableDeclarationStatement);
|
||||
if (Expression const* expression = _variableDeclarationStatement.expression())
|
||||
{
|
||||
compileExpression(*expression, _variableDeclarationStatement.declaration().type());
|
||||
compileExpression(*expression, _variableDeclarationStatement.declaration().annotation().type);
|
||||
CompilerUtils(m_context).moveToStackVariable(_variableDeclarationStatement.declaration());
|
||||
}
|
||||
checker.check();
|
||||
@ -632,7 +633,7 @@ bool Compiler::visit(ExpressionStatement const& _expressionStatement)
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _expressionStatement);
|
||||
Expression const& expression = _expressionStatement.expression();
|
||||
compileExpression(expression);
|
||||
CompilerUtils(m_context).popStackElement(*expression.type());
|
||||
CompilerUtils(m_context).popStackElement(*expression.annotation().type);
|
||||
checker.check();
|
||||
return false;
|
||||
}
|
||||
@ -672,7 +673,7 @@ void Compiler::appendModifierOrFunctionCode()
|
||||
ASTPointer<ModifierInvocation> const& modifierInvocation = m_currentFunction->modifiers()[m_modifierDepth];
|
||||
|
||||
// constructor call should be excluded
|
||||
if (dynamic_cast<ContractDefinition const*>(&modifierInvocation->name()->referencedDeclaration()))
|
||||
if (dynamic_cast<ContractDefinition const*>(modifierInvocation->name()->annotation().referencedDeclaration))
|
||||
{
|
||||
++m_modifierDepth;
|
||||
appendModifierOrFunctionCode();
|
||||
@ -688,7 +689,7 @@ void Compiler::appendModifierOrFunctionCode()
|
||||
m_context.addVariable(*modifier.parameters()[i]);
|
||||
compileExpression(
|
||||
*modifierInvocation->arguments()[i],
|
||||
modifier.parameters()[i]->type()
|
||||
modifier.parameters()[i]->annotation().type
|
||||
);
|
||||
}
|
||||
for (VariableDeclaration const* localVariable: modifier.localVariables())
|
||||
@ -710,7 +711,7 @@ void Compiler::appendStackVariableInitialisation(VariableDeclaration const& _var
|
||||
{
|
||||
CompilerContext::LocationSetter location(m_context, _variable);
|
||||
m_context.addVariable(_variable);
|
||||
CompilerUtils(m_context).pushZeroValue(*_variable.type());
|
||||
CompilerUtils(m_context).pushZeroValue(*_variable.annotation().type);
|
||||
}
|
||||
|
||||
void Compiler::compileExpression(Expression const& _expression, TypePointer const& _targetType)
|
||||
@ -718,7 +719,7 @@ void Compiler::compileExpression(Expression const& _expression, TypePointer cons
|
||||
ExpressionCompiler expressionCompiler(m_context, m_optimize);
|
||||
expressionCompiler.compile(_expression);
|
||||
if (_targetType)
|
||||
CompilerUtils(m_context).convertType(*_expression.type(), *_targetType);
|
||||
CompilerUtils(m_context).convertType(*_expression.annotation().type, *_targetType);
|
||||
}
|
||||
|
||||
eth::Assembly Compiler::cloneRuntime()
|
||||
|
@ -37,10 +37,8 @@ public:
|
||||
explicit Compiler(bool _optimize = false, unsigned _runs = 200):
|
||||
m_optimize(_optimize),
|
||||
m_optimizeRuns(_runs),
|
||||
m_context(),
|
||||
m_returnTag(m_context.newTag())
|
||||
{
|
||||
}
|
||||
{ }
|
||||
|
||||
void compileContract(
|
||||
ContractDefinition const& _contract,
|
||||
@ -71,7 +69,8 @@ public:
|
||||
eth::AssemblyItem functionEntryLabel(FunctionDefinition const& _function) const;
|
||||
|
||||
private:
|
||||
/// Registers the non-function objects inside the contract with the context.
|
||||
/// Registers the non-function objects inside the contract with the context and stores the basic
|
||||
/// information about the contract like the AST annotations.
|
||||
void initializeContext(
|
||||
ContractDefinition const& _contract,
|
||||
std::map<ContractDefinition const*, eth::Assembly const*> const& _compiledContracts
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <libevmasm/Assembly.h>
|
||||
#include <libsolidity/ASTForward.h>
|
||||
#include <libsolidity/Types.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
#include <libdevcore/Common.h>
|
||||
|
||||
namespace dev {
|
||||
|
@ -27,6 +27,7 @@
|
||||
#include <libsolidity/Parser.h>
|
||||
#include <libsolidity/GlobalContext.h>
|
||||
#include <libsolidity/NameAndTypeResolver.h>
|
||||
#include <libsolidity/TypeChecker.h>
|
||||
#include <libsolidity/Compiler.h>
|
||||
#include <libsolidity/CompilerStack.h>
|
||||
#include <libsolidity/InterfaceHandler.h>
|
||||
@ -76,6 +77,7 @@ void CompilerStack::reset(bool _keepSources, bool _addStandardSources)
|
||||
m_globalContext.reset();
|
||||
m_sourceOrder.clear();
|
||||
m_contracts.clear();
|
||||
m_errors.clear();
|
||||
}
|
||||
|
||||
bool CompilerStack::addSource(string const& _name, string const& _content, bool _isLibrary)
|
||||
@ -93,8 +95,10 @@ void CompilerStack::setSource(string const& _sourceCode)
|
||||
addSource("", _sourceCode);
|
||||
}
|
||||
|
||||
void CompilerStack::parse()
|
||||
bool CompilerStack::parse()
|
||||
{
|
||||
m_errors.clear();
|
||||
|
||||
for (auto& sourcePair: m_sources)
|
||||
{
|
||||
sourcePair.second.scanner->reset();
|
||||
@ -116,6 +120,7 @@ void CompilerStack::parse()
|
||||
resolver.resolveNamesAndTypes(*contract);
|
||||
m_contracts[contract->name()].contract = contract;
|
||||
}
|
||||
|
||||
InterfaceHandler interfaceHandler;
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
@ -123,18 +128,22 @@ void CompilerStack::parse()
|
||||
{
|
||||
m_globalContext->setCurrentContract(*contract);
|
||||
resolver.updateDeclaration(*m_globalContext->currentThis());
|
||||
resolver.checkTypeRequirements(*contract);
|
||||
TypeChecker typeChecker;
|
||||
bool typesFine = typeChecker.checkTypeRequirements(*contract);
|
||||
if (!typesFine)
|
||||
m_errors += typeChecker.errors();
|
||||
contract->setDevDocumentation(interfaceHandler.devDocumentation(*contract));
|
||||
contract->setUserDocumentation(interfaceHandler.userDocumentation(*contract));
|
||||
m_contracts[contract->name()].contract = contract;
|
||||
}
|
||||
m_parseSuccessful = true;
|
||||
m_parseSuccessful = m_errors.empty();
|
||||
return m_parseSuccessful;
|
||||
}
|
||||
|
||||
void CompilerStack::parse(string const& _sourceCode)
|
||||
bool CompilerStack::parse(string const& _sourceCode)
|
||||
{
|
||||
setSource(_sourceCode);
|
||||
parse();
|
||||
return parse();
|
||||
}
|
||||
|
||||
vector<string> CompilerStack::contractNames() const
|
||||
@ -148,17 +157,18 @@ vector<string> CompilerStack::contractNames() const
|
||||
}
|
||||
|
||||
|
||||
void CompilerStack::compile(bool _optimize, unsigned _runs)
|
||||
bool CompilerStack::compile(bool _optimize, unsigned _runs)
|
||||
{
|
||||
if (!m_parseSuccessful)
|
||||
parse();
|
||||
if (!parse())
|
||||
return false;
|
||||
|
||||
map<ContractDefinition const*, eth::Assembly const*> compiledContracts;
|
||||
for (Source const* source: m_sourceOrder)
|
||||
for (ASTPointer<ASTNode> const& node: source->ast->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
if (!contract->isFullyImplemented())
|
||||
if (!contract->annotation().isFullyImplemented)
|
||||
continue;
|
||||
shared_ptr<Compiler> compiler = make_shared<Compiler>(_optimize, _runs);
|
||||
compiler->compileContract(*contract, compiledContracts);
|
||||
@ -172,13 +182,12 @@ void CompilerStack::compile(bool _optimize, unsigned _runs)
|
||||
cloneCompiler.compileClone(*contract, compiledContracts);
|
||||
compiledContract.cloneObject = cloneCompiler.assembledObject();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
eth::LinkerObject const& CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
||||
bool CompilerStack::compile(string const& _sourceCode, bool _optimize)
|
||||
{
|
||||
parse(_sourceCode);
|
||||
compile(_optimize);
|
||||
return object();
|
||||
return parse(_sourceCode) && compile(_optimize);
|
||||
}
|
||||
|
||||
void CompilerStack::link(const std::map<string, h160>& _libraries)
|
||||
@ -317,12 +326,6 @@ size_t CompilerStack::functionEntryPoint(
|
||||
return 0;
|
||||
}
|
||||
|
||||
eth::LinkerObject CompilerStack::staticCompile(std::string const& _sourceCode, bool _optimize)
|
||||
{
|
||||
CompilerStack stack;
|
||||
return stack.compile(_sourceCode, _optimize);
|
||||
}
|
||||
|
||||
tuple<int, int, int, int> CompilerStack::positionFromSourceLocation(SourceLocation const& _sourceLocation) const
|
||||
{
|
||||
int startLine;
|
||||
|
@ -54,6 +54,7 @@ class SourceUnit;
|
||||
class Compiler;
|
||||
class GlobalContext;
|
||||
class InterfaceHandler;
|
||||
struct Error;
|
||||
|
||||
enum class DocumentationType: uint8_t
|
||||
{
|
||||
@ -79,22 +80,28 @@ public:
|
||||
|
||||
/// Adds a source object (e.g. file) to the parser. After this, parse has to be called again.
|
||||
/// @returns true if a source object by the name already existed and was replaced.
|
||||
void addSources(StringMap const& _nameContents, bool _isLibrary = false) { for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary); }
|
||||
void addSources(StringMap const& _nameContents, bool _isLibrary = false)
|
||||
{
|
||||
for (auto const& i: _nameContents) addSource(i.first, i.second, _isLibrary);
|
||||
}
|
||||
bool addSource(std::string const& _name, std::string const& _content, bool _isLibrary = false);
|
||||
void setSource(std::string const& _sourceCode);
|
||||
/// Parses all source units that were added
|
||||
void parse();
|
||||
/// @returns false on error.
|
||||
bool parse();
|
||||
/// Sets the given source code as the only source unit apart from standard sources and parses it.
|
||||
void parse(std::string const& _sourceCode);
|
||||
/// @returns false on error.
|
||||
bool parse(std::string const& _sourceCode);
|
||||
/// Returns a list of the contract names in the sources.
|
||||
std::vector<std::string> contractNames() const;
|
||||
std::string defaultContractName() const;
|
||||
|
||||
/// Compiles the source units that were previously added and parsed.
|
||||
void compile(bool _optimize = false, unsigned _runs = 200);
|
||||
/// @returns false on error.
|
||||
bool compile(bool _optimize = false, unsigned _runs = 200);
|
||||
/// Parses and compiles the given source code.
|
||||
/// @returns the compiled linker object
|
||||
eth::LinkerObject const& compile(std::string const& _sourceCode, bool _optimize = false);
|
||||
/// @returns false on error.
|
||||
bool compile(std::string const& _sourceCode, bool _optimize = false);
|
||||
|
||||
/// Inserts the given addresses into the linker objects of all compiled contracts.
|
||||
void link(std::map<std::string, h160> const& _libraries);
|
||||
@ -150,15 +157,14 @@ public:
|
||||
FunctionDefinition const& _function
|
||||
) const;
|
||||
|
||||
/// Compile the given @a _sourceCode to bytecode. If a scanner is provided, it is used for
|
||||
/// scanning the source code - this is useful for printing exception information.
|
||||
static eth::LinkerObject staticCompile(std::string const& _sourceCode, bool _optimize = false);
|
||||
|
||||
/// Helper function for logs printing. Do only use in error cases, it's quite expensive.
|
||||
/// line and columns are numbered starting from 1 with following order:
|
||||
/// start line, start column, end line, end column
|
||||
std::tuple<int, int, int, int> positionFromSourceLocation(SourceLocation const& _sourceLocation) const;
|
||||
|
||||
/// @returns the list of errors that occured during parsing and type checking.
|
||||
std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; }
|
||||
|
||||
private:
|
||||
/**
|
||||
* Information pertaining to one source unit, filled gradually during parsing and compilation.
|
||||
@ -198,6 +204,7 @@ private:
|
||||
std::shared_ptr<GlobalContext> m_globalContext;
|
||||
std::vector<Source const*> m_sourceOrder;
|
||||
std::map<std::string const, Contract> m_contracts;
|
||||
std::vector<std::shared_ptr<Error const>> m_errors;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -600,7 +600,7 @@ void CompilerUtils::pushZeroValue(const Type& _type)
|
||||
void CompilerUtils::moveToStackVariable(VariableDeclaration const& _variable)
|
||||
{
|
||||
unsigned const stackPosition = m_context.baseToCurrentStackOffset(m_context.baseStackOffsetOfVariable(_variable));
|
||||
unsigned const size = _variable.type()->sizeOnStack();
|
||||
unsigned const size = _variable.annotation().type->sizeOnStack();
|
||||
solAssert(stackPosition >= size, "Variable size and position mismatch.");
|
||||
// move variable starting from its top end in the stack
|
||||
if (stackPosition - size + 1 > 16)
|
||||
|
@ -170,7 +170,7 @@ unsigned CompilerUtils::sizeOnStack(std::vector<T> const& _variables)
|
||||
{
|
||||
unsigned size = 0;
|
||||
for (T const& variable: _variables)
|
||||
size += variable->type()->sizeOnStack();
|
||||
size += variable->annotation().type->sizeOnStack();
|
||||
return size;
|
||||
}
|
||||
|
||||
|
59
libsolidity/ConstantEvaluator.cpp
Normal file
59
libsolidity/ConstantEvaluator.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
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 2015
|
||||
* Evaluator for types of constant expressions.
|
||||
*/
|
||||
|
||||
#include <libsolidity/ConstantEvaluator.h>
|
||||
#include <libsolidity/AST.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
|
||||
void ConstantEvaluator::endVisit(UnaryOperation const& _operation)
|
||||
{
|
||||
TypePointer const& subType = _operation.subExpression().annotation().type;
|
||||
if (!dynamic_cast<IntegerConstantType const*>(subType.get()))
|
||||
BOOST_THROW_EXCEPTION(_operation.subExpression().createTypeError("Invalid constant expression."));
|
||||
TypePointer t = subType->unaryOperatorResult(_operation.getOperator());
|
||||
_operation.annotation().type = t;
|
||||
}
|
||||
|
||||
void ConstantEvaluator::endVisit(BinaryOperation const& _operation)
|
||||
{
|
||||
TypePointer const& leftType = _operation.leftExpression().annotation().type;
|
||||
TypePointer const& rightType = _operation.rightExpression().annotation().type;
|
||||
if (!dynamic_cast<IntegerConstantType const*>(leftType.get()))
|
||||
BOOST_THROW_EXCEPTION(_operation.leftExpression().createTypeError("Invalid constant expression."));
|
||||
if (!dynamic_cast<IntegerConstantType const*>(rightType.get()))
|
||||
BOOST_THROW_EXCEPTION(_operation.rightExpression().createTypeError("Invalid constant expression."));
|
||||
TypePointer commonType = leftType->binaryOperatorResult(_operation.getOperator(), rightType);
|
||||
if (Token::isCompareOp(_operation.getOperator()))
|
||||
commonType = make_shared<BoolType>();
|
||||
_operation.annotation().type = commonType;
|
||||
}
|
||||
|
||||
void ConstantEvaluator::endVisit(Literal const& _literal)
|
||||
{
|
||||
_literal.annotation().type = Type::forLiteral(_literal);
|
||||
if (!_literal.annotation().type)
|
||||
BOOST_THROW_EXCEPTION(_literal.createTypeError("Invalid literal value."));
|
||||
}
|
50
libsolidity/ConstantEvaluator.h
Normal file
50
libsolidity/ConstantEvaluator.h
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
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 2015
|
||||
* Evaluator for types of constant expressions.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class TypeChecker;
|
||||
|
||||
/**
|
||||
* Small drop-in replacement for TypeChecker to evaluate simple expressions of integer constants.
|
||||
*/
|
||||
class ConstantEvaluator: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
ConstantEvaluator(Expression const& _expr) { _expr.accept(*this); }
|
||||
|
||||
private:
|
||||
virtual void endVisit(BinaryOperation const& _operation);
|
||||
virtual void endVisit(UnaryOperation const& _operation);
|
||||
virtual void endVisit(Literal const& _literal);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -32,12 +32,16 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
struct ParserError: virtual Exception {};
|
||||
struct TypeError: virtual Exception {};
|
||||
struct DeclarationError: virtual Exception {};
|
||||
struct Error: virtual Exception {};
|
||||
|
||||
struct ParserError: virtual Error {};
|
||||
struct TypeError: virtual Error {};
|
||||
struct DeclarationError: virtual Error {};
|
||||
struct DocstringParsingError: virtual Error {};
|
||||
|
||||
struct CompilerError: virtual Exception {};
|
||||
struct InternalCompilerError: virtual Exception {};
|
||||
struct DocstringParsingError: virtual Exception {};
|
||||
struct FatalError: virtual Exception {};
|
||||
|
||||
using errorSourceLocationInfo = std::pair<std::string, SourceLocation>;
|
||||
|
||||
|
@ -48,12 +48,12 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
|
||||
{
|
||||
if (!_varDecl.value())
|
||||
return;
|
||||
TypePointer type = _varDecl.value()->type();
|
||||
TypePointer type = _varDecl.value()->annotation().type;
|
||||
solAssert(!!type, "Type information not available.");
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _varDecl);
|
||||
_varDecl.value()->accept(*this);
|
||||
|
||||
if (_varDecl.type()->dataStoredIn(DataLocation::Storage))
|
||||
if (_varDecl.annotation().type->dataStoredIn(DataLocation::Storage))
|
||||
{
|
||||
// reference type, only convert value to mobile type and do final conversion in storeValue.
|
||||
utils().convertType(*type, *type->mobileType());
|
||||
@ -61,8 +61,8 @@ void ExpressionCompiler::appendStateVariableInitialization(VariableDeclaration c
|
||||
}
|
||||
else
|
||||
{
|
||||
utils().convertType(*type, *_varDecl.type());
|
||||
type = _varDecl.type();
|
||||
utils().convertType(*type, *_varDecl.annotation().type);
|
||||
type = _varDecl.annotation().type;
|
||||
}
|
||||
StorageItem(m_context, _varDecl).storeValue(*type, _varDecl.location(), true);
|
||||
}
|
||||
@ -71,10 +71,10 @@ void ExpressionCompiler::appendConstStateVariableAccessor(VariableDeclaration co
|
||||
{
|
||||
solAssert(_varDecl.isConstant(), "");
|
||||
_varDecl.value()->accept(*this);
|
||||
utils().convertType(*_varDecl.value()->type(), *_varDecl.type());
|
||||
utils().convertType(*_varDecl.value()->annotation().type, *_varDecl.annotation().type);
|
||||
|
||||
// append return
|
||||
m_context << eth::dupInstruction(_varDecl.type()->sizeOnStack() + 1);
|
||||
m_context << eth::dupInstruction(_varDecl.annotation().type->sizeOnStack() + 1);
|
||||
m_context.appendJump(eth::AssemblyItem::JumpType::OutOfFunction);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ void ExpressionCompiler::appendStateVariableAccessor(VariableDeclaration const&
|
||||
auto const& location = m_context.storageLocationOfVariable(_varDecl);
|
||||
m_context << location.first << u256(location.second);
|
||||
|
||||
TypePointer returnType = _varDecl.type();
|
||||
TypePointer returnType = _varDecl.annotation().type;
|
||||
|
||||
for (size_t i = 0; i < paramTypes.size(); ++i)
|
||||
{
|
||||
@ -179,11 +179,11 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _assignment);
|
||||
_assignment.rightHandSide().accept(*this);
|
||||
TypePointer type = _assignment.rightHandSide().type();
|
||||
if (!_assignment.type()->dataStoredIn(DataLocation::Storage))
|
||||
TypePointer type = _assignment.rightHandSide().annotation().type;
|
||||
if (!_assignment.annotation().type->dataStoredIn(DataLocation::Storage))
|
||||
{
|
||||
utils().convertType(*type, *_assignment.type());
|
||||
type = _assignment.type();
|
||||
utils().convertType(*type, *_assignment.annotation().type);
|
||||
type = _assignment.annotation().type;
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -197,9 +197,9 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
Token::Value op = _assignment.assignmentOperator();
|
||||
if (op != Token::Assign) // compound assignment
|
||||
{
|
||||
solAssert(_assignment.type()->isValueType(), "Compound operators not implemented for non-value types.");
|
||||
solAssert(_assignment.annotation().type->isValueType(), "Compound operators not implemented for non-value types.");
|
||||
unsigned lvalueSize = m_currentLValue->sizeOnStack();
|
||||
unsigned itemSize = _assignment.type()->sizeOnStack();
|
||||
unsigned itemSize = _assignment.annotation().type->sizeOnStack();
|
||||
if (lvalueSize > 0)
|
||||
{
|
||||
utils().copyToStackTop(lvalueSize + itemSize, itemSize);
|
||||
@ -207,7 +207,7 @@ bool ExpressionCompiler::visit(Assignment const& _assignment)
|
||||
// value lvalue_ref value lvalue_ref
|
||||
}
|
||||
m_currentLValue->retrieveValue(_assignment.location(), true);
|
||||
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.type());
|
||||
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.annotation().type);
|
||||
if (lvalueSize > 0)
|
||||
{
|
||||
solAssert(itemSize + lvalueSize <= 16, "Stack too deep, try removing local variables.");
|
||||
@ -228,9 +228,9 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
// the operator should know how to convert itself and to which types it applies, so
|
||||
// put this code together with "Type::acceptsBinary/UnaryOperator" into a class that
|
||||
// represents the operator
|
||||
if (_unaryOperation.type()->category() == Type::Category::IntegerConstant)
|
||||
if (_unaryOperation.annotation().type->category() == Type::Category::IntegerConstant)
|
||||
{
|
||||
m_context << _unaryOperation.type()->literalValue(nullptr);
|
||||
m_context << _unaryOperation.annotation().type->literalValue(nullptr);
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
if (!_unaryOperation.isPrefixOperation())
|
||||
{
|
||||
// store value for later
|
||||
solAssert(_unaryOperation.type()->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||
solAssert(_unaryOperation.annotation().type->sizeOnStack() == 1, "Stack size != 1 not implemented.");
|
||||
m_context << eth::Instruction::DUP1;
|
||||
if (m_currentLValue->sizeOnStack() > 0)
|
||||
for (unsigned i = 1 + m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
@ -275,7 +275,7 @@ bool ExpressionCompiler::visit(UnaryOperation const& _unaryOperation)
|
||||
for (unsigned i = m_currentLValue->sizeOnStack(); i > 0; --i)
|
||||
m_context << eth::swapInstruction(i);
|
||||
m_currentLValue->storeValue(
|
||||
*_unaryOperation.type(), _unaryOperation.location(),
|
||||
*_unaryOperation.annotation().type, _unaryOperation.location(),
|
||||
!_unaryOperation.isPrefixOperation());
|
||||
m_currentLValue.reset();
|
||||
break;
|
||||
@ -297,7 +297,8 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _binaryOperation);
|
||||
Expression const& leftExpression = _binaryOperation.leftExpression();
|
||||
Expression const& rightExpression = _binaryOperation.rightExpression();
|
||||
Type const& commonType = _binaryOperation.commonType();
|
||||
solAssert(!!_binaryOperation.annotation().commonType, "");
|
||||
Type const& commonType = *_binaryOperation.annotation().commonType;
|
||||
Token::Value const c_op = _binaryOperation.getOperator();
|
||||
|
||||
if (c_op == Token::And || c_op == Token::Or) // special case: short-circuiting
|
||||
@ -312,22 +313,22 @@ bool ExpressionCompiler::visit(BinaryOperation const& _binaryOperation)
|
||||
// for commutative operators, push the literal as late as possible to allow improved optimization
|
||||
auto isLiteral = [](Expression const& _e)
|
||||
{
|
||||
return dynamic_cast<Literal const*>(&_e) || _e.type()->category() == Type::Category::IntegerConstant;
|
||||
return dynamic_cast<Literal const*>(&_e) || _e.annotation().type->category() == Type::Category::IntegerConstant;
|
||||
};
|
||||
bool swap = m_optimize && Token::isCommutativeOp(c_op) && isLiteral(rightExpression) && !isLiteral(leftExpression);
|
||||
if (swap)
|
||||
{
|
||||
leftExpression.accept(*this);
|
||||
utils().convertType(*leftExpression.type(), commonType, cleanupNeeded);
|
||||
utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
|
||||
rightExpression.accept(*this);
|
||||
utils().convertType(*rightExpression.type(), commonType, cleanupNeeded);
|
||||
utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
|
||||
}
|
||||
else
|
||||
{
|
||||
rightExpression.accept(*this);
|
||||
utils().convertType(*rightExpression.type(), commonType, cleanupNeeded);
|
||||
utils().convertType(*rightExpression.annotation().type, commonType, cleanupNeeded);
|
||||
leftExpression.accept(*this);
|
||||
utils().convertType(*leftExpression.type(), commonType, cleanupNeeded);
|
||||
utils().convertType(*leftExpression.annotation().type, commonType, cleanupNeeded);
|
||||
}
|
||||
if (Token::isCompareOp(c_op))
|
||||
appendCompareOperatorCode(c_op, commonType);
|
||||
@ -343,25 +344,25 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _functionCall);
|
||||
using Location = FunctionType::Location;
|
||||
if (_functionCall.isTypeConversion())
|
||||
if (_functionCall.annotation().isTypeConversion)
|
||||
{
|
||||
solAssert(_functionCall.arguments().size() == 1, "");
|
||||
solAssert(_functionCall.names().empty(), "");
|
||||
Expression const& firstArgument = *_functionCall.arguments().front();
|
||||
firstArgument.accept(*this);
|
||||
utils().convertType(*firstArgument.type(), *_functionCall.type());
|
||||
utils().convertType(*firstArgument.annotation().type, *_functionCall.annotation().type);
|
||||
return false;
|
||||
}
|
||||
|
||||
FunctionTypePointer functionType;
|
||||
if (_functionCall.isStructConstructorCall())
|
||||
if (_functionCall.annotation().isStructConstructorCall)
|
||||
{
|
||||
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type());
|
||||
auto const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||
functionType = structType.constructorType();
|
||||
}
|
||||
else
|
||||
functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().type());
|
||||
functionType = dynamic_pointer_cast<FunctionType const>(_functionCall.expression().annotation().type);
|
||||
|
||||
TypePointers const& parameterTypes = functionType->parameterTypes();
|
||||
vector<ASTPointer<Expression const>> const& callArguments = _functionCall.arguments();
|
||||
@ -385,9 +386,9 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
solAssert(found, "");
|
||||
}
|
||||
|
||||
if (_functionCall.isStructConstructorCall())
|
||||
if (_functionCall.annotation().isStructConstructorCall)
|
||||
{
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().type());
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_functionCall.expression().annotation().type);
|
||||
auto const& structType = dynamic_cast<StructType const&>(*type.actualType());
|
||||
|
||||
m_context << max(u256(32u), structType.memorySize());
|
||||
@ -397,7 +398,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
arguments[i]->accept(*this);
|
||||
utils().convertType(*arguments[i]->type(), *functionType->parameterTypes()[i]);
|
||||
utils().convertType(*arguments[i]->annotation().type, *functionType->parameterTypes()[i]);
|
||||
utils().storeInMemoryDynamic(*functionType->parameterTypes()[i]);
|
||||
}
|
||||
m_context << eth::Instruction::POP;
|
||||
@ -416,7 +417,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
for (unsigned i = 0; i < arguments.size(); ++i)
|
||||
{
|
||||
arguments[i]->accept(*this);
|
||||
utils().convertType(*arguments[i]->type(), *function.parameterTypes()[i]);
|
||||
utils().convertType(*arguments[i]->annotation().type, *function.parameterTypes()[i]);
|
||||
}
|
||||
_functionCall.expression().accept(*this);
|
||||
|
||||
@ -449,7 +450,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
for (auto const& arg: arguments)
|
||||
{
|
||||
arg->accept(*this);
|
||||
argumentTypes.push_back(arg->type());
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
}
|
||||
ContractDefinition const& contract =
|
||||
dynamic_cast<ContractType const&>(*function.returnParameterTypes().front()).contractDefinition();
|
||||
@ -481,7 +482,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
_functionCall.expression().accept(*this);
|
||||
|
||||
arguments.front()->accept(*this);
|
||||
utils().convertType(*arguments.front()->type(), IntegerType(256), true);
|
||||
utils().convertType(*arguments.front()->annotation().type, IntegerType(256), true);
|
||||
// Note that function is not the original function, but the ".gas" function.
|
||||
// Its values of gasSet and valueSet is equal to the original function's though.
|
||||
unsigned stackDepth = (function.gasSet() ? 1 : 0) + (function.valueSet() ? 1 : 0);
|
||||
@ -505,7 +506,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
m_context << u256(0); // do not send gas (there still is the stipend)
|
||||
arguments.front()->accept(*this);
|
||||
utils().convertType(
|
||||
*arguments.front()->type(),
|
||||
*arguments.front()->annotation().type,
|
||||
*function.parameterTypes().front(), true
|
||||
);
|
||||
appendExternalFunctionCall(
|
||||
@ -525,7 +526,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
break;
|
||||
case Location::Suicide:
|
||||
arguments.front()->accept(*this);
|
||||
utils().convertType(*arguments.front()->type(), *function.parameterTypes().front(), true);
|
||||
utils().convertType(*arguments.front()->annotation().type, *function.parameterTypes().front(), true);
|
||||
m_context << eth::Instruction::SUICIDE;
|
||||
break;
|
||||
case Location::SHA3:
|
||||
@ -534,7 +535,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
for (auto const& arg: arguments)
|
||||
{
|
||||
arg->accept(*this);
|
||||
argumentTypes.push_back(arg->type());
|
||||
argumentTypes.push_back(arg->annotation().type);
|
||||
}
|
||||
utils().fetchFreeMemoryPointer();
|
||||
utils().encodeToMemory(argumentTypes, TypePointers(), function.padArguments(), true);
|
||||
@ -552,12 +553,12 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
for (unsigned arg = logNumber; arg > 0; --arg)
|
||||
{
|
||||
arguments[arg]->accept(*this);
|
||||
utils().convertType(*arguments[arg]->type(), *function.parameterTypes()[arg], true);
|
||||
utils().convertType(*arguments[arg]->annotation().type, *function.parameterTypes()[arg], true);
|
||||
}
|
||||
arguments.front()->accept(*this);
|
||||
utils().fetchFreeMemoryPointer();
|
||||
utils().encodeToMemory(
|
||||
{arguments.front()->type()},
|
||||
{arguments.front()->annotation().type},
|
||||
{function.parameterTypes().front()},
|
||||
false,
|
||||
true);
|
||||
@ -577,7 +578,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
++numIndexed;
|
||||
arguments[arg - 1]->accept(*this);
|
||||
utils().convertType(
|
||||
*arguments[arg - 1]->type(),
|
||||
*arguments[arg - 1]->annotation().type,
|
||||
*function.parameterTypes()[arg - 1],
|
||||
true
|
||||
);
|
||||
@ -596,7 +597,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
if (!event.parameters()[arg]->isIndexed())
|
||||
{
|
||||
arguments[arg]->accept(*this);
|
||||
nonIndexedArgTypes.push_back(arguments[arg]->type());
|
||||
nonIndexedArgTypes.push_back(arguments[arg]->annotation().type);
|
||||
nonIndexedParamTypes.push_back(function.parameterTypes()[arg]);
|
||||
}
|
||||
utils().fetchFreeMemoryPointer();
|
||||
@ -609,7 +610,7 @@ bool ExpressionCompiler::visit(FunctionCall const& _functionCall)
|
||||
case Location::BlockHash:
|
||||
{
|
||||
arguments[0]->accept(*this);
|
||||
utils().convertType(*arguments[0]->type(), *function.parameterTypes()[0], true);
|
||||
utils().convertType(*arguments[0]->annotation().type, *function.parameterTypes()[0], true);
|
||||
m_context << eth::Instruction::BLOCKHASH;
|
||||
break;
|
||||
}
|
||||
@ -644,24 +645,24 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _memberAccess);
|
||||
ASTString const& member = _memberAccess.memberName();
|
||||
switch (_memberAccess.expression().type()->category())
|
||||
switch (_memberAccess.expression().annotation().type->category())
|
||||
{
|
||||
case Type::Category::Contract:
|
||||
{
|
||||
bool alsoSearchInteger = false;
|
||||
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().type());
|
||||
ContractType const& type = dynamic_cast<ContractType const&>(*_memberAccess.expression().annotation().type);
|
||||
if (type.isSuper())
|
||||
{
|
||||
solAssert(!!_memberAccess.referencedDeclaration(), "Referenced declaration not resolved.");
|
||||
solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved.");
|
||||
m_context << m_context.superFunctionEntryLabel(
|
||||
dynamic_cast<FunctionDefinition const&>(*_memberAccess.referencedDeclaration()),
|
||||
dynamic_cast<FunctionDefinition const&>(*_memberAccess.annotation().referencedDeclaration),
|
||||
type.contractDefinition()
|
||||
).pushTag();
|
||||
}
|
||||
else
|
||||
{
|
||||
// ordinary contract type
|
||||
if (Declaration const* declaration = _memberAccess.referencedDeclaration())
|
||||
if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration)
|
||||
{
|
||||
u256 identifier;
|
||||
if (auto const* variable = dynamic_cast<VariableDeclaration const*>(declaration))
|
||||
@ -684,7 +685,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
if (member == "balance")
|
||||
{
|
||||
utils().convertType(
|
||||
*_memberAccess.expression().type(),
|
||||
*_memberAccess.expression().annotation().type,
|
||||
IntegerType(0, IntegerType::Modifier::Address),
|
||||
true
|
||||
);
|
||||
@ -692,7 +693,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
}
|
||||
else if ((set<string>{"send", "call", "callcode"}).count(member))
|
||||
utils().convertType(
|
||||
*_memberAccess.expression().type(),
|
||||
*_memberAccess.expression().annotation().type,
|
||||
IntegerType(0, IntegerType::Modifier::Address),
|
||||
true
|
||||
);
|
||||
@ -700,7 +701,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to integer."));
|
||||
break;
|
||||
case Type::Category::Function:
|
||||
solAssert(!!_memberAccess.expression().type()->memberType(member),
|
||||
solAssert(!!_memberAccess.expression().annotation().type->memberType(member),
|
||||
"Invalid member access to function.");
|
||||
break;
|
||||
case Type::Category::Magic:
|
||||
@ -735,7 +736,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
break;
|
||||
case Type::Category::Struct:
|
||||
{
|
||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().type());
|
||||
StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.expression().annotation().type);
|
||||
switch (type.location())
|
||||
{
|
||||
case DataLocation::Storage:
|
||||
@ -748,7 +749,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
case DataLocation::Memory:
|
||||
{
|
||||
m_context << type.memoryOffsetOfMember(member) << eth::Instruction::ADD;
|
||||
setLValue<MemoryItem>(_memberAccess, *_memberAccess.type());
|
||||
setLValue<MemoryItem>(_memberAccess, *_memberAccess.annotation().type);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -758,13 +759,13 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
}
|
||||
case Type::Category::Enum:
|
||||
{
|
||||
EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().type());
|
||||
EnumType const& type = dynamic_cast<EnumType const&>(*_memberAccess.expression().annotation().type);
|
||||
m_context << type.memberValue(_memberAccess.memberName());
|
||||
break;
|
||||
}
|
||||
case Type::Category::TypeType:
|
||||
{
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().type());
|
||||
TypeType const& type = dynamic_cast<TypeType const&>(*_memberAccess.expression().annotation().type);
|
||||
solAssert(
|
||||
!type.members().membersByName(_memberAccess.memberName()).empty(),
|
||||
"Invalid member access to " + type.toString(false)
|
||||
@ -772,12 +773,12 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
|
||||
if (dynamic_cast<ContractType const*>(type.actualType().get()))
|
||||
{
|
||||
auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.type());
|
||||
auto const& funType = dynamic_cast<FunctionType const&>(*_memberAccess.annotation().type);
|
||||
if (funType.location() != FunctionType::Location::Internal)
|
||||
m_context << funType.externalIdentifier();
|
||||
else
|
||||
{
|
||||
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.referencedDeclaration());
|
||||
auto const* function = dynamic_cast<FunctionDefinition const*>(_memberAccess.annotation().referencedDeclaration);
|
||||
solAssert(!!function, "Function not found in member access");
|
||||
m_context << m_context.functionEntryLabel(*function).pushTag();
|
||||
}
|
||||
@ -789,7 +790,7 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess)
|
||||
case Type::Category::Array:
|
||||
{
|
||||
solAssert(member == "length", "Illegal array member.");
|
||||
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().type());
|
||||
auto const& type = dynamic_cast<ArrayType const&>(*_memberAccess.expression().annotation().type);
|
||||
if (!type.isDynamicallySized())
|
||||
{
|
||||
utils().popStackElement(type);
|
||||
@ -820,7 +821,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _indexAccess);
|
||||
_indexAccess.baseExpression().accept(*this);
|
||||
|
||||
Type const& baseType = *_indexAccess.baseExpression().type();
|
||||
Type const& baseType = *_indexAccess.baseExpression().annotation().type;
|
||||
|
||||
if (baseType.category() == Type::Category::Mapping)
|
||||
{
|
||||
@ -834,7 +835,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||
// stack: base index mem
|
||||
// note: the following operations must not allocate memory!
|
||||
utils().encodeToMemory(
|
||||
TypePointers{_indexAccess.indexExpression()->type()},
|
||||
TypePointers{_indexAccess.indexExpression()->annotation().type},
|
||||
TypePointers{keyType},
|
||||
false,
|
||||
true
|
||||
@ -876,7 +877,7 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||
setLValueToStorageItem(_indexAccess);
|
||||
break;
|
||||
case DataLocation::Memory:
|
||||
setLValue<MemoryItem>(_indexAccess, *_indexAccess.type(), !arrayType.isByteArray());
|
||||
setLValue<MemoryItem>(_indexAccess, *_indexAccess.annotation().type, !arrayType.isByteArray());
|
||||
break;
|
||||
case DataLocation::CallData:
|
||||
//@todo if we implement this, the value in calldata has to be added to the base offset
|
||||
@ -900,14 +901,14 @@ bool ExpressionCompiler::visit(IndexAccess const& _indexAccess)
|
||||
void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _identifier);
|
||||
Declaration const* declaration = &_identifier.referencedDeclaration();
|
||||
Declaration const* declaration = _identifier.annotation().referencedDeclaration;
|
||||
if (MagicVariableDeclaration const* magicVar = dynamic_cast<MagicVariableDeclaration const*>(declaration))
|
||||
{
|
||||
switch (magicVar->type()->category())
|
||||
switch (magicVar->type(_identifier.annotation().contractScope)->category())
|
||||
{
|
||||
case Type::Category::Contract:
|
||||
// "this" or "super"
|
||||
if (!dynamic_cast<ContractType const&>(*magicVar->type()).isSuper())
|
||||
if (!dynamic_cast<ContractType const&>(*magicVar->type(_identifier.annotation().contractScope)).isSuper())
|
||||
m_context << eth::Instruction::ADDRESS;
|
||||
break;
|
||||
case Type::Category::Integer:
|
||||
@ -927,7 +928,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
else
|
||||
{
|
||||
variable->value()->accept(*this);
|
||||
utils().convertType(*variable->value()->type(), *variable->type());
|
||||
utils().convertType(*variable->value()->annotation().type, *variable->annotation().type);
|
||||
}
|
||||
}
|
||||
else if (auto contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
@ -953,7 +954,7 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier)
|
||||
void ExpressionCompiler::endVisit(Literal const& _literal)
|
||||
{
|
||||
CompilerContext::LocationSetter locationSetter(m_context, _literal);
|
||||
TypePointer type = _literal.type();
|
||||
TypePointer type = _literal.annotation().type;
|
||||
switch (type->category())
|
||||
{
|
||||
case Type::Category::IntegerConstant:
|
||||
@ -1141,7 +1142,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
bool manualFunctionId =
|
||||
(funKind == FunctionKind::Bare || funKind == FunctionKind::BareCallCode) &&
|
||||
!_arguments.empty() &&
|
||||
_arguments.front()->type()->mobileType()->calldataEncodedSize(false) ==
|
||||
_arguments.front()->annotation().type->mobileType()->calldataEncodedSize(false) ==
|
||||
CompilerUtils::dataStartOffset;
|
||||
if (manualFunctionId)
|
||||
{
|
||||
@ -1149,7 +1150,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
// function identifier.
|
||||
_arguments.front()->accept(*this);
|
||||
utils().convertType(
|
||||
*_arguments.front()->type(),
|
||||
*_arguments.front()->annotation().type,
|
||||
IntegerType(8 * CompilerUtils::dataStartOffset),
|
||||
true
|
||||
);
|
||||
@ -1161,7 +1162,7 @@ void ExpressionCompiler::appendExternalFunctionCall(
|
||||
for (size_t i = manualFunctionId ? 1 : 0; i < _arguments.size(); ++i)
|
||||
{
|
||||
_arguments[i]->accept(*this);
|
||||
argumentTypes.push_back(_arguments[i]->type());
|
||||
argumentTypes.push_back(_arguments[i]->annotation().type);
|
||||
}
|
||||
|
||||
// Copy function identifier to memory.
|
||||
@ -1266,16 +1267,16 @@ void ExpressionCompiler::appendExpressionCopyToMemory(Type const& _expectedType,
|
||||
{
|
||||
solAssert(_expectedType.isValueType(), "Not implemented for non-value types.");
|
||||
_expression.accept(*this);
|
||||
utils().convertType(*_expression.type(), _expectedType, true);
|
||||
utils().convertType(*_expression.annotation().type, _expectedType, true);
|
||||
utils().storeInMemoryDynamic(_expectedType);
|
||||
}
|
||||
|
||||
void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaration, Expression const& _expression)
|
||||
{
|
||||
if (m_context.isLocalVariable(&_declaration))
|
||||
setLValue<StackVariable>(_expression, _declaration);
|
||||
setLValue<StackVariable>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
|
||||
else if (m_context.isStateVariable(&_declaration))
|
||||
setLValue<StorageItem>(_expression, _declaration);
|
||||
setLValue<StorageItem>(_expression, dynamic_cast<VariableDeclaration const&>(_declaration));
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError()
|
||||
<< errinfo_sourceLocation(_expression.location())
|
||||
@ -1284,7 +1285,7 @@ void ExpressionCompiler::setLValueFromDeclaration(Declaration const& _declaratio
|
||||
|
||||
void ExpressionCompiler::setLValueToStorageItem(Expression const& _expression)
|
||||
{
|
||||
setLValue<StorageItem>(_expression, *_expression.type());
|
||||
setLValue<StorageItem>(_expression, *_expression.annotation().type);
|
||||
}
|
||||
|
||||
CompilerUtils ExpressionCompiler::utils()
|
||||
|
@ -127,7 +127,7 @@ void ExpressionCompiler::setLValue(Expression const& _expression, _Arguments con
|
||||
{
|
||||
solAssert(!m_currentLValue, "Current LValue not reset before trying to set new one.");
|
||||
std::unique_ptr<_LValueType> lvalue(new _LValueType(m_context, _arguments...));
|
||||
if (_expression.lvalueRequested())
|
||||
if (_expression.annotation().lValueRequested)
|
||||
m_currentLValue = move(lvalue);
|
||||
else
|
||||
lvalue->retrieveValue(_expression.location(), true);
|
||||
|
@ -96,7 +96,7 @@ string InterfaceHandler::abiInterface(ContractDefinition const& _contractDef)
|
||||
{
|
||||
Json::Value input;
|
||||
input["name"] = p->name();
|
||||
input["type"] = p->type()->toString(true);
|
||||
input["type"] = p->annotation().type->toString(true);
|
||||
input["indexed"] = p->isIndexed();
|
||||
params.append(input);
|
||||
}
|
||||
|
@ -31,8 +31,8 @@ using namespace dev;
|
||||
using namespace solidity;
|
||||
|
||||
|
||||
StackVariable::StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration):
|
||||
LValue(_compilerContext, *_declaration.type()),
|
||||
StackVariable::StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
|
||||
LValue(_compilerContext, *_declaration.annotation().type),
|
||||
m_baseStackOffset(m_context.baseStackOffsetOfVariable(_declaration)),
|
||||
m_size(m_dataType.sizeOnStack())
|
||||
{
|
||||
@ -131,8 +131,8 @@ void MemoryItem::setToZero(SourceLocation const&, bool _removeReference) const
|
||||
m_context << eth::Instruction::POP;
|
||||
}
|
||||
|
||||
StorageItem::StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration):
|
||||
StorageItem(_compilerContext, *_declaration.type())
|
||||
StorageItem::StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration):
|
||||
StorageItem(_compilerContext, *_declaration.annotation().type)
|
||||
{
|
||||
auto const& location = m_context.storageLocationOfVariable(_declaration);
|
||||
m_context << location.first << u256(location.second);
|
||||
|
@ -35,6 +35,7 @@ class Declaration;
|
||||
class Type;
|
||||
class ArrayType;
|
||||
class CompilerContext;
|
||||
class VariableDeclaration;
|
||||
|
||||
/**
|
||||
* Abstract class used to retrieve, delete and store data in lvalues/variables.
|
||||
@ -76,7 +77,7 @@ protected:
|
||||
class StackVariable: public LValue
|
||||
{
|
||||
public:
|
||||
StackVariable(CompilerContext& _compilerContext, Declaration const& _declaration);
|
||||
StackVariable(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
|
||||
|
||||
virtual unsigned sizeOnStack() const override { return 0; }
|
||||
virtual void retrieveValue(SourceLocation const& _location, bool _remove = false) const override;
|
||||
@ -129,7 +130,7 @@ class StorageItem: public LValue
|
||||
{
|
||||
public:
|
||||
/// Constructs the LValue and pushes the location of @a _declaration onto the stack.
|
||||
StorageItem(CompilerContext& _compilerContext, Declaration const& _declaration);
|
||||
StorageItem(CompilerContext& _compilerContext, VariableDeclaration const& _declaration);
|
||||
/// Constructs the LValue and assumes that the storage reference is already on the stack.
|
||||
StorageItem(CompilerContext& _compilerContext, Type const& _type);
|
||||
virtual unsigned sizeOnStack() const override { return 2; }
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include <libsolidity/NameAndTypeResolver.h>
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/TypeChecker.h>
|
||||
#include <libsolidity/Exceptions.h>
|
||||
|
||||
using namespace std;
|
||||
@ -31,7 +32,9 @@ namespace dev
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
NameAndTypeResolver::NameAndTypeResolver(vector<Declaration const*> const& _globals)
|
||||
NameAndTypeResolver::NameAndTypeResolver(
|
||||
vector<Declaration const*> const& _globals
|
||||
)
|
||||
{
|
||||
for (Declaration const* declaration: _globals)
|
||||
m_scopes[nullptr].registerDeclaration(*declaration);
|
||||
@ -54,8 +57,8 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
|
||||
|
||||
linearizeBaseContracts(_contract);
|
||||
std::vector<ContractDefinition const*> properBases(
|
||||
++_contract.linearizedBaseContracts().begin(),
|
||||
_contract.linearizedBaseContracts().end()
|
||||
++_contract.annotation().linearizedBaseContracts.begin(),
|
||||
_contract.annotation().linearizedBaseContracts.end()
|
||||
);
|
||||
|
||||
for (ContractDefinition const* base: properBases)
|
||||
@ -108,13 +111,6 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
|
||||
}
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::checkTypeRequirements(ContractDefinition& _contract)
|
||||
{
|
||||
for (ASTPointer<StructDefinition> const& structDef: _contract.definedStructs())
|
||||
structDef->checkValidityOfMembers();
|
||||
_contract.checkTypeRequirements();
|
||||
}
|
||||
|
||||
void NameAndTypeResolver::updateDeclaration(Declaration const& _declaration)
|
||||
{
|
||||
m_scopes[nullptr].registerDeclaration(_declaration, false, true);
|
||||
@ -187,23 +183,23 @@ void NameAndTypeResolver::linearizeBaseContracts(ContractDefinition& _contract)
|
||||
list<list<ContractDefinition const*>> input(1, {});
|
||||
for (ASTPointer<InheritanceSpecifier> const& baseSpecifier: _contract.baseContracts())
|
||||
{
|
||||
ASTPointer<Identifier> baseName = baseSpecifier->name();
|
||||
auto base = dynamic_cast<ContractDefinition const*>(&baseName->referencedDeclaration());
|
||||
Identifier const& baseName = baseSpecifier->name();
|
||||
auto base = dynamic_cast<ContractDefinition const*>(baseName.annotation().referencedDeclaration);
|
||||
if (!base)
|
||||
BOOST_THROW_EXCEPTION(baseName->createTypeError("Contract expected."));
|
||||
BOOST_THROW_EXCEPTION(baseName.createTypeError("Contract expected."));
|
||||
// "push_front" has the effect that bases mentioned later can overwrite members of bases
|
||||
// mentioned earlier
|
||||
input.back().push_front(base);
|
||||
vector<ContractDefinition const*> const& basesBases = base->linearizedBaseContracts();
|
||||
vector<ContractDefinition const*> const& basesBases = base->annotation().linearizedBaseContracts;
|
||||
if (basesBases.empty())
|
||||
BOOST_THROW_EXCEPTION(baseName->createTypeError("Definition of base has to precede definition of derived contract"));
|
||||
BOOST_THROW_EXCEPTION(baseName.createTypeError("Definition of base has to precede definition of derived contract"));
|
||||
input.push_front(list<ContractDefinition const*>(basesBases.begin(), basesBases.end()));
|
||||
}
|
||||
input.back().push_front(&_contract);
|
||||
vector<ContractDefinition const*> result = cThreeMerge(input);
|
||||
if (result.empty())
|
||||
BOOST_THROW_EXCEPTION(_contract.createTypeError("Linearization of inheritance graph impossible"));
|
||||
_contract.setLinearizedBaseContracts(result);
|
||||
_contract.annotation().linearizedBaseContracts = result;
|
||||
}
|
||||
|
||||
template <class _T>
|
||||
@ -404,139 +400,5 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
|
||||
enterNewSubScope(_declaration);
|
||||
}
|
||||
|
||||
ReferencesResolver::ReferencesResolver(
|
||||
ASTNode& _root,
|
||||
NameAndTypeResolver& _resolver,
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters,
|
||||
bool _resolveInsideCode,
|
||||
bool _allowLazyTypes
|
||||
):
|
||||
m_resolver(_resolver),
|
||||
m_currentContract(_currentContract),
|
||||
m_returnParameters(_returnParameters),
|
||||
m_resolveInsideCode(_resolveInsideCode),
|
||||
m_allowLazyTypes(_allowLazyTypes)
|
||||
{
|
||||
_root.accept(*this);
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(VariableDeclaration& _variable)
|
||||
{
|
||||
// endVisit because the internal type needs resolving if it is a user defined type
|
||||
// or mapping
|
||||
if (_variable.typeName())
|
||||
{
|
||||
TypePointer type = _variable.typeName()->toType();
|
||||
using Location = VariableDeclaration::Location;
|
||||
Location loc = _variable.referenceLocation();
|
||||
// References are forced to calldata for external function parameters (not return)
|
||||
// and memory for parameters (also return) of publicly visible functions.
|
||||
// They default to memory for function parameters and storage for local variables.
|
||||
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
|
||||
{
|
||||
if (_variable.isExternalCallableParameter())
|
||||
{
|
||||
// force location of external function parameters (not return) to calldata
|
||||
if (loc != Location::Default)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Location has to be calldata for external functions "
|
||||
"(remove the \"memory\" or \"storage\" keyword)."
|
||||
));
|
||||
type = ref->copyForLocation(DataLocation::CallData, true);
|
||||
}
|
||||
else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
|
||||
{
|
||||
// force locations of public or external function (return) parameters to memory
|
||||
if (loc == VariableDeclaration::Location::Storage)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Location has to be memory for publicly visible functions "
|
||||
"(remove the \"storage\" keyword)."
|
||||
));
|
||||
type = ref->copyForLocation(DataLocation::Memory, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_variable.isConstant())
|
||||
{
|
||||
if (loc != Location::Default && loc != Location::Memory)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Storage location has to be \"memory\" (or unspecified) for constants."
|
||||
));
|
||||
loc = Location::Memory;
|
||||
}
|
||||
if (loc == Location::Default)
|
||||
loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage;
|
||||
bool isPointer = !_variable.isStateVariable();
|
||||
type = ref->copyForLocation(
|
||||
loc == Location::Memory ?
|
||||
DataLocation::Memory :
|
||||
DataLocation::Storage,
|
||||
isPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (loc != Location::Default && !ref)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Storage location can only be given for array or struct types."
|
||||
));
|
||||
|
||||
_variable.setType(type);
|
||||
|
||||
if (!_variable.type())
|
||||
BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name"));
|
||||
}
|
||||
else if (!m_allowLazyTypes)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
|
||||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Return& _return)
|
||||
{
|
||||
_return.setFunctionReturnParameters(m_returnParameters);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Mapping&)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(UserDefinedTypeName& _typeName)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_typeName.name());
|
||||
if (declarations.empty())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_typeName.location()) <<
|
||||
errinfo_comment("Undeclared identifier.")
|
||||
);
|
||||
else if (declarations.size() > 1)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_typeName.location()) <<
|
||||
errinfo_comment("Duplicate identifier.")
|
||||
);
|
||||
else
|
||||
_typeName.setReferencedDeclaration(**declarations.begin());
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Identifier& _identifier)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||
if (declarations.empty())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_identifier.location()) <<
|
||||
errinfo_comment("Undeclared identifier.")
|
||||
);
|
||||
else if (declarations.size() == 1)
|
||||
_identifier.setReferencedDeclaration(*declarations.front(), m_currentContract);
|
||||
else
|
||||
_identifier.setOverloadedDeclarations(m_resolver.cleanedDeclarations(_identifier, declarations));
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,10 @@
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
#include <libsolidity/DeclarationContainer.h>
|
||||
#include <libsolidity/ReferencesResolver.h>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
@ -42,13 +43,11 @@ namespace solidity
|
||||
class NameAndTypeResolver: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
explicit NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
|
||||
NameAndTypeResolver(std::vector<Declaration const*> const& _globals);
|
||||
/// Registers all declarations found in the source unit.
|
||||
void registerDeclarations(SourceUnit& _sourceUnit);
|
||||
/// Resolves all names and types referenced from the given contract.
|
||||
void resolveNamesAndTypes(ContractDefinition& _contract);
|
||||
/// Check all type requirements in the given contract.
|
||||
void checkTypeRequirements(ContractDefinition& _contract);
|
||||
/// Updates the given global declaration (used for "this"). Not to be used with declarations
|
||||
/// that create their own scope.
|
||||
void updateDeclaration(Declaration const& _declaration);
|
||||
@ -125,36 +124,5 @@ private:
|
||||
VariableScope* m_currentFunction;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolves references to declarations (of variables and types) and also establishes the link
|
||||
* between a return statement and the return parameter list.
|
||||
*/
|
||||
class ReferencesResolver: private ASTVisitor
|
||||
{
|
||||
public:
|
||||
ReferencesResolver(
|
||||
ASTNode& _root,
|
||||
NameAndTypeResolver& _resolver,
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters,
|
||||
bool _resolveInsideCode = false,
|
||||
bool _allowLazyTypes = true
|
||||
);
|
||||
|
||||
private:
|
||||
virtual void endVisit(VariableDeclaration& _variable) override;
|
||||
virtual bool visit(Block&) override { return m_resolveInsideCode; }
|
||||
virtual bool visit(Identifier& _identifier) override;
|
||||
virtual bool visit(UserDefinedTypeName& _typeName) override;
|
||||
virtual bool visit(Mapping&) override;
|
||||
virtual bool visit(Return& _return) override;
|
||||
|
||||
NameAndTypeResolver& m_resolver;
|
||||
ContractDefinition const* m_currentContract;
|
||||
ParameterList const* m_returnParameters;
|
||||
bool m_resolveInsideCode;
|
||||
bool m_allowLazyTypes;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
225
libsolidity/ReferencesResolver.cpp
Normal file
225
libsolidity/ReferencesResolver.cpp
Normal file
@ -0,0 +1,225 @@
|
||||
/*
|
||||
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 2015
|
||||
* Component that resolves type names to types and annotates the AST accordingly.
|
||||
*/
|
||||
|
||||
#include <libsolidity/ReferencesResolver.h>
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/NameAndTypeResolver.h>
|
||||
#include <libsolidity/Exceptions.h>
|
||||
#include <libsolidity/ConstantEvaluator.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev;
|
||||
using namespace dev::solidity;
|
||||
|
||||
|
||||
ReferencesResolver::ReferencesResolver(
|
||||
ASTNode& _root,
|
||||
NameAndTypeResolver& _resolver,
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters,
|
||||
bool _resolveInsideCode
|
||||
):
|
||||
m_resolver(_resolver),
|
||||
m_currentContract(_currentContract),
|
||||
m_returnParameters(_returnParameters),
|
||||
m_resolveInsideCode(_resolveInsideCode)
|
||||
{
|
||||
_root.accept(*this);
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Return const& _return)
|
||||
{
|
||||
_return.annotation().functionReturnParameters = m_returnParameters;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_typeName.name());
|
||||
if (declarations.empty())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_typeName.location()) <<
|
||||
errinfo_comment("Undeclared identifier.")
|
||||
);
|
||||
else if (declarations.size() > 1)
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_typeName.location()) <<
|
||||
errinfo_comment("Duplicate identifier.")
|
||||
);
|
||||
Declaration const* declaration = *declarations.begin();
|
||||
_typeName.annotation().referencedDeclaration = declaration;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReferencesResolver::visit(Identifier const& _identifier)
|
||||
{
|
||||
auto declarations = m_resolver.nameFromCurrentScope(_identifier.name());
|
||||
if (declarations.empty())
|
||||
BOOST_THROW_EXCEPTION(
|
||||
DeclarationError() <<
|
||||
errinfo_sourceLocation(_identifier.location()) <<
|
||||
errinfo_comment("Undeclared identifier.")
|
||||
);
|
||||
else if (declarations.size() == 1)
|
||||
{
|
||||
_identifier.annotation().referencedDeclaration = declarations.front();
|
||||
_identifier.annotation().contractScope = m_currentContract;
|
||||
}
|
||||
else
|
||||
_identifier.annotation().overloadedDeclarations =
|
||||
m_resolver.cleanedDeclarations(_identifier, declarations);
|
||||
return false;
|
||||
}
|
||||
|
||||
void ReferencesResolver::endVisit(VariableDeclaration const& _variable)
|
||||
{
|
||||
if (_variable.annotation().type)
|
||||
return;
|
||||
|
||||
TypePointer type;
|
||||
if (_variable.typeName())
|
||||
{
|
||||
type = typeFor(*_variable.typeName());
|
||||
using Location = VariableDeclaration::Location;
|
||||
Location loc = _variable.referenceLocation();
|
||||
// References are forced to calldata for external function parameters (not return)
|
||||
// and memory for parameters (also return) of publicly visible functions.
|
||||
// They default to memory for function parameters and storage for local variables.
|
||||
if (auto ref = dynamic_cast<ReferenceType const*>(type.get()))
|
||||
{
|
||||
if (_variable.isExternalCallableParameter())
|
||||
{
|
||||
// force location of external function parameters (not return) to calldata
|
||||
if (loc != Location::Default)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Location has to be calldata for external functions "
|
||||
"(remove the \"memory\" or \"storage\" keyword)."
|
||||
));
|
||||
type = ref->copyForLocation(DataLocation::CallData, true);
|
||||
}
|
||||
else if (_variable.isCallableParameter() && _variable.scope()->isPublic())
|
||||
{
|
||||
// force locations of public or external function (return) parameters to memory
|
||||
if (loc == VariableDeclaration::Location::Storage)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Location has to be memory for publicly visible functions "
|
||||
"(remove the \"storage\" keyword)."
|
||||
));
|
||||
type = ref->copyForLocation(DataLocation::Memory, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_variable.isConstant())
|
||||
{
|
||||
if (loc != Location::Default && loc != Location::Memory)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Storage location has to be \"memory\" (or unspecified) for constants."
|
||||
));
|
||||
loc = Location::Memory;
|
||||
}
|
||||
if (loc == Location::Default)
|
||||
loc = _variable.isCallableParameter() ? Location::Memory : Location::Storage;
|
||||
bool isPointer = !_variable.isStateVariable();
|
||||
type = ref->copyForLocation(
|
||||
loc == Location::Memory ?
|
||||
DataLocation::Memory :
|
||||
DataLocation::Storage,
|
||||
isPointer
|
||||
);
|
||||
}
|
||||
}
|
||||
else if (loc != Location::Default && !ref)
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError(
|
||||
"Storage location can only be given for array or struct types."
|
||||
));
|
||||
|
||||
if (!type)
|
||||
BOOST_THROW_EXCEPTION(_variable.typeName()->createTypeError("Invalid type name."));
|
||||
|
||||
}
|
||||
else if (!_variable.canHaveAutoType())
|
||||
BOOST_THROW_EXCEPTION(_variable.createTypeError("Explicit type needed."));
|
||||
// otherwise we have a "var"-declaration whose type is resolved by the first assignment
|
||||
|
||||
_variable.annotation().type = type;
|
||||
}
|
||||
|
||||
TypePointer ReferencesResolver::typeFor(TypeName const& _typeName)
|
||||
{
|
||||
if (_typeName.annotation().type)
|
||||
return _typeName.annotation().type;
|
||||
|
||||
TypePointer type;
|
||||
if (auto elemTypeName = dynamic_cast<ElementaryTypeName const*>(&_typeName))
|
||||
type = Type::fromElementaryTypeName(elemTypeName->typeName());
|
||||
else if (auto typeName = dynamic_cast<UserDefinedTypeName const*>(&_typeName))
|
||||
{
|
||||
Declaration const* declaration = typeName->annotation().referencedDeclaration;
|
||||
solAssert(!!declaration, "");
|
||||
|
||||
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
|
||||
type = make_shared<StructType>(*structDef);
|
||||
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
|
||||
type = make_shared<EnumType>(*enumDef);
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
type = make_shared<ContractType>(*contract);
|
||||
else
|
||||
BOOST_THROW_EXCEPTION(typeName->createTypeError(
|
||||
"Name has to refer to a struct, enum or contract."
|
||||
));
|
||||
}
|
||||
else if (auto mapping = dynamic_cast<Mapping const*>(&_typeName))
|
||||
{
|
||||
TypePointer keyType = typeFor(mapping->keyType());
|
||||
TypePointer valueType = typeFor(mapping->valueType());
|
||||
// Convert key type to memory.
|
||||
keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
|
||||
// Convert value type to storage reference.
|
||||
valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
|
||||
type = make_shared<MappingType>(keyType, valueType);
|
||||
}
|
||||
else if (auto arrayType = dynamic_cast<ArrayTypeName const*>(&_typeName))
|
||||
{
|
||||
TypePointer baseType = typeFor(arrayType->baseType());
|
||||
if (baseType->storageBytes() == 0)
|
||||
BOOST_THROW_EXCEPTION(arrayType->baseType().createTypeError(
|
||||
"Illegal base type of storage size zero for array."
|
||||
));
|
||||
if (Expression const* length = arrayType->length())
|
||||
{
|
||||
if (!length->annotation().type)
|
||||
ConstantEvaluator e(*length);
|
||||
|
||||
auto const* lengthType = dynamic_cast<IntegerConstantType const*>(length->annotation().type.get());
|
||||
if (!lengthType)
|
||||
BOOST_THROW_EXCEPTION(length->createTypeError("Invalid array length."));
|
||||
type = make_shared<ArrayType>(DataLocation::Storage, baseType, lengthType->literalValue(nullptr));
|
||||
}
|
||||
else
|
||||
type = make_shared<ArrayType>(DataLocation::Storage, baseType);
|
||||
}
|
||||
|
||||
return _typeName.annotation().type = move(type);
|
||||
}
|
||||
|
69
libsolidity/ReferencesResolver.h
Normal file
69
libsolidity/ReferencesResolver.h
Normal file
@ -0,0 +1,69 @@
|
||||
/*
|
||||
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
|
||||
* Component that resolves type names to types and annotates the AST accordingly.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <list>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
class NameAndTypeResolver;
|
||||
|
||||
/**
|
||||
* Resolves references to declarations (of variables and types) and also establishes the link
|
||||
* between a return statement and the return parameter list.
|
||||
*/
|
||||
class ReferencesResolver: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
ReferencesResolver(
|
||||
ASTNode& _root,
|
||||
NameAndTypeResolver& _resolver,
|
||||
ContractDefinition const* _currentContract,
|
||||
ParameterList const* _returnParameters,
|
||||
bool _resolveInsideCode = false
|
||||
);
|
||||
|
||||
private:
|
||||
virtual bool visit(Block const&) override { return m_resolveInsideCode; }
|
||||
virtual bool visit(Identifier const& _identifier) override;
|
||||
virtual bool visit(UserDefinedTypeName const& _typeName) override;
|
||||
virtual bool visit(Return const& _return) override;
|
||||
virtual void endVisit(VariableDeclaration const& _variable) override;
|
||||
|
||||
TypePointer typeFor(TypeName const& _typeName);
|
||||
|
||||
NameAndTypeResolver& m_resolver;
|
||||
ContractDefinition const* m_currentContract;
|
||||
ParameterList const* m_returnParameters;
|
||||
bool m_resolveInsideCode;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
1143
libsolidity/TypeChecker.cpp
Normal file
1143
libsolidity/TypeChecker.cpp
Normal file
File diff suppressed because it is too large
Load Diff
114
libsolidity/TypeChecker.h
Normal file
114
libsolidity/TypeChecker.h
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
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 2015
|
||||
* Type analyzer and checker.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <libsolidity/TypeChecker.h>
|
||||
#include <libsolidity/Types.h>
|
||||
#include <libsolidity/ASTAnnotations.h>
|
||||
#include <libsolidity/ASTForward.h>
|
||||
#include <libsolidity/ASTVisitor.h>
|
||||
|
||||
namespace dev
|
||||
{
|
||||
namespace solidity
|
||||
{
|
||||
|
||||
|
||||
/**
|
||||
* The module that performs type analysis on the AST, checks the applicability of operations on
|
||||
* those types and stores errors for invalid operations.
|
||||
* Provides a way to retrieve the type of an AST node.
|
||||
*/
|
||||
class TypeChecker: private ASTConstVisitor
|
||||
{
|
||||
public:
|
||||
/// Performs type checking on the given contract and all of its sub-nodes.
|
||||
/// @returns true iff all checks passed.
|
||||
bool checkTypeRequirements(ContractDefinition const& _contract);
|
||||
|
||||
/// @returns the list of errors found during type checking.
|
||||
std::vector<std::shared_ptr<Error const>> const& errors() const { return m_errors; }
|
||||
|
||||
/// @returns the type of an expression and asserts that it is present.
|
||||
TypePointer const& type(Expression const& _expression) const;
|
||||
/// @returns the type of the given variable and throws if the type is not present
|
||||
/// (this can happen for variables with non-explicit types before their types are resolved)
|
||||
TypePointer const& type(VariableDeclaration const& _variable) const;
|
||||
|
||||
/// Adds a new error to the list of errors.
|
||||
void typeError(ASTNode const& _node, std::string const& _description);
|
||||
/// Adds a new error to the list of errors and throws to abort type checking.
|
||||
void fatalTypeError(ASTNode const& _node, std::string const& _description);
|
||||
|
||||
private:
|
||||
virtual bool visit(ContractDefinition const& _contract) override;
|
||||
/// Checks that two functions defined in this contract with the same name have different
|
||||
/// arguments and that there is at most one constructor.
|
||||
void checkContractDuplicateFunctions(ContractDefinition const& _contract);
|
||||
void checkContractIllegalOverrides(ContractDefinition const& _contract);
|
||||
void checkContractAbstractFunctions(ContractDefinition const& _contract);
|
||||
void checkContractAbstractConstructors(ContractDefinition const& _contract);
|
||||
/// Checks that different functions with external visibility end up having different
|
||||
/// external argument types (i.e. different signature).
|
||||
void checkContractExternalTypeClashes(ContractDefinition const& _contract);
|
||||
/// Checks that all requirements for a library are fulfilled if this is a library.
|
||||
void checkLibraryRequirements(ContractDefinition const& _contract);
|
||||
|
||||
virtual void endVisit(InheritanceSpecifier const& _inheritance) override;
|
||||
virtual bool visit(StructDefinition const& _struct) override;
|
||||
virtual bool visit(FunctionDefinition const& _function) override;
|
||||
virtual bool visit(VariableDeclaration const& _variable) override;
|
||||
/// We need to do this manually because we want to pass the bases of the current contract in
|
||||
/// case this is a base constructor call.
|
||||
void visitManually(ModifierInvocation const& _modifier, std::vector<ContractDefinition const*> const& _bases);
|
||||
virtual bool visit(EventDefinition const& _eventDef) override;
|
||||
virtual bool visit(IfStatement const& _ifStatement) override;
|
||||
virtual bool visit(WhileStatement const& _whileStatement) override;
|
||||
virtual bool visit(ForStatement const& _forStatement) override;
|
||||
virtual void endVisit(Return const& _return) override;
|
||||
virtual void endVisit(ExpressionStatement const& _statement) override;
|
||||
virtual bool visit(Assignment const& _assignment) override;
|
||||
virtual void endVisit(BinaryOperation const& _operation) override;
|
||||
virtual bool visit(UnaryOperation const& _operation) override;
|
||||
virtual bool visit(FunctionCall const& _functionCall) override;
|
||||
virtual void endVisit(NewExpression const& _newExpression) override;
|
||||
virtual bool visit(MemberAccess const& _memberAccess) override;
|
||||
virtual bool visit(IndexAccess const& _indexAccess) override;
|
||||
virtual bool visit(Identifier const& _identifier) override;
|
||||
virtual void endVisit(ElementaryTypeNameExpression const& _expr) override;
|
||||
virtual void endVisit(Literal const& _literal) override;
|
||||
|
||||
/// @returns the referenced declaration and throws on error.
|
||||
Declaration const& dereference(Identifier const& _identifier);
|
||||
|
||||
/// Runs type checks on @a _expression to infer its type and then checks that it is implicitly
|
||||
/// convertible to @a _expectedType.
|
||||
void expectType(Expression const& _expression, Type const& _expectedType);
|
||||
/// Runs type checks on @a _expression to infer its type and then checks that it is an LValue.
|
||||
void requireLValue(Expression const& _expression);
|
||||
|
||||
std::vector<std::shared_ptr<Error const>> m_errors;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
@ -157,55 +157,6 @@ TypePointer Type::fromElementaryTypeName(string const& _name)
|
||||
return fromElementaryTypeName(Token::fromIdentifierOrKeyword(_name));
|
||||
}
|
||||
|
||||
TypePointer Type::fromUserDefinedTypeName(UserDefinedTypeName const& _typeName)
|
||||
{
|
||||
Declaration const* declaration = _typeName.referencedDeclaration();
|
||||
if (StructDefinition const* structDef = dynamic_cast<StructDefinition const*>(declaration))
|
||||
return make_shared<StructType>(*structDef);
|
||||
else if (EnumDefinition const* enumDef = dynamic_cast<EnumDefinition const*>(declaration))
|
||||
return make_shared<EnumType>(*enumDef);
|
||||
else if (FunctionDefinition const* function = dynamic_cast<FunctionDefinition const*>(declaration))
|
||||
return make_shared<FunctionType>(*function);
|
||||
else if (ContractDefinition const* contract = dynamic_cast<ContractDefinition const*>(declaration))
|
||||
return make_shared<ContractType>(*contract);
|
||||
return TypePointer();
|
||||
}
|
||||
|
||||
TypePointer Type::fromMapping(ElementaryTypeName& _keyType, TypeName& _valueType)
|
||||
{
|
||||
TypePointer keyType = _keyType.toType();
|
||||
if (!keyType)
|
||||
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Error resolving type name."));
|
||||
TypePointer valueType = _valueType.toType();
|
||||
if (!valueType)
|
||||
BOOST_THROW_EXCEPTION(_valueType.createTypeError("Invalid type name."));
|
||||
// Convert value type to storage reference.
|
||||
valueType = ReferenceType::copyForLocationIfReference(DataLocation::Storage, valueType);
|
||||
// Convert key type to memory.
|
||||
keyType = ReferenceType::copyForLocationIfReference(DataLocation::Memory, keyType);
|
||||
return make_shared<MappingType>(keyType, valueType);
|
||||
}
|
||||
|
||||
TypePointer Type::fromArrayTypeName(TypeName& _baseTypeName, Expression* _length)
|
||||
{
|
||||
TypePointer baseType = _baseTypeName.toType();
|
||||
if (!baseType)
|
||||
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Invalid type name."));
|
||||
if (baseType->storageBytes() == 0)
|
||||
BOOST_THROW_EXCEPTION(_baseTypeName.createTypeError("Illegal base type of storage size zero for array."));
|
||||
if (_length)
|
||||
{
|
||||
if (!_length->type())
|
||||
_length->checkTypeRequirements(nullptr);
|
||||
auto const* length = dynamic_cast<IntegerConstantType const*>(_length->type().get());
|
||||
if (!length)
|
||||
BOOST_THROW_EXCEPTION(_length->createTypeError("Invalid array length."));
|
||||
return make_shared<ArrayType>(DataLocation::Storage, baseType, length->literalValue(nullptr));
|
||||
}
|
||||
else
|
||||
return make_shared<ArrayType>(DataLocation::Storage, baseType);
|
||||
}
|
||||
|
||||
TypePointer Type::forLiteral(Literal const& _literal)
|
||||
{
|
||||
switch (_literal.token())
|
||||
@ -686,7 +637,7 @@ bool ContractType::isImplicitlyConvertibleTo(Type const& _convertTo) const
|
||||
return dynamic_cast<IntegerType const&>(_convertTo).isAddress();
|
||||
if (_convertTo.category() == Category::Contract)
|
||||
{
|
||||
auto const& bases = contractDefinition().linearizedBaseContracts();
|
||||
auto const& bases = contractDefinition().annotation().linearizedBaseContracts;
|
||||
if (m_super && bases.size() <= 1)
|
||||
return false;
|
||||
return find(m_super ? ++bases.begin() : bases.begin(), bases.end(),
|
||||
@ -944,7 +895,7 @@ MemberList const& ContractType::members() const
|
||||
if (m_super)
|
||||
{
|
||||
// add the most derived of all functions which are visible in derived contracts
|
||||
for (ContractDefinition const* base: m_contract.linearizedBaseContracts())
|
||||
for (ContractDefinition const* base: m_contract.annotation().linearizedBaseContracts)
|
||||
for (ASTPointer<FunctionDefinition> const& function: base->definedFunctions())
|
||||
{
|
||||
if (!function->isVisibleInDerivedContracts())
|
||||
@ -998,13 +949,13 @@ shared_ptr<FunctionType const> const& ContractType::constructorType() const
|
||||
vector<tuple<VariableDeclaration const*, u256, unsigned>> ContractType::stateVariables() const
|
||||
{
|
||||
vector<VariableDeclaration const*> variables;
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.linearizedBaseContracts()))
|
||||
for (ContractDefinition const* contract: boost::adaptors::reverse(m_contract.annotation().linearizedBaseContracts))
|
||||
for (ASTPointer<VariableDeclaration> const& variable: contract->stateVariables())
|
||||
if (!variable->isConstant())
|
||||
variables.push_back(variable.get());
|
||||
TypePointers types;
|
||||
for (auto variable: variables)
|
||||
types.push_back(variable->type());
|
||||
types.push_back(variable->annotation().type);
|
||||
StorageOffsets offsets;
|
||||
offsets.computeOffsets(types);
|
||||
|
||||
@ -1082,7 +1033,7 @@ MemberList const& StructType::members() const
|
||||
MemberList::MemberMap members;
|
||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||
{
|
||||
TypePointer type = variable->type();
|
||||
TypePointer type = variable->annotation().type;
|
||||
// Skip all mapping members if we are not in storage.
|
||||
if (location() != DataLocation::Storage && !type->canLiveOutsideStorage())
|
||||
continue;
|
||||
@ -1147,7 +1098,7 @@ set<string> StructType::membersMissingInMemory() const
|
||||
{
|
||||
set<string> missing;
|
||||
for (ASTPointer<VariableDeclaration> const& variable: m_struct.members())
|
||||
if (!variable->type()->canLiveOutsideStorage())
|
||||
if (!variable->annotation().type->canLiveOutsideStorage())
|
||||
missing.insert(variable->name());
|
||||
return missing;
|
||||
}
|
||||
@ -1211,14 +1162,14 @@ FunctionType::FunctionType(FunctionDefinition const& _function, bool _isInternal
|
||||
for (ASTPointer<VariableDeclaration> const& var: _function.parameters())
|
||||
{
|
||||
paramNames.push_back(var->name());
|
||||
params.push_back(var->type());
|
||||
params.push_back(var->annotation().type);
|
||||
}
|
||||
retParams.reserve(_function.returnParameters().size());
|
||||
retParamNames.reserve(_function.returnParameters().size());
|
||||
for (ASTPointer<VariableDeclaration> const& var: _function.returnParameters())
|
||||
{
|
||||
retParamNames.push_back(var->name());
|
||||
retParams.push_back(var->type());
|
||||
retParams.push_back(var->annotation().type);
|
||||
}
|
||||
swap(params, m_parameterTypes);
|
||||
swap(paramNames, m_parameterNames);
|
||||
@ -1231,7 +1182,7 @@ FunctionType::FunctionType(VariableDeclaration const& _varDecl):
|
||||
{
|
||||
TypePointers paramTypes;
|
||||
vector<string> paramNames;
|
||||
auto returnType = _varDecl.type();
|
||||
auto returnType = _varDecl.annotation().type;
|
||||
|
||||
while (true)
|
||||
{
|
||||
@ -1293,7 +1244,7 @@ FunctionType::FunctionType(const EventDefinition& _event):
|
||||
for (ASTPointer<VariableDeclaration> const& var: _event.parameters())
|
||||
{
|
||||
paramNames.push_back(var->name());
|
||||
params.push_back(var->type());
|
||||
params.push_back(var->annotation().type);
|
||||
}
|
||||
swap(params, m_parameterTypes);
|
||||
swap(paramNames, m_parameterNames);
|
||||
@ -1662,7 +1613,7 @@ MemberList const& TypeType::members() const
|
||||
));
|
||||
else if (m_currentContract != nullptr)
|
||||
{
|
||||
vector<ContractDefinition const*> currentBases = m_currentContract->linearizedBaseContracts();
|
||||
auto const& currentBases = m_currentContract->annotation().linearizedBaseContracts;
|
||||
if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end())
|
||||
// We are accessing the type of a base contract, so add all public and protected
|
||||
// members. Note that this does not add inherited functions on purpose.
|
||||
@ -1687,7 +1638,7 @@ ModifierType::ModifierType(const ModifierDefinition& _modifier)
|
||||
TypePointers params;
|
||||
params.reserve(_modifier.parameters().size());
|
||||
for (ASTPointer<VariableDeclaration> const& var: _modifier.parameters())
|
||||
params.push_back(var->type());
|
||||
params.push_back(var->annotation().type);
|
||||
swap(params, m_parameterTypes);
|
||||
}
|
||||
|
||||
|
@ -490,7 +490,12 @@ bool CommandLineInterface::processInput()
|
||||
// TODO: Perhaps we should not compile unless requested
|
||||
bool optimize = m_args.count("optimize") > 0;
|
||||
unsigned runs = m_args["optimize-runs"].as<unsigned>();
|
||||
m_compiler->compile(optimize, runs);
|
||||
if (!m_compiler->compile(optimize, runs))
|
||||
{
|
||||
for (auto const& error: m_compiler->errors())
|
||||
SourceReferenceFormatter::printExceptionInformation(cerr, *error, "Error", *m_compiler);
|
||||
return false;
|
||||
}
|
||||
m_compiler->link(m_libraries);
|
||||
}
|
||||
catch (ParserError const& _exception)
|
||||
|
@ -46,10 +46,7 @@ string formatError(Exception const& _exception, string const& _name, CompilerSta
|
||||
{
|
||||
ostringstream errorOutput;
|
||||
SourceReferenceFormatter::printExceptionInformation(errorOutput, _exception, _name, _compiler);
|
||||
|
||||
Json::Value output(Json::objectValue);
|
||||
output["error"] = errorOutput.str();
|
||||
return Json::FastWriter().write(output);
|
||||
return errorOutput.str();
|
||||
}
|
||||
|
||||
Json::Value functionHashes(ContractDefinition const& _contract)
|
||||
@ -123,43 +120,52 @@ string compile(string _input, bool _optimize)
|
||||
sources[""] = _input;
|
||||
|
||||
Json::Value output(Json::objectValue);
|
||||
Json::Value errors(Json::arrayValue);
|
||||
CompilerStack compiler;
|
||||
try
|
||||
{
|
||||
compiler.compile(_input, _optimize);
|
||||
if (!compiler.compile(_input, _optimize))
|
||||
{
|
||||
for (auto const& error: compiler.errors())
|
||||
errors.append(formatError(*error, "Error", compiler));
|
||||
}
|
||||
}
|
||||
catch (ParserError const& exception)
|
||||
{
|
||||
return formatError(exception, "Parser error", compiler);
|
||||
errors.append(formatError(exception, "Parser error", compiler));
|
||||
}
|
||||
catch (DeclarationError const& exception)
|
||||
{
|
||||
return formatError(exception, "Declaration error", compiler);
|
||||
errors.append(formatError(exception, "Declaration error", compiler));
|
||||
}
|
||||
catch (TypeError const& exception)
|
||||
{
|
||||
return formatError(exception, "Type error", compiler);
|
||||
errors.append(formatError(exception, "Type error", compiler));
|
||||
}
|
||||
catch (CompilerError const& exception)
|
||||
{
|
||||
return formatError(exception, "Compiler error", compiler);
|
||||
errors.append(formatError(exception, "Compiler error", compiler));
|
||||
}
|
||||
catch (InternalCompilerError const& exception)
|
||||
{
|
||||
return formatError(exception, "Internal compiler error", compiler);
|
||||
errors.append(formatError(exception, "Internal compiler error", compiler));
|
||||
}
|
||||
catch (DocstringParsingError const& exception)
|
||||
{
|
||||
return formatError(exception, "Documentation parsing error", compiler);
|
||||
errors.append(formatError(exception, "Documentation parsing error", compiler));
|
||||
}
|
||||
catch (Exception const& exception)
|
||||
{
|
||||
output["error"] = "Exception during compilation: " + boost::diagnostic_information(exception);
|
||||
return Json::FastWriter().write(output);
|
||||
errors.append("Exception during compilation: " + boost::diagnostic_information(exception));
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
output["error"] = "Unknown exception during compilation.";
|
||||
errors.append("Unknown exception during compilation.");
|
||||
}
|
||||
|
||||
if (errors.size() > 0)
|
||||
{
|
||||
output["errors"] = errors;
|
||||
return Json::FastWriter().write(output);
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
#include <libsolidity/NameAndTypeResolver.h>
|
||||
#include <libsolidity/Compiler.h>
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/TypeChecker.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace dev::eth;
|
||||
@ -60,7 +61,8 @@ eth::AssemblyItems compileContract(const string& _sourceCode)
|
||||
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
BOOST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract));
|
||||
TypeChecker checker;
|
||||
BOOST_REQUIRE_NO_THROW(checker.checkTypeRequirements(*contract));
|
||||
}
|
||||
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
|
@ -1999,7 +1999,7 @@ BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance)
|
||||
BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(23));
|
||||
}
|
||||
|
||||
BOOST_AUTO_TEST_CASE(explicit_base_cass)
|
||||
BOOST_AUTO_TEST_CASE(explicit_base_class)
|
||||
{
|
||||
char const* sourceCode = R"(
|
||||
contract BaseBase { function g() returns (uint r) { return 1; } }
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <libsolidity/CompilerContext.h>
|
||||
#include <libsolidity/ExpressionCompiler.h>
|
||||
#include <libsolidity/AST.h>
|
||||
#include <libsolidity/TypeChecker.h>
|
||||
#include "../TestHelper.h"
|
||||
|
||||
using namespace std;
|
||||
@ -117,7 +118,8 @@ bytes compileFirstExpression(const string& _sourceCode, vector<vector<string>> _
|
||||
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
{
|
||||
ETH_TEST_REQUIRE_NO_THROW(resolver.checkTypeRequirements(*contract), "Checking type Requirements failed");
|
||||
TypeChecker typeChecker;
|
||||
BOOST_REQUIRE(typeChecker.checkTypeRequirements(*contract));
|
||||
}
|
||||
for (ASTPointer<ASTNode> const& node: sourceUnit->nodes())
|
||||
if (ContractDefinition* contract = dynamic_cast<ContractDefinition*>(node.get()))
|
||||
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user