diff --git a/libsolidity/analysis/GlobalContext.cpp b/libsolidity/analysis/GlobalContext.cpp index 2bf893675..29de12009 100644 --- a/libsolidity/analysis/GlobalContext.cpp +++ b/libsolidity/analysis/GlobalContext.cpp @@ -140,7 +140,7 @@ MagicVariableDeclaration const* GlobalContext::currentSuper() const { Type const* type = TypeProvider::emptyTuple(); if (m_currentContract) - type = TypeProvider::contract(*m_currentContract, true); + type = TypeProvider::typeType(TypeProvider::contract(*m_currentContract, true)); m_superPointer[m_currentContract] = make_shared(magicVariableToID("super"), "super", type); } diff --git a/libsolidity/analysis/TypeChecker.cpp b/libsolidity/analysis/TypeChecker.cpp index f02673405..5d9e23242 100644 --- a/libsolidity/analysis/TypeChecker.cpp +++ b/libsolidity/analysis/TypeChecker.cpp @@ -2760,9 +2760,12 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) ); if (!funType->bound()) - if (auto contractType = dynamic_cast(exprType)) - if (contractType->isSuper()) + if (auto typeType = dynamic_cast(exprType)) + { + auto contractType = dynamic_cast(typeType->actualType()); + if (contractType && contractType->isSuper()) requiredLookup = VirtualLookup::Super; + } } annotation.requiredLookup = requiredLookup; @@ -2837,8 +2840,13 @@ bool TypeChecker::visit(MemberAccess const& _memberAccess) _memberAccess.location(), "\"runtimeCode\" is not available for contracts containing immutable variables." ); - - if (m_currentContract) + if (accessedContractType.isSuper()) + m_errorReporter.typeError( + 3625_error, + _memberAccess.location(), + "\"creationCode\" and \"runtimeCode\" are not available for the \"super\" contract." + ); + else if (m_currentContract) { // TODO in the same way as with ``new``, // this is not properly detecting creation-cycles if they go through diff --git a/libsolidity/analysis/ViewPureChecker.cpp b/libsolidity/analysis/ViewPureChecker.cpp index 385f01b55..375973f21 100644 --- a/libsolidity/analysis/ViewPureChecker.cpp +++ b/libsolidity/analysis/ViewPureChecker.cpp @@ -194,8 +194,8 @@ void ViewPureChecker::endVisit(Identifier const& _identifier) switch (magicVar->type()->category()) { case Type::Category::Contract: - solAssert(_identifier.name() == "this" || _identifier.name() == "super", ""); - if (!dynamic_cast(*magicVar->type()).isSuper()) + solAssert(_identifier.name() == "this", ""); + if (dynamic_cast(magicVar->type())) // reads the address mutability = StateMutability::View; break; @@ -440,4 +440,3 @@ void ViewPureChecker::endVisit(ModifierInvocation const& _modifier) else solAssert(dynamic_cast(_modifier.name().annotation().referencedDeclaration), ""); } - diff --git a/libsolidity/ast/Types.cpp b/libsolidity/ast/Types.cpp index d74efa8c4..b21d867ca 100644 --- a/libsolidity/ast/Types.cpp +++ b/libsolidity/ast/Types.cpp @@ -2158,36 +2158,8 @@ string ContractType::canonicalName() const MemberList::MemberMap ContractType::nativeMembers(ASTNode const*) const { MemberList::MemberMap members; - if (m_super) - { - // add the most derived of all functions which are visible in derived contracts - auto bases = m_contract.annotation().linearizedBaseContracts; - solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); - // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. - for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) - for (FunctionDefinition const* function: base->definedFunctions()) - { - if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) - continue; - - auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal); - bool functionWithEqualArgumentsFound = false; - for (auto const& member: members) - { - if (member.name != function->name()) - continue; - auto memberType = dynamic_cast(member.type); - solAssert(!!memberType, "Override changes type."); - if (!memberType->hasEqualParameterTypes(*functionType)) - continue; - functionWithEqualArgumentsFound = true; - break; - } - if (!functionWithEqualArgumentsFound) - members.emplace_back(function->name(), functionType, function); - } - } - else if (!m_contract.isLibrary()) + solAssert(!m_super, ""); + if (!m_contract.isLibrary()) for (auto const& it: m_contract.interfaceFunctions()) members.emplace_back( it.second->declaration().name(), @@ -3838,7 +3810,11 @@ vector> TypeType::makeStackItems() const { if (auto contractType = dynamic_cast(m_actualType)) if (contractType->contractDefinition().isLibrary()) + { + solAssert(!contractType->isSuper(), ""); return {make_tuple("address", TypeProvider::address())}; + } + return {}; } @@ -3847,32 +3823,65 @@ MemberList::MemberMap TypeType::nativeMembers(ASTNode const* _currentScope) cons MemberList::MemberMap members; if (m_actualType->category() == Category::Contract) { - auto const* contractScope = dynamic_cast(_currentScope); - ContractDefinition const& contract = dynamic_cast(*m_actualType).contractDefinition(); - bool inDerivingScope = contractScope && contractScope->derivesFrom(contract); - - for (auto const* declaration: contract.declarations()) + auto contractType = dynamic_cast(m_actualType); + ContractDefinition const& contract = contractType->contractDefinition(); + if (contractType->isSuper()) { - if (dynamic_cast(declaration)) - continue; - if (declaration->name().empty()) - continue; + // add the most derived of all functions which are visible in derived contracts + auto bases = contract.annotation().linearizedBaseContracts; + solAssert(bases.size() >= 1, "linearizedBaseContracts should at least contain the most derived contract."); + // `sliced(1, ...)` ignores the most derived contract, which should not be searchable from `super`. + for (ContractDefinition const* base: bases | boost::adaptors::sliced(1, bases.size())) + for (FunctionDefinition const* function: base->definedFunctions()) + { + if (!function->isVisibleInDerivedContracts() || !function->isImplemented()) + continue; - if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) + auto functionType = TypeProvider::function(*function, FunctionType::Kind::Internal); + bool functionWithEqualArgumentsFound = false; + for (auto const& member: members) + { + if (member.name != function->name()) + continue; + auto memberType = dynamic_cast(member.type); + solAssert(!!memberType, "Override changes type."); + if (!memberType->hasEqualParameterTypes(*functionType)) + continue; + functionWithEqualArgumentsFound = true; + break; + } + if (!functionWithEqualArgumentsFound) + members.emplace_back(function->name(), functionType, function); + } + } + else + { + auto const* contractScope = dynamic_cast(_currentScope); + bool inDerivingScope = contractScope && contractScope->derivesFrom(contract); + + for (auto const* declaration: contract.declarations()) { - if ( - auto const* functionDefinition = dynamic_cast(declaration); - functionDefinition && !functionDefinition->isImplemented() + if (dynamic_cast(declaration)) + continue; + if (declaration->name().empty()) + continue; + + if (!contract.isLibrary() && inDerivingScope && declaration->isVisibleInDerivedContracts()) + { + if ( + auto const* functionDefinition = dynamic_cast(declaration); + functionDefinition && !functionDefinition->isImplemented() + ) + members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); + else + members.emplace_back(declaration->name(), declaration->type(), declaration); + } + else if ( + (contract.isLibrary() && declaration->isVisibleAsLibraryMember()) || + declaration->isVisibleViaContractTypeAccess() ) members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); - else - members.emplace_back(declaration->name(), declaration->type(), declaration); } - else if ( - (contract.isLibrary() && declaration->isVisibleAsLibraryMember()) || - declaration->isVisibleViaContractTypeAccess() - ) - members.emplace_back(declaration->name(), declaration->typeViaContractName(), declaration); } } else if (m_actualType->category() == Category::Enum) diff --git a/libsolidity/codegen/ExpressionCompiler.cpp b/libsolidity/codegen/ExpressionCompiler.cpp index 0d36fa7de..6f7e1a66a 100644 --- a/libsolidity/codegen/ExpressionCompiler.cpp +++ b/libsolidity/codegen/ExpressionCompiler.cpp @@ -1355,59 +1355,72 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) // for internal functions, or enum/struct definitions. if (TypeType const* type = dynamic_cast(_memberAccess.expression().annotation().type)) { - if (dynamic_cast(type->actualType())) + if (auto contractType = dynamic_cast(type->actualType())) { solAssert(_memberAccess.annotation().type, "_memberAccess has no type"); - if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - appendVariable(*variable, static_cast(_memberAccess)); - else if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + if (contractType->isSuper()) { - switch (funType->kind()) - { - case FunctionType::Kind::Declaration: - break; - case FunctionType::Kind::Internal: - // We do not visit the expression here on purpose, because in the case of an - // internal library function call, this would push the library address forcing - // us to link against it although we actually do not need it. - if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - { - solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); - utils().pushCombinedFunctionEntryLabel(*function); - } - else - solAssert(false, "Function not found in member access"); - break; - case FunctionType::Kind::Event: - if (!dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - solAssert(false, "event not found"); - // no-op, because the parent node will do the job - break; - case FunctionType::Kind::DelegateCall: - _memberAccess.expression().accept(*this); - m_context << funType->externalIdentifier(); - break; - case FunctionType::Kind::External: - case FunctionType::Kind::Creation: - case FunctionType::Kind::Send: - case FunctionType::Kind::BareCall: - case FunctionType::Kind::BareCallCode: - case FunctionType::Kind::BareDelegateCall: - case FunctionType::Kind::BareStaticCall: - case FunctionType::Kind::Transfer: - case FunctionType::Kind::ECRecover: - case FunctionType::Kind::SHA256: - case FunctionType::Kind::RIPEMD160: - default: - solAssert(false, "unsupported member function"); - } - } - else if (dynamic_cast(_memberAccess.annotation().type)) - { - // no-op + _memberAccess.expression().accept(*this); + solAssert(_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Super, ""); + utils().pushCombinedFunctionEntryLabel(m_context.superFunction( + dynamic_cast(*_memberAccess.annotation().referencedDeclaration), + contractType->contractDefinition() + )); } else - _memberAccess.expression().accept(*this); + { + if (auto variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + appendVariable(*variable, static_cast(_memberAccess)); + else if (auto funType = dynamic_cast(_memberAccess.annotation().type)) + { + switch (funType->kind()) + { + case FunctionType::Kind::Declaration: + break; + case FunctionType::Kind::Internal: + // We do not visit the expression here on purpose, because in the case of an + // internal library function call, this would push the library address forcing + // us to link against it although we actually do not need it. + if (auto const* function = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + { + solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Static, ""); + utils().pushCombinedFunctionEntryLabel(*function); + } + else + solAssert(false, "Function not found in member access"); + break; + case FunctionType::Kind::Event: + if (!dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + solAssert(false, "event not found"); + // no-op, because the parent node will do the job + break; + case FunctionType::Kind::DelegateCall: + _memberAccess.expression().accept(*this); + m_context << funType->externalIdentifier(); + break; + case FunctionType::Kind::External: + case FunctionType::Kind::Creation: + case FunctionType::Kind::Send: + case FunctionType::Kind::BareCall: + case FunctionType::Kind::BareCallCode: + case FunctionType::Kind::BareDelegateCall: + case FunctionType::Kind::BareStaticCall: + case FunctionType::Kind::Transfer: + case FunctionType::Kind::ECRecover: + case FunctionType::Kind::SHA256: + case FunctionType::Kind::RIPEMD160: + default: + solAssert(false, "unsupported member function"); + } + } + else if (dynamic_cast(_memberAccess.annotation().type)) + { + // no-op + } + else + _memberAccess.expression().accept(*this); + } } else if (auto enumType = dynamic_cast(type->actualType())) { @@ -1480,17 +1493,8 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) case Type::Category::Contract: { ContractType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); - if (type.isSuper()) - { - solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); - solAssert(*_memberAccess.annotation().requiredLookup == VirtualLookup::Super, ""); - utils().pushCombinedFunctionEntryLabel(m_context.superFunction( - dynamic_cast(*_memberAccess.annotation().referencedDeclaration), - type.contractDefinition() - )); - } // ordinary contract type - else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) + if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) { u256 identifier; if (auto const* variable = dynamic_cast(declaration)) @@ -1592,8 +1596,11 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) solAssert(false, "Blockhash has been removed."); else if (member == "creationCode" || member == "runtimeCode") { + // FIXME For super this needs to be fixed TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); utils().fetchFreeMemoryPointer(); m_context << Instruction::DUP1 << u256(32) << Instruction::ADD; utils().copyContractCodeToMemory(contract, member == "creationCode"); @@ -1610,7 +1617,10 @@ bool ExpressionCompiler::visit(MemberAccess const& _memberAccess) else if (member == "name") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + ContractDefinition const& contract = contractType.isSuper() ? + *contractType.contractDefinition().superContract(m_context.mostDerivedContract()) : + dynamic_cast(*arg).contractDefinition(); utils().allocateMemory(((contract.name().length() + 31) / 32) * 32 + 32); // store string length m_context << u256(contract.name().length()) << Instruction::DUP2 << Instruction::MSTORE; @@ -1960,9 +1970,11 @@ void ExpressionCompiler::endVisit(Identifier const& _identifier) switch (magicVar->type()->category()) { case Type::Category::Contract: - // "this" or "super" - if (!dynamic_cast(*magicVar->type()).isSuper()) + if (dynamic_cast(magicVar->type())) + { + solAssert(_identifier.name() == "this", ""); m_context << Instruction::ADDRESS; + } break; default: break; diff --git a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp index e44723549..5b7c36ca2 100644 --- a/libsolidity/codegen/ir/IRGeneratorForStatements.cpp +++ b/libsolidity/codegen/ir/IRGeneratorForStatements.cpp @@ -884,17 +884,17 @@ void IRGeneratorForStatements::endVisit(FunctionCall const& _functionCall) solAssert(*identifier->annotation().requiredLookup == VirtualLookup::Virtual, ""); functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract()); } - else - { - ContractType const* type = dynamic_cast(memberAccess->expression().annotation().type); - if (type && type->isSuper()) + else if (auto typeType = dynamic_cast(memberAccess->expression().annotation().type)) + if ( + auto contractType = dynamic_cast(typeType->actualType()); + contractType->isSuper() + ) { - ContractDefinition const* super = type->contractDefinition().superContract(m_context.mostDerivedContract()); + ContractDefinition const* super = contractType->contractDefinition().superContract(m_context.mostDerivedContract()); solAssert(super, "Super contract not available."); solAssert(*memberAccess->annotation().requiredLookup == VirtualLookup::Super, ""); functionDef = &functionDef->resolveVirtual(m_context.mostDerivedContract(), super); } - } solAssert(functionDef && functionDef->isImplemented(), ""); } @@ -1527,19 +1527,8 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) { ContractType const& type = dynamic_cast(*_memberAccess.expression().annotation().type); if (type.isSuper()) - { - solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); - ContractDefinition const* super = type.contractDefinition().superContract(m_context.mostDerivedContract()); - solAssert(super, "Super contract not available."); - FunctionDefinition const& resolvedFunctionDef = dynamic_cast( - *_memberAccess.annotation().referencedDeclaration - ).resolveVirtual(m_context.mostDerivedContract(), super); + solAssert(false, ""); - define(_memberAccess) << to_string(resolvedFunctionDef.id()) << "\n"; - solAssert(resolvedFunctionDef.functionType(true), ""); - solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); - m_context.internalFunctionAccessed(_memberAccess, resolvedFunctionDef); - } // ordinary contract type else if (Declaration const* declaration = _memberAccess.annotation().referencedDeclaration) { @@ -1649,7 +1638,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "creationCode" || member == "runtimeCode") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); m_context.subObjectsCreated().insert(&contract); m_code << Whiskers(R"( let := datasize("") @@ -1669,7 +1660,9 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) else if (member == "interfaceId") { TypePointer arg = dynamic_cast(*_memberAccess.expression().annotation().type).typeArgument(); - ContractDefinition const& contract = dynamic_cast(*arg).contractDefinition(); + auto const& contractType = dynamic_cast(*arg); + solAssert(!contractType.isSuper(), ""); + ContractDefinition const& contract = contractType.contractDefinition(); define(_memberAccess) << formatNumber(u256{contract.interfaceId()} << (256 - 32)) << "\n"; } else if (member == "min" || member == "max") @@ -1790,8 +1783,24 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) if (actualType.category() == Type::Category::Contract) { - if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) - handleVariableReference(*variable, _memberAccess); + ContractType const& contractType = dynamic_cast(actualType); + if (contractType.isSuper()) + { + solAssert(!!_memberAccess.annotation().referencedDeclaration, "Referenced declaration not resolved."); + ContractDefinition const* super = contractType.contractDefinition().superContract(m_context.mostDerivedContract()); + solAssert(super, "Super contract not available."); + FunctionDefinition const& resolvedFunctionDef = + dynamic_cast( + *_memberAccess.annotation().referencedDeclaration + ).resolveVirtual(m_context.mostDerivedContract(), super); + + define(_memberAccess) << to_string(resolvedFunctionDef.id()) << "\n"; + solAssert(resolvedFunctionDef.functionType(true), ""); + solAssert(resolvedFunctionDef.functionType(true)->kind() == FunctionType::Kind::Internal, ""); + m_context.internalFunctionAccessed(_memberAccess, resolvedFunctionDef); + } + else if (auto const* variable = dynamic_cast(_memberAccess.annotation().referencedDeclaration)) + handleVariableReference(*variable, _memberAccess); else if (memberFunctionType) { switch (memberFunctionType->kind()) @@ -1812,7 +1821,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) dynamic_cast(_memberAccess.annotation().referencedDeclaration), "Event not found" ); - // the call will do the resolving + // the call will do the resolving break; case FunctionType::Kind::DelegateCall: define(IRVariable(_memberAccess).part("address"), _memberAccess.expression()); @@ -1835,7 +1844,7 @@ void IRGeneratorForStatements::endVisit(MemberAccess const& _memberAccess) } else if (dynamic_cast(_memberAccess.annotation().type)) { - // no-op + // no-op } else // The old code generator had a generic "else" case here @@ -2095,18 +2104,20 @@ void IRGeneratorForStatements::endVisit(Identifier const& _identifier) switch (magicVar->type()->category()) { case Type::Category::Contract: - if (dynamic_cast(*magicVar->type()).isSuper()) - solAssert(_identifier.name() == "super", ""); - else - { - solAssert(_identifier.name() == "this", ""); - define(_identifier) << "address()\n"; - } + solAssert(_identifier.name() == "this", ""); + define(_identifier) << "address()\n"; break; case Type::Category::Integer: solAssert(_identifier.name() == "now", ""); define(_identifier) << "timestamp()\n"; break; + case Type::Category::TypeType: + { + auto typeType = dynamic_cast(magicVar->type()); + if (auto contractType = dynamic_cast(typeType->actualType())) + solAssert(!contractType->isSuper() || _identifier.name() == "super", ""); + break; + } default: break; }