diff --git a/clang/include/clang/AST/DeclBase.h b/clang/include/clang/AST/DeclBase.h --- a/clang/include/clang/AST/DeclBase.h +++ b/clang/include/clang/AST/DeclBase.h @@ -232,9 +232,12 @@ /// This declaration has an owning module, but is only visible to /// lookups that occur within that module. - /// The discarded declarations in global module fragment belongs - /// to this group too. - ModulePrivate + ModulePrivate, + + /// This declaration is part of a Global Module Fragment, but is not + /// reachable or visible to importers of the named module of which the + /// GMF is part. + ModuleUnreachable }; protected: @@ -594,6 +597,12 @@ void setReferenced(bool R = true) { Referenced = R; } + /// Whether this declaration should be retained if it is used. + bool isModuleUnreachable() const { + return getModuleOwnershipKind() + == Decl::ModuleOwnershipKind::ModuleUnreachable; + } + /// Whether this declaration is a top-level declaration (function, /// global variable, etc.) that is lexically inside an objc container /// definition. @@ -633,9 +642,10 @@ return getModuleOwnershipKind() > ModuleOwnershipKind::VisibleWhenImported; } - /// FIXME: Implement discarding declarations actually in global module - /// fragment. See [module.global.frag]p3,4 for details. - bool isDiscardedInGlobalModuleFragment() const { return false; } + /// See [module.global.frag]p3,4 for details. + bool isDiscardedInGlobalModuleFragment() const { + return getModuleOwnershipKind() == ModuleOwnershipKind::ModuleUnreachable; + } /// Return true if this declaration has an attribute which acts as /// definition of the entity, such as 'alias' or 'ifunc'. diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -2265,7 +2265,37 @@ bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind); + /// Given a Decl D (which is from the GMF) which has been made reachable by + /// use, mark any additional decls which this makes reachable. + void markReachableGMFDecls(Decl *D); + + void HandleGMFReachability(Decl *D) { + if (D->isModuleUnreachable() && isCurrentModulePurview()) { + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); + markReachableGMFDecls(D); + } + } + public: + /// To implement C++20 GMF elision semantics, we initially mark decls in the + /// Global Module Fragment as "ModuleUnreachable". If the decl is then used + /// within the module purview, we reset that to visible. + void setDeclIsUsed(Decl *D) { + D->setIsUsed(); + HandleGMFReachability(D); + } + + /// Likewise when the decl is marked used and notifies mutation listeners. + void markDeclUsed(Decl *D, ASTContext &C) { + D->markUsed(C); + HandleGMFReachability(D); + } + + void setDeclReferenced(Decl *D, bool R = true) { + D->setReferenced(R); + HandleGMFReachability(D); + } + /// Get the module unit whose scope we are currently within. Module *getCurrentModule() const { return ModuleScopes.empty() ? nullptr : ModuleScopes.back().Module; diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -590,6 +590,7 @@ case Decl::ModuleOwnershipKind::Unowned: case Decl::ModuleOwnershipKind::ReachableWhenImported: case Decl::ModuleOwnershipKind::ModulePrivate: + case Decl::ModuleOwnershipKind::ModuleUnreachable: return false; case Decl::ModuleOwnershipKind::Visible: case Decl::ModuleOwnershipKind::VisibleWhenImported: diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -12,6 +12,7 @@ #include "clang/AST/TextNodeDumper.h" #include "clang/AST/APValue.h" +#include "clang/AST/DeclBase.h" #include "clang/AST/DeclFriend.h" #include "clang/AST/DeclOpenMP.h" #include "clang/AST/DeclTemplate.h" @@ -1617,6 +1618,8 @@ dumpType(D->getUnderlyingType()); if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; } void TextNodeDumper::VisitEnumDecl(const EnumDecl *D) { @@ -1629,6 +1632,8 @@ dumpName(D); if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; if (D->isFixed()) dumpType(D->getIntegerType()); } @@ -1638,6 +1643,8 @@ dumpName(D); if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; if (D->isCompleteDefinition()) OS << " definition"; } @@ -1668,6 +1675,8 @@ OS << " virtual"; if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; if (D->isPure()) OS << " pure"; @@ -1743,6 +1752,8 @@ OS << " mutable"; if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; } void TextNodeDumper::VisitVarDecl(const VarDecl *D) { @@ -1763,6 +1774,8 @@ } if (D->isModulePrivate()) OS << " __module_private__"; + if (D->isModuleUnreachable()) + OS << " ModuleUnreachable"; if (D->isNRVOVariable()) OS << " nrvo"; if (D->isInline()) diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp --- a/clang/lib/Sema/SemaDecl.cpp +++ b/clang/lib/Sema/SemaDecl.cpp @@ -4121,7 +4121,7 @@ // Merge "used" flag. if (Old->getMostRecentDecl()->isUsed(false)) - New->setIsUsed(); + setDeclIsUsed(New); // Merge attributes from the parameters. These can mismatch with K&R // declarations. @@ -4537,7 +4537,7 @@ // Merge "used" flag. if (Old->getMostRecentDecl()->isUsed(false)) - New->setIsUsed(); + setDeclIsUsed(New); // Keep a chain of previous declarations. New->setPreviousDecl(Old); diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -2012,7 +2012,7 @@ LookupResult LR(S, target, Sema::LookupOrdinaryName); if (S.LookupQualifiedName(LR, S.getCurLexicalContext())) for (NamedDecl *ND : LR) - ND->markUsed(S.Context); + S.markDeclUsed(ND, S.Context); } D->addAttr(::new (S.Context) AliasAttr(S.Context, AL, Str)); diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp --- a/clang/lib/Sema/SemaDeclCXX.cpp +++ b/clang/lib/Sema/SemaDeclCXX.cpp @@ -8797,7 +8797,7 @@ return; } FD->setBody(Body.get()); - FD->markUsed(Context); + markDeclUsed(FD,Context); } // The exception specification is needed because we are defining the @@ -13491,7 +13491,7 @@ ? Constructor->getEndLoc() : Constructor->getLocation(); Constructor->setBody(new (Context) CompoundStmt(Loc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor,Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13670,7 +13670,7 @@ } Constructor->setBody(new (Context) CompoundStmt(InitLoc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13781,7 +13781,7 @@ ? Destructor->getEndLoc() : Destructor->getLocation(); Destructor->setBody(new (Context) CompoundStmt(Loc)); - Destructor->markUsed(Context); + markDeclUsed(Destructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Destructor); @@ -14640,7 +14640,7 @@ assert(!Body.isInvalid() && "Compound statement creation cannot fail"); } CopyAssignOperator->setBody(Body.getAs()); - CopyAssignOperator->markUsed(Context); + markDeclUsed(CopyAssignOperator, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(CopyAssignOperator); @@ -15010,7 +15010,7 @@ assert(!Body.isInvalid() && "Compound statement creation cannot fail"); } MoveAssignOperator->setBody(Body.getAs()); - MoveAssignOperator->markUsed(Context); + markDeclUsed(MoveAssignOperator, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(MoveAssignOperator); @@ -15154,7 +15154,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); CopyConstructor->setBody( ActOnCompoundStmt(Loc, Loc, None, /*isStmtExpr=*/false).getAs()); - CopyConstructor->markUsed(Context); + markDeclUsed(CopyConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15280,7 +15280,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); MoveConstructor->setBody(ActOnCompoundStmt( Loc, Loc, None, /*isStmtExpr=*/ false).getAs()); - MoveConstructor->markUsed(Context); + markDeclUsed(MoveConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15331,7 +15331,7 @@ // Fill in the __invoke function with a dummy implementation. IR generation // will fill in the actual details. Update its type in case it contained // an 'auto'. - Invoker->markUsed(Context); + markDeclUsed(Invoker, Context); Invoker->setReferenced(); Invoker->setType(Conv->getReturnType()->getPointeeType()); Invoker->setBody(new (Context) CompoundStmt(Conv->getLocation())); @@ -15343,7 +15343,7 @@ Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get(); Conv->setBody(CompoundStmt::Create(Context, Return, Conv->getLocation(), Conv->getLocation())); - Conv->markUsed(Context); + markDeclUsed(Conv, Context); Conv->setReferenced(); if (ASTMutationListener *L = getASTMutationListener()) { @@ -15398,7 +15398,7 @@ Stmt *ReturnS = Return.get(); Conv->setBody(CompoundStmt::Create(Context, ReturnS, Conv->getLocation(), Conv->getLocation())); - Conv->markUsed(Context); + markDeclUsed(Conv, Context); // We're done; notify the mutation listener, if any. if (ASTMutationListener *L = getASTMutationListener()) { diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -47,6 +47,7 @@ #include "clang/Sema/ParsedTemplate.h" #include "clang/Sema/Scope.h" #include "clang/Sema/ScopeInfo.h" +#include "clang/Sema/Sema.h" #include "clang/Sema/SemaFixItUtils.h" #include "clang/Sema/SemaInternal.h" #include "clang/Sema/Template.h" @@ -15719,7 +15720,7 @@ /// ActOnAddrLabel - Parse the GNU address of label extension: "&&foo". ExprResult Sema::ActOnAddrLabel(SourceLocation OpLoc, SourceLocation LabLoc, LabelDecl *TheDecl) { - TheDecl->markUsed(Context); + markDeclUsed(TheDecl, Context); // Create the AST node. The address of a label always has type 'void*'. return new (Context) AddrLabelExpr(OpLoc, LabLoc, TheDecl, Context.getPointerType(Context.VoidTy)); @@ -17804,7 +17805,7 @@ bool MightBeOdrUse) { assert(Func && "No function?"); - Func->setReferenced(); + setDeclReferenced(Func); // Recursive functions aren't really used until they're used from some other // context. @@ -18037,7 +18038,7 @@ } } - Func->markUsed(Context); + markDeclUsed(Func, Context); } } @@ -18108,7 +18109,7 @@ } } - Var->markUsed(SemaRef.Context); + SemaRef.markDeclUsed(Var, SemaRef.Context); } void Sema::MarkCaptureUsedInEnclosingContext(VarDecl *Capture, @@ -19264,7 +19265,7 @@ assert((!E || isa(E) || isa(E) || isa(E)) && "Invalid Expr argument to DoMarkVarDeclReferenced"); - Var->setReferenced(); + SemaRef.setDeclReferenced(Var); if (Var->isInvalidDecl()) return; @@ -19540,7 +19541,7 @@ MarkFunctionReferenced(Loc, FD, MightBeOdrUse); return; } - D->setReferenced(); + setDeclReferenced(D); } namespace { diff --git a/clang/lib/Sema/SemaInit.cpp b/clang/lib/Sema/SemaInit.cpp --- a/clang/lib/Sema/SemaInit.cpp +++ b/clang/lib/Sema/SemaInit.cpp @@ -4692,7 +4692,7 @@ FunctionDecl *Function = Best->Function; // This is the overload that will be used for this initialization step if we // use this initialization. Mark it as referenced. - Function->setReferenced(); + S.setDeclReferenced(Function); // Compute the returned type and value kind of the conversion. QualType cv3T3; @@ -5363,7 +5363,7 @@ } FunctionDecl *Function = Best->Function; - Function->setReferenced(); + S.setDeclReferenced(Function); bool HadMultipleCandidates = (CandidateSet.size() > 1); if (isa(Function)) { diff --git a/clang/lib/Sema/SemaLambda.cpp b/clang/lib/Sema/SemaLambda.cpp --- a/clang/lib/Sema/SemaLambda.cpp +++ b/clang/lib/Sema/SemaLambda.cpp @@ -874,7 +874,7 @@ NewVD->setReferenced(true); // FIXME: Pass in a VarDecl::InitializationStyle. NewVD->setInitStyle(static_cast(InitStyle)); - NewVD->markUsed(Context); + markDeclUsed(static_cast(NewVD), Context); NewVD->setInit(Init); if (NewVD->isParameterPack()) getCurLambda()->LocalPacks.push_back(NewVD); @@ -1975,7 +1975,7 @@ Lambda->lookup( Context.DeclarationNames.getCXXOperatorName(OO_Call)).front()); CallOperator->setReferenced(); - CallOperator->markUsed(Context); + markDeclUsed(CallOperator, Context); ExprResult Init = PerformCopyInitialization( InitializedEntity::InitializeLambdaToBlock(ConvLocation, Src->getType()), diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -83,8 +83,6 @@ return nullptr; } - // We start in the global module; all those declarations are implicitly - // module-private (though they do not have module linkage). Module *GlobalModule = PushGlobalModuleFragment(ModuleLoc, /*IsImplicit=*/false); @@ -96,8 +94,10 @@ // provide declarations that are attached to the global module and usable // within the module unit. // - // So the declations in the global module shouldn't be visible by default. - TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); + // We are expected to elide GMF decls that are not used in the purview of the + // named module. In order to do that, create GMF decls with a marker that is + // updated when a decl is used. + TU->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleUnreachable); TU->setLocalOwningModule(GlobalModule); // FIXME: Consider creating an explicit representation of this declaration. @@ -261,9 +261,8 @@ const_cast(getLangOpts()).CurrentModule = ModuleName; auto &Map = PP.getHeaderSearchInfo().getModuleMap(); - Module *Mod; - - ImportDecl *Import = nullptr; + Module *Mod; // The module we are creating. + Module *Interface = nullptr; // The interface fir an implementation. switch (MDK) { case ModuleDeclKind::Interface: case ModuleDeclKind::PartitionInterface: { @@ -298,22 +297,18 @@ // declaration. // First find the interface we need to import. - Mod = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, - Module::AllVisible, - /*IsInclusionDirective=*/false); - if (!Mod) { + Interface = getModuleLoader().loadModule(ModuleLoc, {ModuleNameLoc}, + Module::AllVisible, + /*IsInclusionDirective=*/false); + if (!Interface) { Diag(ModuleLoc, diag::err_module_not_defined) << ModuleName; // Create an empty module interface unit for error recovery. Mod = Map.createModuleForInterfaceUnit(ModuleLoc, ModuleName, GlobalModuleFragment); - } else { - // Make the decl. - Import = ImportDecl::Create(Context, CurContext, ModuleLoc, Mod, - Path[0].second); - // now create the implementation module + } else + // We found the interface, now create the implementation module Mod = Map.createModuleForImplementationUnit(ModuleLoc, ModuleName, GlobalModuleFragment); - } } break; case ModuleDeclKind::PartitionImplementation: @@ -357,13 +352,23 @@ // We already potentially made an implicit import (in the case of a module // implementation unit importing its interface). Make this module visible // and return the import decl to be added to the current TU. - if (Import) - VisibleModules.setVisible(Import->getImportedModule(), ModuleLoc); - + if (Interface) { + // Make the import decl for the interface. + ImportDecl *Import = ImportDecl::Create(Context, CurContext, ModuleLoc, + Interface, Path[0].second); + VisibleModules.setVisible(Interface, ModuleLoc); + if (auto *InterfaceGMF = Interface->getGlobalModuleFragment()) + // The fact that the GMF is a seaprate sub-module is an implementation + // detail, if it's present then it should also be made visible here. + // Elision of unreachable decls is handled by marking them. + VisibleModules.setVisible(InterfaceGMF, ModuleLoc); + + // If we made an implicit import of the module interface, then return the + // imported module decl. + return ConvertDeclToDeclGroup(Import); + } // FIXME: Create a ModuleDecl. - // If we made an implicit import of the module interface, then return the - // imported module decl. - return Import ? ConvertDeclToDeclGroup(Import) : nullptr; + return nullptr; } Sema::DeclGroupPtrTy @@ -951,3 +956,56 @@ "left the wrong module scope, which is not global module fragment"); ModuleScopes.pop_back(); } + +// A helper to recurse through the statement tree finding DeclRefExprs and +// marking the referenced decls as reachable. +static void FindDeclRefExprs(clang::Stmt *S) { + if (DeclRefExpr *DR = dyn_cast(S)) { + auto *D = DR->getFoundDecl(); +// llvm::dbgs() << "DR:"; D->dump(); + if (D->isModuleUnreachable()) { + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported ); + } + } else + for (auto *CH : S->children()) + FindDeclRefExprs(CH); +} + +// Given that we mark a decl 'orig' as reachable, this then analyzes that decl +// to determine if it, in turn, makes other decls reachable. We can simplify +// some checking here - we know we are in a module purview. +void Sema::markReachableGMFDecls(Decl *Orig) { + + if (isa(*Orig)) { + auto *FD = cast(Orig); + for (auto *P : FD->parameters()) { +// bool Changed = false; + if (P->isModuleUnreachable()) { + P->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); +// Changed = true; + } + if (auto *E = P->getDefaultArg()) { + if (isa(*E)) { + Decl *D = cast(E)->getCalleeDecl(); + if (D->isModuleUnreachable()) { + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ReachableWhenImported); +// Changed = true; + } + } + } +// if (Changed) { +// llvm::dbgs() << "P:"; P->dump(); +// } + } + const FunctionDecl *BodyDecl; + if (auto *S = FD->getBody(BodyDecl)) { + FindDeclRefExprs(S); + } + } else if (isa(*Orig)) { + auto *VD = cast(Orig); + QualType T = VD->getTypeSourceInfo() + ? VD->getTypeSourceInfo()->getType() + : VD->getASTContext().getUnqualifiedObjCPointerType(VD->getType()); + T.dump(); + } +} diff --git a/clang/lib/Sema/SemaOpenMP.cpp b/clang/lib/Sema/SemaOpenMP.cpp --- a/clang/lib/Sema/SemaOpenMP.cpp +++ b/clang/lib/Sema/SemaOpenMP.cpp @@ -1455,7 +1455,7 @@ SourceLocation Loc, bool RefersToCapture = false) { D->setReferenced(); - D->markUsed(S.Context); + S.markDeclUsed(D, S.Context); return DeclRefExpr::Create(S.getASTContext(), NestedNameSpecifierLoc(), SourceLocation(), D, RefersToCapture, Loc, Ty, VK_LValue); @@ -3004,7 +3004,7 @@ // Mark variable as used. VD->setReferenced(); - VD->markUsed(Context); + markDeclUsed(static_cast(VD), Context); QualType QType = VD->getType(); if (QType->isDependentType() || QType->isInstantiationDependentType()) { diff --git a/clang/lib/Sema/SemaStmt.cpp b/clang/lib/Sema/SemaStmt.cpp --- a/clang/lib/Sema/SemaStmt.cpp +++ b/clang/lib/Sema/SemaStmt.cpp @@ -2751,7 +2751,7 @@ if (RangeVarType->isDependentType()) { // The range is implicitly used as a placeholder when it is dependent. - RangeVar->markUsed(Context); + markDeclUsed(static_cast(RangeVar), Context); // Deduce any 'auto's in the loop variable as 'DependentTy'. We'll fill // them in properly when we instantiate the loop. @@ -3266,7 +3266,7 @@ SourceLocation LabelLoc, LabelDecl *TheDecl) { setFunctionHasBranchIntoScope(); - TheDecl->markUsed(Context); + markDeclUsed(TheDecl, Context); return new (Context) GotoStmt(TheDecl, GotoLoc, LabelLoc); } diff --git a/clang/lib/Sema/SemaStmtAsm.cpp b/clang/lib/Sema/SemaStmtAsm.cpp --- a/clang/lib/Sema/SemaStmtAsm.cpp +++ b/clang/lib/Sema/SemaStmtAsm.cpp @@ -956,7 +956,7 @@ if (Label->isMSAsmLabel()) { // If we have previously created this label implicitly, mark it as used. - Label->markUsed(Context); + markDeclUsed(Label, Context); } else { // Otherwise, insert it, but only resolve it if we have seen the label itself. std::string InternalName; diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -1004,7 +1004,7 @@ SemaRef.inferGslPointerAttribute(Typedef); Typedef->setAccess(D->getAccess()); - Typedef->setReferenced(D->isReferenced()); + SemaRef.setDeclReferenced(Typedef, D->isReferenced()); return Typedef; } @@ -1069,7 +1069,7 @@ Decl *TemplateDeclInstantiator::VisitBindingDecl(BindingDecl *D) { auto *NewBD = BindingDecl::Create(SemaRef.Context, Owner, D->getLocation(), D->getIdentifier()); - NewBD->setReferenced(D->isReferenced()); + SemaRef.setDeclReferenced(NewBD, D->isReferenced()); SemaRef.CurrentInstantiationScope->InstantiatedLocal(D, NewBD); return NewBD; } @@ -5210,8 +5210,8 @@ if (!OldVar->isStaticDataMember()) { if (OldVar->isUsed(false)) - NewVar->setIsUsed(); - NewVar->setReferenced(OldVar->isReferenced()); + setDeclIsUsed(NewVar); + setDeclReferenced(static_cast(NewVar), OldVar->isReferenced()); } InstantiateAttrs(TemplateArgs, OldVar, NewVar, LateAttrs, StartingScope); diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -621,6 +621,7 @@ case Decl::ModuleOwnershipKind::VisibleWhenImported: case Decl::ModuleOwnershipKind::ReachableWhenImported: case Decl::ModuleOwnershipKind::ModulePrivate: + case Decl::ModuleOwnershipKind::ModuleUnreachable: break; default: @@ -631,9 +632,10 @@ // Store the owning submodule ID in the declaration. D->setOwningModuleID(SubmoduleID); - if (ModulePrivate) { - // Module-private declarations are never visible, so there is no work to - // do. + if (ModulePrivate || + ModuleOwnership == Decl::ModuleOwnershipKind::ModuleUnreachable) { + // Module-private and unreachable declarations are never visible, so + // there is no work to do. } else if (Reader.getContext().getLangOpts().ModulesLocalVisibility) { // If local visibility is being tracked, this declaration will become // hidden and visible as the owning module does. @@ -644,6 +646,8 @@ else Reader.HiddenNamesMap[Owner].push_back(D); } + } else if (ModuleOwnership == Decl::ModuleOwnershipKind::ModuleUnreachable) { + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleUnreachable); } else if (ModulePrivate) { D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); } diff --git a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp --- a/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp +++ b/clang/test/CXX/basic/basic.scope/basic.scope.namespace/p2.cpp @@ -29,8 +29,7 @@ #endif void test_early() { - in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} - // expected-note@* {{not visible}} + in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}} global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}} // expected-note@p2.cpp:16 {{not visible}} @@ -57,8 +56,7 @@ #endif void test_late() { - in_header = 1; // expected-error {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} - // expected-note@* {{not visible}} + in_header = 1; // expected-error {{use of undeclared identifier 'in_header'}} global_module_fragment = 1; // expected-error {{missing '#include'; 'global_module_fragment' must be declared before it is used}} // expected-note@p2.cpp:16 {{not visible}}