diff --git a/AST.cpp b/AST.cpp index a529d6dfd..e0ee5c098 100644 --- a/AST.cpp +++ b/AST.cpp @@ -373,7 +373,8 @@ void MemberAccess::checkTypeRequirements() Type const& type = *m_expression->getType(); m_type = type.getMemberType(*m_memberName); if (!m_type) - BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found in " + type.toString())); + BOOST_THROW_EXCEPTION(createTypeError("Member \"" + *m_memberName + "\" not found or not " + "visible in " + type.toString())); //@todo later, this will not always be STORAGE m_lvalue = type.getCategory() == Type::Category::STRUCT ? LValueType::STORAGE : LValueType::NONE; } @@ -423,7 +424,7 @@ void Identifier::checkTypeRequirements() ContractDefinition const* contractDef = dynamic_cast(m_referencedDeclaration); if (contractDef) { - m_type = make_shared(make_shared(*contractDef)); + m_type = make_shared(make_shared(*contractDef), m_currentContract); return; } MagicVariableDeclaration const* magicVariable = dynamic_cast(m_referencedDeclaration); diff --git a/AST.h b/AST.h index e8bc7f6a9..76bdaebc7 100755 --- a/AST.h +++ b/AST.h @@ -595,7 +595,7 @@ public: virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; - void setFunctionReturnParameters(ParameterList& _parameters) { m_returnParameters = &_parameters; } + void setFunctionReturnParameters(ParameterList const& _parameters) { m_returnParameters = &_parameters; } ParameterList const& getFunctionReturnParameters() const { solAssert(m_returnParameters, ""); @@ -607,7 +607,7 @@ private: ASTPointer m_expression; ///< value to return, optional /// Pointer to the parameter list of the function, filled by the @ref NameAndTypeResolver. - ParameterList* m_returnParameters; + ParameterList const* m_returnParameters; }; /** @@ -884,21 +884,30 @@ class Identifier: public PrimaryExpression { public: Identifier(Location const& _location, ASTPointer const& _name): - PrimaryExpression(_location), m_name(_name), m_referencedDeclaration(nullptr) {} + PrimaryExpression(_location), m_name(_name) {} virtual void accept(ASTVisitor& _visitor) override; virtual void accept(ASTConstVisitor& _visitor) const override; virtual void checkTypeRequirements() override; ASTString const& getName() const { return *m_name; } - void setReferencedDeclaration(Declaration const& _referencedDeclaration) { m_referencedDeclaration = &_referencedDeclaration; } + void setReferencedDeclaration(Declaration const& _referencedDeclaration, + ContractDefinition const* _currentContract = nullptr) + { + m_referencedDeclaration = &_referencedDeclaration; + m_currentContract = _currentContract; + } Declaration const* getReferencedDeclaration() const { return m_referencedDeclaration; } + ContractDefinition const* getCurrentContract() const { return m_currentContract; } private: ASTPointer m_name; /// Declaration the name refers to. - Declaration const* m_referencedDeclaration; + 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_currentContract = nullptr; }; /** diff --git a/ExpressionCompiler.cpp b/ExpressionCompiler.cpp index 5a45bfd6d..60c5c4ded 100644 --- a/ExpressionCompiler.cpp +++ b/ExpressionCompiler.cpp @@ -419,6 +419,22 @@ void ExpressionCompiler::endVisit(MemberAccess const& _memberAccess) m_currentLValue.retrieveValueIfLValueNotRequested(_memberAccess); break; } + case Type::Category::TYPE: + { + TypeType const& type = dynamic_cast(*_memberAccess.getExpression().getType()); + if (type.getMembers().getMemberType(member)) + { + ContractDefinition const& contract = dynamic_cast(*type.getActualType()) + .getContractDefinition(); + for (ASTPointer const& function: contract.getDefinedFunctions()) + if (function->getName() == member) + { + m_context << m_context.getFunctionEntryLabel(*function).pushTag(); + return; + } + } + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Invalid member access to " + type.toString())); + } default: BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Member access to unknown type.")); } @@ -449,20 +465,22 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) { if (magicVar->getType()->getCategory() == Type::Category::CONTRACT) // must be "this" m_context << eth::Instruction::ADDRESS; - return; } - if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) - { + else if (FunctionDefinition const* functionDef = dynamic_cast(declaration)) m_context << m_context.getVirtualFunctionEntryLabel(*functionDef).pushTag(); - return; - } - if (dynamic_cast(declaration)) + else if (dynamic_cast(declaration)) { m_currentLValue.fromIdentifier(_identifier, *declaration); m_currentLValue.retrieveValueIfLValueNotRequested(_identifier); - return; } - BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); + else if (dynamic_cast(declaration)) + { + // no-op + } + else + { + BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Identifier type not expected in expression context.")); + } } void ExpressionCompiler::endVisit(Literal const& _literal) diff --git a/NameAndTypeResolver.cpp b/NameAndTypeResolver.cpp index 0b6afdd58..f208dc787 100644 --- a/NameAndTypeResolver.cpp +++ b/NameAndTypeResolver.cpp @@ -49,7 +49,7 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) m_currentScope = &m_scopes[nullptr]; for (ASTPointer const& baseContract: _contract.getBaseContracts()) - ReferencesResolver resolver(*baseContract, *this, nullptr); + ReferencesResolver resolver(*baseContract, *this, &_contract, nullptr); m_currentScope = &m_scopes[&_contract]; @@ -58,13 +58,13 @@ void NameAndTypeResolver::resolveNamesAndTypes(ContractDefinition& _contract) importInheritedScope(*base); for (ASTPointer const& structDef: _contract.getDefinedStructs()) - ReferencesResolver resolver(*structDef, *this, nullptr); + ReferencesResolver resolver(*structDef, *this, &_contract, nullptr); for (ASTPointer const& variable: _contract.getStateVariables()) - ReferencesResolver resolver(*variable, *this, nullptr); + ReferencesResolver resolver(*variable, *this, &_contract, nullptr); for (ASTPointer const& function: _contract.getDefinedFunctions()) { m_currentScope = &m_scopes[function.get()]; - ReferencesResolver referencesResolver(*function, *this, + ReferencesResolver referencesResolver(*function, *this, &_contract, function->getReturnParameterList().get()); } } @@ -267,8 +267,10 @@ void DeclarationRegistrationHelper::registerDeclaration(Declaration& _declaratio } ReferencesResolver::ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ParameterList* _returnParameters, bool _allowLazyTypes): - m_resolver(_resolver), m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, bool _allowLazyTypes): + m_resolver(_resolver), m_currentContract(_currentContract), + m_returnParameters(_returnParameters), m_allowLazyTypes(_allowLazyTypes) { _root.accept(*this); } @@ -316,7 +318,7 @@ bool ReferencesResolver::visit(Identifier& _identifier) if (!declaration) BOOST_THROW_EXCEPTION(DeclarationError() << errinfo_sourceLocation(_identifier.getLocation()) << errinfo_comment("Undeclared identifier.")); - _identifier.setReferencedDeclaration(*declaration); + _identifier.setReferencedDeclaration(*declaration, m_currentContract); return false; } diff --git a/NameAndTypeResolver.h b/NameAndTypeResolver.h index 4fb67da34..f97c7ae56 100644 --- a/NameAndTypeResolver.h +++ b/NameAndTypeResolver.h @@ -120,7 +120,9 @@ class ReferencesResolver: private ASTVisitor { public: ReferencesResolver(ASTNode& _root, NameAndTypeResolver& _resolver, - ParameterList* _returnParameters, bool _allowLazyTypes = true); + ContractDefinition const* _currentContract, + ParameterList const* _returnParameters, + bool _allowLazyTypes = true); private: virtual void endVisit(VariableDeclaration& _variable) override; @@ -130,7 +132,8 @@ private: virtual bool visit(Return& _return) override; NameAndTypeResolver& m_resolver; - ParameterList* m_returnParameters; + ContractDefinition const* m_currentContract; + ParameterList const* m_returnParameters; bool m_allowLazyTypes; }; diff --git a/Types.cpp b/Types.cpp index a99e4853f..94fd57501 100644 --- a/Types.cpp +++ b/Types.cpp @@ -695,6 +695,29 @@ bool TypeType::operator==(Type const& _other) const return *getActualType() == *other.getActualType(); } +MemberList const& TypeType::getMembers() const +{ + // We need to lazy-initialize it because of recursive references. + if (!m_members) + { + map members; + if (m_actualType->getCategory() == Category::CONTRACT && m_currentContract != nullptr) + { + ContractDefinition const& contract = dynamic_cast(*m_actualType).getContractDefinition(); + vector currentBases = m_currentContract->getLinearizedBaseContracts(); + if (find(currentBases.begin(), currentBases.end(), &contract) != currentBases.end()) + // We are accessing the type of a base contract, so add all public and private + // functions. Note that this does not add inherited functions on purpose. + for (ASTPointer const& f: contract.getDefinedFunctions()) + if (f->getName() != contract.getName()) + members[f->getName()] = make_shared(*f); + } + m_members.reset(new MemberList(members)); + } + return *m_members; +} + + MagicType::MagicType(MagicType::Kind _kind): m_kind(_kind) { diff --git a/Types.h b/Types.h index cb8a8db0d..e6c99fe3b 100644 --- a/Types.h +++ b/Types.h @@ -442,7 +442,8 @@ class TypeType: public Type { public: virtual Category getCategory() const override { return Category::TYPE; } - TypeType(TypePointer const& _actualType): m_actualType(_actualType) {} + TypeType(TypePointer const& _actualType, ContractDefinition const* _currentContract = nullptr): + m_actualType(_actualType), m_currentContract(_currentContract) {} TypePointer const& getActualType() const { return m_actualType; } virtual TypePointer binaryOperatorResult(Token::Value, TypePointer const&) const override { return TypePointer(); } @@ -451,9 +452,14 @@ public: virtual u256 getStorageSize() const override { BOOST_THROW_EXCEPTION(InternalCompilerError() << errinfo_comment("Storage size of non-storable type type requested.")); } virtual bool canLiveOutsideStorage() const override { return false; } virtual std::string toString() const override { return "type(" + m_actualType->toString() + ")"; } + virtual MemberList const& getMembers() const override; private: TypePointer m_actualType; + /// Context in which this type is used (influences visibility etc.), can be nullptr. + ContractDefinition const* m_currentContract; + /// List of member types, will be lazy-initialized because of recursive references. + mutable std::unique_ptr m_members; };