From 4fe47c5014f2a20fc6589d464ae36d82076fc959 Mon Sep 17 00:00:00 2001 From: Christian Date: Mon, 19 Jan 2015 19:18:34 +0100 Subject: [PATCH] Explicit calls to base class function. --- libsolidity/AST.cpp | 5 +++-- libsolidity/AST.h | 19 +++++++++++----- libsolidity/ExpressionCompiler.cpp | 34 ++++++++++++++++++++++------- libsolidity/NameAndTypeResolver.cpp | 16 ++++++++------ libsolidity/NameAndTypeResolver.h | 7 ++++-- libsolidity/Types.cpp | 23 +++++++++++++++++++ libsolidity/Types.h | 8 ++++++- test/SolidityEndToEndTest.cpp | 15 +++++++++++++ 8 files changed, 102 insertions(+), 25 deletions(-) diff --git a/libsolidity/AST.cpp b/libsolidity/AST.cpp index a529d6dfd..e0ee5c098 100644 --- a/libsolidity/AST.cpp +++ b/libsolidity/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/libsolidity/AST.h b/libsolidity/AST.h index e8bc7f6a9..76bdaebc7 100755 --- a/libsolidity/AST.h +++ b/libsolidity/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/libsolidity/ExpressionCompiler.cpp b/libsolidity/ExpressionCompiler.cpp index 5a45bfd6d..60c5c4ded 100644 --- a/libsolidity/ExpressionCompiler.cpp +++ b/libsolidity/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/libsolidity/NameAndTypeResolver.cpp b/libsolidity/NameAndTypeResolver.cpp index 0b6afdd58..f208dc787 100644 --- a/libsolidity/NameAndTypeResolver.cpp +++ b/libsolidity/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/libsolidity/NameAndTypeResolver.h b/libsolidity/NameAndTypeResolver.h index 4fb67da34..f97c7ae56 100644 --- a/libsolidity/NameAndTypeResolver.h +++ b/libsolidity/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/libsolidity/Types.cpp b/libsolidity/Types.cpp index a99e4853f..94fd57501 100644 --- a/libsolidity/Types.cpp +++ b/libsolidity/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/libsolidity/Types.h b/libsolidity/Types.h index cb8a8db0d..e6c99fe3b 100644 --- a/libsolidity/Types.h +++ b/libsolidity/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; }; diff --git a/test/SolidityEndToEndTest.cpp b/test/SolidityEndToEndTest.cpp index 934b39ad6..0f61d2851 100644 --- a/test/SolidityEndToEndTest.cpp +++ b/test/SolidityEndToEndTest.cpp @@ -1553,6 +1553,21 @@ BOOST_AUTO_TEST_CASE(single_copy_with_multiple_inheritance) BOOST_CHECK(callContractFunction("getViaB()") == encodeArgs(23)); } +BOOST_AUTO_TEST_CASE(explicit_base_cass) +{ + char const* sourceCode = R"( + contract BaseBase { function g() returns (uint r) { return 1; } } + contract Base is BaseBase { function g() returns (uint r) { return 2; } } + contract Derived is Base { + function f() returns (uint r) { return BaseBase.g(); } + function g() returns (uint r) { return 3; } + } + )"; + compileAndRun(sourceCode, 0, "Derived"); + BOOST_CHECK(callContractFunction("g()") == encodeArgs(3)); + BOOST_CHECK(callContractFunction("f()") == encodeArgs(1)); +} + BOOST_AUTO_TEST_SUITE_END() }