aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChristian <c@ethdev.com>2014-11-13 08:12:57 +0800
committerChristian <c@ethdev.com>2014-11-14 21:08:14 +0800
commitc560a62352b8ba1a106ec06aedf779df06af3a22 (patch)
tree325f19538a1239529e68d0126341e0f6aa655ef7
parent46dd62982084dfe5712292b88047d2a58e0a420e (diff)
downloaddexon-solidity-c560a62352b8ba1a106ec06aedf779df06af3a22.tar.gz
dexon-solidity-c560a62352b8ba1a106ec06aedf779df06af3a22.tar.zst
dexon-solidity-c560a62352b8ba1a106ec06aedf779df06af3a22.zip
Struct types.
-rw-r--r--AST.cpp13
-rw-r--r--AST.h5
-rw-r--r--ExpressionCompiler.cpp17
-rw-r--r--NameAndTypeResolver.cpp29
-rw-r--r--NameAndTypeResolver.h11
-rw-r--r--Types.cpp55
-rw-r--r--Types.h16
7 files changed, 126 insertions, 20 deletions
diff --git a/AST.cpp b/AST.cpp
index d5f856df..e8bdecf3 100644
--- a/AST.cpp
+++ b/AST.cpp
@@ -460,8 +460,17 @@ bool FunctionCall::isTypeConversion() const
void MemberAccess::checkTypeRequirements()
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access not yet implemented."));
- // m_type = ;
+ m_expression->checkTypeRequirements();
+ m_expression->requireLValue();
+ if (m_expression->getType()->getCategory() != Type::Category::STRUCT)
+ BOOST_THROW_EXCEPTION(createTypeError("Member access to a non-struct (is " +
+ m_expression->getType()->toString() + ")"));
+ StructType const& type = dynamic_cast<StructType const&>(*m_expression->getType());
+ unsigned memberIndex = type.memberNameToIndex(*m_memberName);
+ if (memberIndex >= type.getMemberCount())
+ BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString()));
+ m_type = type.getMemberByIndex(memberIndex).getType();
+ m_isLvalue = true;
}
void IndexAccess::checkTypeRequirements()
diff --git a/AST.h b/AST.h
index 31ca56f7..80c7dd19 100644
--- a/AST.h
+++ b/AST.h
@@ -146,7 +146,7 @@ private:
/**
* Parameter list, used as function parameter list and return list.
* None of the parameters is allowed to contain mappings (not even recursively
- * inside structs), but (@todo) this is not yet enforced.
+ * inside structs).
*/
class ParameterList: public ASTNode
{
@@ -368,7 +368,6 @@ private:
/**
* Statement in which a break statement is legal.
- * @todo actually check this requirement.
*/
class BreakableStatement: public Statement
{
@@ -629,6 +628,7 @@ public:
ASTPointer<ASTString> const& _memberName):
Expression(_location), m_expression(_expression), m_memberName(_memberName) {}
virtual void accept(ASTVisitor& _visitor) override;
+ Expression& getExpression() const { return *m_expression; }
ASTString const& getMemberName() const { return *m_memberName; }
virtual void checkTypeRequirements() override;
@@ -651,6 +651,7 @@ public:
Expression& getBaseExpression() const { return *m_base; }
Expression& getIndexExpression() const { return *m_index; }
+
private:
ASTPointer<Expression> m_base;
ASTPointer<Expression> m_index;
diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp
index d80b42b3..f37ce39c 100644
--- a/ExpressionCompiler.cpp
+++ b/ExpressionCompiler.cpp
@@ -49,7 +49,6 @@ bool ExpressionCompiler::visit(Assignment& _assignment)
{
_assignment.getRightHandSide().accept(*this);
appendTypeConversion(*_assignment.getRightHandSide().getType(), *_assignment.getType());
- m_currentLValue.reset();
_assignment.getLeftHandSide().accept(*this);
if (asserts(m_currentLValue.isValid()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("LValue not retrieved."));
@@ -63,6 +62,7 @@ bool ExpressionCompiler::visit(Assignment& _assignment)
appendOrdinaryBinaryOperatorCode(Token::AssignmentToBinaryOp(op), *_assignment.getType());
}
m_currentLValue.storeValue(_assignment);
+ m_currentLValue.reset();
return false;
}
@@ -90,6 +90,7 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation);
+ m_currentLValue.reset();
break;
case Token::INC: // ++ (pre- or postfix)
case Token::DEC: // -- (pre- or postfix)
@@ -113,6 +114,7 @@ void ExpressionCompiler::endVisit(UnaryOperation& _unaryOperation)
if (m_currentLValue.storesReferenceOnStack())
m_context << eth::Instruction::SWAP1;
m_currentLValue.storeValue(_unaryOperation, !_unaryOperation.isPrefixOperation());
+ m_currentLValue.reset();
break;
case Token::ADD: // +
// unary add, so basically no-op
@@ -182,10 +184,10 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
arguments[i]->accept(*this);
appendTypeConversion(*arguments[i]->getType(), *function.getParameters()[i]->getType());
}
- m_currentLValue.reset();
_functionCall.getExpression().accept(*this);
if (asserts(m_currentLValue.isInCode()))
BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Code reference expected."));
+ m_currentLValue.reset();
m_context.appendJump();
m_context << returnLabel;
@@ -201,9 +203,16 @@ bool ExpressionCompiler::visit(FunctionCall& _functionCall)
return false;
}
-void ExpressionCompiler::endVisit(MemberAccess&)
+void ExpressionCompiler::endVisit(MemberAccess& _memberAccess)
{
- BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access not yet implemented."));
+ if (asserts(m_currentLValue.isInStorage()))
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to a non-storage value."));
+ StructType const& type = dynamic_cast<StructType const&>(*_memberAccess.getExpression().getType());
+ unsigned memberIndex = type.memberNameToIndex(_memberAccess.getMemberName());
+ if (asserts(memberIndex <= type.getMemberCount()))
+ BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member not found in struct during compilation."));
+ m_context << type.getStorageOffsetOfMember(memberIndex) << eth::Instruction::ADD;
+ m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess);
}
bool ExpressionCompiler::visit(IndexAccess& _indexAccess)
diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp
index 4a15fe79..5bc40685 100644
--- a/NameAndTypeResolver.cpp
+++ b/NameAndTypeResolver.cpp
@@ -37,7 +37,10 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract)
reset();
DeclarationRegistrationHelper registrar(m_scopes, _contract);
m_currentScope = &m_scopes[&_contract];
- //@todo structs
+ for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
+ ReferencesResolver resolver(*structDef, *this, nullptr);
+ for (ASTPointer<StructDefinition> const& structDef: _contract.getDefinedStructs())
+ checkForRecursion(*structDef);
for (ASTPointer<VariableDeclaration> const& variable: _contract.getStateVariables())
ReferencesResolver resolver(*variable, *this, nullptr);
for (ASTPointer<FunctionDefinition> const& function: _contract.getDefinedFunctions())
@@ -70,6 +73,24 @@ Declaration* NameAndTypeResolver::getNameFromCurrentScope(ASTString const& _name
return m_currentScope->resolveName(_name, _recursive);
}
+void NameAndTypeResolver::checkForRecursion(StructDefinition const& _struct)
+{
+ set<StructDefinition const*> definitionsSeen;
+ vector<StructDefinition const*> queue = {&_struct};
+ while (!queue.empty())
+ {
+ StructDefinition const* def = queue.back();
+ queue.pop_back();
+ if (definitionsSeen.count(def))
+ BOOST_THROW_EXCEPTION(ParserError() << errinfo_sourceLocation(def->getLocation())
+ << errinfo_comment("Recursive struct definition."));
+ definitionsSeen.insert(def);
+ for (ASTPointer<VariableDeclaration> const& member: def->getMembers())
+ if (member->getType()->getCategory() == Type::Category::STRUCT)
+ queue.push_back(dynamic_cast<UserDefinedTypeName&>(*member->getTypeName()).getReferencedStruct());
+ }
+}
+
void NameAndTypeResolver::reset()
{
m_scopes.clear();
@@ -163,8 +184,8 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio
}
ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
- ParameterList* _returnParameters):
- m_resolver(_resolver), m_returnParameters(_returnParameters)
+ ParameterList* _returnParameters, bool _allowLazyTypes):
+ m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes)
{
_root.accept(*this);
}
@@ -175,6 +196,8 @@ void ReferencesResolver::endVisit(VariableDeclaration& _variable)
// or mapping
if (_variable.getTypeName())
_variable.setType(_variable.getTypeName()->toType());
+ 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
}
diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h
index 90902494..d335807e 100644
--- a/NameAndTypeResolver.h
+++ b/NameAndTypeResolver.h
@@ -55,10 +55,13 @@ public:
Declaration* getNameFromCurrentScope(ASTString const& _name, bool _recursive = true);
private:
+ /// Throws if @a _struct contains a recursive loop. Note that recursion via mappings is fine.
+ void checkForRecursion(StructDefinition const& _struct);
void reset();
- /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition, FunctionDeclaration and
- /// StructDefinition (@todo not yet implemented), where nullptr denotes the global scope.
+ /// Maps nodes declaring a scope to scopes, i.e. ContractDefinition and FunctionDeclaration,
+ /// where nullptr denotes the global scope. Note that structs are not scope since they do
+ /// not contain code.
std::map<ASTNode const*, Scope> m_scopes;
Scope* m_currentScope;
@@ -99,7 +102,8 @@ private:
class ReferencesResolver: private ASTVisitor
{
public:
- ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, ParameterList* _returnParameters);
+ ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver,
+ ParameterList* _returnParameters, bool _allowLazyTypes = true);
private:
virtual void endVisit(VariableDeclaration& _variable) override;
@@ -110,6 +114,7 @@ private:
NameAndTypeResolver& m_resolver;
ParameterList* m_returnParameters;
+ bool m_allowLazyTypes;
};
}
diff --git a/Types.cpp b/Types.cpp
index e37ed3e5..63bad5d6 100644
--- a/Types.cpp
+++ b/Types.cpp
@@ -80,7 +80,7 @@ shared_ptr<Type> Type::forLiteral(Literal const& _literal)
case Token::NUMBER:
return IntegerType::smallestTypeForLiteral(_literal.getValue());
case Token::STRING_LITERAL:
- return shared_ptr<Type>(); // @todo
+ return shared_ptr<Type>(); // @todo add string literals
default:
return shared_ptr<Type>();
}
@@ -231,6 +231,48 @@ u256 StructType::getStorageSize() const
return max<u256>(1, size);
}
+bool StructType::canLiveOutsideStorage() const
+{
+ for (unsigned i = 0; i < getMemberCount(); ++i)
+ if (!getMemberByIndex(i).getType()->canLiveOutsideStorage())
+ return false;
+ return true;
+}
+
+string StructType::toString() const
+{
+ return string("struct ") + m_struct.getName();
+}
+
+unsigned StructType::getMemberCount() const
+{
+ return m_struct.getMembers().size();
+}
+
+unsigned StructType::memberNameToIndex(string const& _name) const
+{
+ vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
+ for (unsigned index = 0; index < members.size(); ++index)
+ if (members[index]->getName() == _name)
+ return index;
+ return unsigned(-1);
+}
+
+VariableDeclaration const& StructType::getMemberByIndex(unsigned _index) const
+{
+ return *m_struct.getMembers()[_index];
+}
+
+u256 StructType::getStorageOffsetOfMember(unsigned _index) const
+{
+ //@todo cache member offset?
+ u256 offset;
+ vector<ASTPointer<VariableDeclaration>> const& members = m_struct.getMembers();
+ for (unsigned index = 0; index < _index; ++index)
+ offset += getMemberByIndex(index).getType()->getStorageSize();
+ return offset;
+}
+
bool FunctionType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@@ -239,6 +281,12 @@ bool FunctionType::operator==(Type const& _other) const
return other.m_function == m_function;
}
+string FunctionType::toString() const
+{
+ //@todo nice string for function types
+ return "function(...)returns(...)";
+}
+
bool MappingType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
@@ -247,6 +295,11 @@ bool MappingType::operator==(Type const& _other) const
return *other.m_keyType == *m_keyType && *other.m_valueType == *m_valueType;
}
+string MappingType::toString() const
+{
+ return "mapping(" + getKeyType()->toString() + " => " + getValueType()->toString() + ")";
+}
+
bool TypeType::operator==(Type const& _other) const
{
if (_other.getCategory() != getCategory())
diff --git a/Types.h b/Types.h
index b9bb74db..72647017 100644
--- a/Types.h
+++ b/Types.h
@@ -184,9 +184,14 @@ public:
virtual bool operator==(Type const& _other) const override;
virtual u256 getStorageSize() const;
- //@todo it can, if its members can
- virtual bool canLiveOutsideStorage() const { return false; }
- virtual std::string toString() const override { return "struct{...}"; }
+ virtual bool canLiveOutsideStorage() const;
+ virtual std::string toString() const override;
+
+ unsigned getMemberCount() const;
+ /// Returns the index of the member with name @a _name or unsigned(-1) if it does not exist.
+ unsigned memberNameToIndex(std::string const& _name) const;
+ VariableDeclaration const& getMemberByIndex(unsigned _index) const;
+ u256 getStorageOffsetOfMember(unsigned _index) const;
private:
StructDefinition const& m_struct;
@@ -204,7 +209,7 @@ public:
FunctionDefinition const& getFunction() const { return m_function; }
virtual bool operator==(Type const& _other) const override;
- virtual std::string toString() const override { return "function(...)returns(...)"; }
+ virtual std::string toString() const override;
virtual u256 getStorageSize() const { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable function type requested.")); }
virtual bool canLiveOutsideStorage() const { return false; }
@@ -223,11 +228,12 @@ public:
m_keyType(_keyType), m_valueType(_valueType) {}
virtual bool operator==(Type const& _other) const override;
- virtual std::string toString() const override { return "mapping(...=>...)"; }
+ virtual std::string toString() const override;
virtual bool canLiveOutsideStorage() const { return false; }
std::shared_ptr<Type const> getKeyType() const { return m_keyType; }
std::shared_ptr<Type const> getValueType() const { return m_valueType; }
+
private:
std::shared_ptr<Type const> m_keyType;
std::shared_ptr<Type const> m_valueType;