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, it is permitted + /// to discard it and therefore it is not reachable or visible to importers + /// of the named module of which the GMF is part. + ModuleDiscardable }; protected: @@ -594,6 +597,12 @@ void setReferenced(bool R = true) { Referenced = R; } + /// Whether this declaration should be retained if it is used. + bool isModuleDiscardable() const { + return getModuleOwnershipKind() == + Decl::ModuleOwnershipKind::ModuleDiscardable; + } + /// 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::ModuleDiscardable; + } /// 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,52 @@ bool isAcceptableSlow(const NamedDecl *D, AcceptableKind Kind); + llvm::SmallPtrSet SeenDecls; + llvm::SmallPtrSet SeenTypes; + + /// Examine a type, unwrapping ay decls that might be in the GMF. + void findGMFReachableDeclsForType(const Type *T); + + /// Walk a statement/expression tree looking for cases that require GMF decls + /// to be made reachable. + void findGMFReachableDeclExprs(clang::Stmt *S); + + /// Starting from a decl D mark any additional decls in the GMF this makes + /// reachable. Make the first decl, D, visible if so specified. + void markGMFDeclsReachableFrom(Decl *D, bool MakeVisible = false); + + // We have marked some decl as used (or referenced) and therefore (if we are + // in a module purview, and there is a GMF) we need to see if that use or + // reference requires us to make GMF decl(s) reachable. + void handleGMFDeclsReachableFrom(Decl *D) { + // Early exit for the cases we do not need to consider. + if (!GlobalModuleFragment || !isCurrentModulePurview()) + return; + + // We only need to make the decls reachable, not visible. + markGMFDeclsReachableFrom(D); + } + public: + /// To implement C++20 GMF elision semantics, we initially mark decls in the + /// Global Module Fragment as "ModuleDiscardable". If the decl is then used + /// within the module purview, we reset that to visible. + void setDeclIsUsed(Decl *D) { + D->setIsUsed(); + handleGMFDeclsReachableFrom(D); + } + + /// Likewise when the decl is marked used and notifies mutation listeners. + void markDeclUsed(Decl *D, ASTContext &C) { + D->markUsed(C); + handleGMFDeclsReachableFrom(D); + } + + void setDeclReferenced(Decl *D, bool R = true) { + D->setReferenced(R); + handleGMFDeclsReachableFrom(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::ModuleDiscardable: 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" @@ -57,6 +58,29 @@ llvm_unreachable("Decl that isn't part of DeclNodes.inc!"); } +static void dumpModuleOwnership(raw_ostream &OS, const Decl *D) { + switch (D->getModuleOwnershipKind()) { + case Decl::ModuleOwnershipKind::Unowned: + // OS << " Unowned"; + break; + case Decl::ModuleOwnershipKind::Visible: + OS << " Visible"; + break; + case Decl::ModuleOwnershipKind::VisibleWhenImported: + OS << " VisibleWhenImported"; + break; + case Decl::ModuleOwnershipKind::ReachableWhenImported: + OS << " ReachableWhenImported"; + break; + case Decl::ModuleOwnershipKind::ModulePrivate: + OS << " __module_private__"; + break; + case Decl::ModuleOwnershipKind::ModuleDiscardable: + OS << " ModuleDiscardable"; + break; + } +} + TextNodeDumper::TextNodeDumper(raw_ostream &OS, const ASTContext &Context, bool ShowColors) : TextTreeStructure(OS, ShowColors), OS(OS), ShowColors(ShowColors), @@ -1615,8 +1639,7 @@ void TextNodeDumper::VisitTypedefDecl(const TypedefDecl *D) { dumpName(D); dumpType(D->getUnderlyingType()); - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitEnumDecl(const EnumDecl *D) { @@ -1627,8 +1650,7 @@ OS << " struct"; } dumpName(D); - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); if (D->isFixed()) dumpType(D->getIntegerType()); } @@ -1636,8 +1658,7 @@ void TextNodeDumper::VisitRecordDecl(const RecordDecl *D) { OS << ' ' << D->getKindName(); dumpName(D); - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); if (D->isCompleteDefinition()) OS << " definition"; } @@ -1666,8 +1687,7 @@ OS << " inline"; if (D->isVirtualAsWritten()) OS << " virtual"; - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); if (D->isPure()) OS << " pure"; @@ -1741,8 +1761,7 @@ dumpType(D->getType()); if (D->isMutable()) OS << " mutable"; - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitVarDecl(const VarDecl *D) { @@ -1761,8 +1780,7 @@ OS << " tls_dynamic"; break; } - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); if (D->isNRVOVariable()) OS << " nrvo"; if (D->isInline()) @@ -1938,6 +1956,7 @@ { ColorScope Color(OS, ShowColors, DeclKindNameColor); OS << "DefinitionData"; + dumpModuleOwnership(OS, D); } #define FLAG(fn, name) \ if (D->fn()) \ @@ -1977,6 +1996,7 @@ FLAG(hasConstexprDefaultConstructor, constexpr); FLAG(needsImplicitDefaultConstructor, needs_implicit); FLAG(defaultedDefaultConstructorIsConstexpr, defaulted_is_constexpr); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -1995,6 +2015,7 @@ if (!D->needsOverloadResolutionForCopyConstructor()) FLAG(defaultedCopyConstructorIsDeleted, defaulted_is_deleted); FLAG(implicitCopyConstructorHasConstParam, implicit_has_const_param); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2012,6 +2033,7 @@ needs_overload_resolution); if (!D->needsOverloadResolutionForMoveConstructor()) FLAG(defaultedMoveConstructorIsDeleted, defaulted_is_deleted); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2027,6 +2049,7 @@ FLAG(needsImplicitCopyAssignment, needs_implicit); FLAG(needsOverloadResolutionForCopyAssignment, needs_overload_resolution); FLAG(implicitCopyAssignmentHasConstParam, implicit_has_const_param); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2041,6 +2064,7 @@ FLAG(hasUserDeclaredMoveAssignment, user_declared); FLAG(needsImplicitMoveAssignment, needs_implicit); FLAG(needsOverloadResolutionForMoveAssignment, needs_overload_resolution); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2058,6 +2082,7 @@ FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution); if (!D->needsOverloadResolutionForDestructor()) FLAG(defaultedDestructorIsDeleted, defaulted_is_deleted); + dumpModuleOwnership(OS, D); }); }); @@ -2069,20 +2094,24 @@ dumpType(I.getType()); if (I.isPackExpansion()) OS << "..."; + dumpModuleOwnership(OS, D); }); } } void TextNodeDumper::VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitClassTemplateDecl(const ClassTemplateDecl *D) { dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitVarTemplateDecl(const VarTemplateDecl *D) { dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitBuiltinTemplateDecl(const BuiltinTemplateDecl *D) { @@ -2106,6 +2135,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitNonTypeTemplateParmDecl( @@ -2115,6 +2145,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitTemplateTemplateParmDecl( @@ -2123,6 +2154,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) { @@ -2130,6 +2162,7 @@ if (D->getQualifier()) D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy()); OS << D->getDeclName(); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) { @@ -2157,6 +2190,7 @@ void TextNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) { OS << ' '; dumpBareDeclRef(D->getTargetDecl()); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitConstructorUsingShadowDecl( @@ -2203,6 +2237,7 @@ void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) { if (TypeSourceInfo *T = D->getFriendType()) dumpType(T->getType()); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitObjCIvarDecl(const ObjCIvarDecl *D) { diff --git a/clang/lib/Sema/Sema.cpp b/clang/lib/Sema/Sema.cpp --- a/clang/lib/Sema/Sema.cpp +++ b/clang/lib/Sema/Sema.cpp @@ -1118,6 +1118,10 @@ assert(DelayedDiagnostics.getCurrentPool() == nullptr && "reached end of translation unit with a pool attached?"); + // We have finished checking if decls in a GMF need to be made reachable. + SeenDecls.clear(); + SeenTypes.clear(); + // If code completion is enabled, don't perform any end-of-translation-unit // work. if (PP.isCodeCompletionEnabled()) 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 @@ -4114,7 +4114,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. @@ -4530,7 +4530,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 @@ -8796,7 +8796,7 @@ return; } FD->setBody(Body.get()); - FD->markUsed(Context); + markDeclUsed(FD, Context); } // The exception specification is needed because we are defining the @@ -13490,7 +13490,7 @@ ? Constructor->getEndLoc() : Constructor->getLocation(); Constructor->setBody(new (Context) CompoundStmt(Loc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13669,7 +13669,7 @@ } Constructor->setBody(new (Context) CompoundStmt(InitLoc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13780,7 +13780,7 @@ ? Destructor->getEndLoc() : Destructor->getLocation(); Destructor->setBody(new (Context) CompoundStmt(Loc)); - Destructor->markUsed(Context); + markDeclUsed(Destructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Destructor); @@ -14639,7 +14639,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); @@ -15009,7 +15009,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); @@ -15153,7 +15153,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); CopyConstructor->setBody( ActOnCompoundStmt(Loc, Loc, None, /*isStmtExpr=*/false).getAs()); - CopyConstructor->markUsed(Context); + markDeclUsed(CopyConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15279,7 +15279,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); MoveConstructor->setBody(ActOnCompoundStmt( Loc, Loc, None, /*isStmtExpr=*/ false).getAs()); - MoveConstructor->markUsed(Context); + markDeclUsed(MoveConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15330,7 +15330,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())); @@ -15342,7 +15342,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()) { @@ -15397,7 +15397,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" @@ -15745,7 +15746,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)); @@ -17830,7 +17831,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. @@ -18063,7 +18064,7 @@ } } - Func->markUsed(Context); + markDeclUsed(Func, Context); } } @@ -18134,7 +18135,7 @@ } } - Var->markUsed(SemaRef.Context); + SemaRef.markDeclUsed(Var, SemaRef.Context); } void Sema::MarkCaptureUsedInEnclosingContext(VarDecl *Capture, @@ -19290,7 +19291,7 @@ assert((!E || isa(E) || isa(E) || isa(E)) && "Invalid Expr argument to DoMarkVarDeclReferenced"); - Var->setReferenced(); + SemaRef.setDeclReferenced(Var); if (Var->isInvalidDecl()) return; @@ -19566,7 +19567,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 @@ -12,10 +12,14 @@ //===----------------------------------------------------------------------===// #include "clang/AST/ASTConsumer.h" +#include "clang/AST/DeclFriend.h" #include "clang/Lex/HeaderSearch.h" #include "clang/Lex/Preprocessor.h" #include "clang/Sema/SemaInternal.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/Format.h" + using namespace clang; using namespace sema; @@ -83,8 +87,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 +98,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::ModuleDiscardable); TU->setLocalOwningModule(GlobalModule); // FIXME: Consider creating an explicit representation of this declaration. @@ -901,6 +905,18 @@ diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, BlockStart); } + // An export must be in module purview, so mark any used GMF content + // as not discardable, and the current Decl as visible. + if (auto *USD = dyn_cast(Child)) { + NamedDecl *Target = USD->getUnderlyingDecl(); + if (Target->hasOwningModule() && + Target->getOwningModule()->isGlobalModule()) { + // Handle using declarations that make GMF content visible. + markGMFDeclsReachableFrom(Target, /*MakeVisible*/ true); + } + } else { + markGMFDeclsReachableFrom(Child, /*MakeVisible*/ true); + } } } @@ -935,3 +951,171 @@ "left the wrong module scope, which is not global module fragment"); ModuleScopes.pop_back(); } + +// A helper to process a used type to find GMF reachable decls which it uses. + +void Sema::findGMFReachableDeclsForType(const Type *T) { + assert(T && "missing type pointer?"); + // Only visit each Type once. + if (!SeenTypes.insert(T).second) + return; + // Return as early as possible if this is not an entity we would need to mark + // but typedefs of built-in types need to be caught first (since the typedef + // could be in the GMF). + if (auto *TD = dyn_cast(T)) { + markGMFDeclsReachableFrom(TD->getDecl()); + } else if (T->isBuiltinType()) { + return; + } else if (auto *TST = dyn_cast(T)) { + if (auto *RT = TST->getAsCXXRecordDecl()) + markGMFDeclsReachableFrom(RT); + TemplateDecl *TD = TST->getTemplateName().getAsTemplateDecl(); + markGMFDeclsReachableFrom(TD); + } else if (auto *RD = T->getAsCXXRecordDecl()) { + markGMFDeclsReachableFrom(RD); + } else if (auto *RT = dyn_cast(T)) { + findGMFReachableDeclsForType(RT->getPointeeType().getTypePtr()); + } else { + llvm::dbgs() << llvm::format("unhandled type: %p ", T); + T->dump(); + } +} + +// A helper to recurse through a stmt/expr tree finding expressions with +// content that makes GMF decls reachable. + +void Sema::findGMFReachableDeclExprs(clang::Stmt *S) { + + if (DeclRefExpr *DR = dyn_cast(S)) { + markGMFDeclsReachableFrom(DR->getFoundDecl()); + } else if (DependentScopeDeclRefExpr *DS = + dyn_cast(S)) { + auto *NNS = DS->getQualifier(); + assert(NNS && (NNS->getKind() == NestedNameSpecifier::TypeSpec) && + "not a type spec"); + findGMFReachableDeclsForType(NNS->getAsType()); + } else if (UnresolvedLookupExpr *ULE = dyn_cast(S)) { + for (auto *D : ULE->decls()) + markGMFDeclsReachableFrom(D); + } else if (DeclStmt *DS = dyn_cast(S)) { + if (DS->isSingleDecl()) + markGMFDeclsReachableFrom(DS->getSingleDecl()); + else { + for (auto *D : DS->decls()) { + llvm::dbgs() << "EMD: "; + D->dump(); + markGMFDeclsReachableFrom(D); + } + } + } + + // Recurse into sub-expressions. + for (auto *CH : S->children()) + findGMFReachableDeclExprs(CH); +} + +// Starting from decl Orig (which is marked as reachable/visible if it is in +// the GMF and currently marked as ModuleDiscardable), process other decls +// reachable from this one and mark them too. + +void Sema::markGMFDeclsReachableFrom(Decl *Orig, bool MakeVisible) { + // Only visit each Decl once. + if (!SeenDecls.insert(Orig).second) + return; + + // Do not alter the ownership unless it is ModuleDiscardable. + if (Orig->isModuleDiscardable()) { + assert(Orig->getOwningModule() && + Orig->getOwningModule()->isGlobalModule() && + "should not have a discardable decl outside the GMF"); + Orig->setModuleOwnershipKind( + MakeVisible ? Decl::ModuleOwnershipKind::VisibleWhenImported + : Decl::ModuleOwnershipKind::ReachableWhenImported); + } + + if (auto FT = dyn_cast(Orig)) { + markGMFDeclsReachableFrom(FT->getTemplatedDecl()); + for (auto *SP : FT->specializations()) + markGMFDeclsReachableFrom(SP); + for (auto *TA : *FT->getTemplateParameters()) + markGMFDeclsReachableFrom(TA); + return; + } + + if (auto *FD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(FD->getReturnType().getTypePtr()); + for (ParmVarDecl *P : FD->parameters()) + markGMFDeclsReachableFrom(P); + const FunctionDecl *BodyDecl; + if (auto *S = FD->getBody(BodyDecl)) + findGMFReachableDeclExprs(S); + return; + } + + if (auto TS = dyn_cast(Orig)) { + markGMFDeclsReachableFrom(TS->getSpecializedTemplate()); + for (auto B : TS->bases()) { + auto *T = B.getType().getTypePtr(); + assert(T->isRecordType() && "base not a record?"); + markGMFDeclsReachableFrom(T->getAsCXXRecordDecl()); + } + return; + } + + if (auto CD = dyn_cast(Orig)) { + markGMFDeclsReachableFrom(CD->getTemplatedDecl()); + for (auto *SP : CD->specializations()) + markGMFDeclsReachableFrom(SP); + for (auto *TA : *CD->getTemplateParameters()) + markGMFDeclsReachableFrom(TA); + return; + } + + if (auto *RD = dyn_cast(Orig)) { + if (!RD->isCompleteDefinition()) + return; + DeclContext *DC = Decl::castToDeclContext(RD); + for (auto *D : DC->decls()) + markGMFDeclsReachableFrom(D); + for (auto B : RD->bases()) { + auto *T = B.getType().getTypePtr(); + assert(T->isRecordType() && "base not a record?"); + auto *BR = T->getAsCXXRecordDecl(); + markGMFDeclsReachableFrom(BR); + } + return; + } + + if (auto *TTA = dyn_cast(Orig)) { + if (TTA->hasDefaultArgument()) + findGMFReachableDeclsForType(TTA->getDefaultArgument().getTypePtr()); + return; + } + + if (auto *PD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(PD->getOriginalType().getTypePtr()); + if (auto *E = PD->getDefaultArg()) + findGMFReachableDeclExprs(E); + return; + } + + if (auto *VD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(VD->getType().getTypePtr()); + if (VD->hasInit()) + findGMFReachableDeclExprs(VD->getInit()); + return; + } + + if (auto *FD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(FD->getType().getTypePtr()); + if (auto *Init = FD->getInClassInitializer()) + findGMFReachableDeclExprs(Init); + return; + } + + if (auto *FD = dyn_cast(Orig)) { + if (auto *DD = FD->getFriendDecl()) + markGMFDeclsReachableFrom(DD, MakeVisible); + return; + } +} 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 @@ -953,7 +953,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; } @@ -5212,8 +5212,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::ModuleDiscardable: 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::ModuleDiscardable) { + // 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::ModuleDiscardable) { + D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModuleDiscardable); } else if (ModulePrivate) { D->setModuleOwnershipKind(Decl::ModuleOwnershipKind::ModulePrivate); } diff --git a/clang/test/AST/ast-dump-decl.c b/clang/test/AST/ast-dump-decl.c --- a/clang/test/AST/ast-dump-decl.c +++ b/clang/test/AST/ast-dump-decl.c @@ -1,16 +1,16 @@ // Test without serialization: // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump -ast-dump-filter Test %s \ -// RUN: | FileCheck --strict-whitespace %s +// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE // // Test with serialization: // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -emit-pch -o %t %s // RUN: %clang_cc1 -x c -triple x86_64-unknown-unknown -Wno-strict-prototypes -include-pch %t \ // RUN: -ast-dump-all -ast-dump-filter Test /dev/null \ // RUN: | sed -e "s/ //" -e "s/ imported//" \ -// RUN: | FileCheck --strict-whitespace %s +// RUN: | FileCheck --strict-whitespace %s -check-prefix CHECK-NOMODULE // // RUN: %clang_cc1 -triple x86_64-unknown-unknown -Wno-strict-prototypes -ast-dump %s \ -// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s +// RUN: | FileCheck -check-prefix CHECK-TU --strict-whitespace %s -check-prefix CHECK-NOMODULE // // RUN: %clang_cc1 -fmodules -fmodules-local-submodule-visibility -fmodule-name=X \ // RUN: -triple x86_64-unknown-unknown -Wno-strict-prototypes -fmodule-map-file=%S/Inputs/module.modulemap \ @@ -166,13 +166,16 @@ // CHECK-MODULE: FieldDecl{{.*}} TestFieldDeclPrivate 'int' __module_private__ int TestVarDecl; -// CHECK: VarDecl{{.*}} TestVarDecl 'int' +// CHECK-NOMODULE: VarDecl{{.*}} TestVarDecl 'int' +// CHECK-MODULE: VarDecl{{.*}} TestVarDecl 'int' VisibleWhenImported extern int TestVarDeclSC; -// CHECK: VarDecl{{.*}} TestVarDeclSC 'int' extern +// CHECK-NOMODULE: VarDecl{{.*}} TestVarDeclSC 'int' extern +// CHECK-MODULE: VarDecl{{.*}} TestVarDeclSC 'int' extern VisibleWhenImported __thread int TestVarDeclThread; -// CHECK: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} +// CHECK-NOMODULE: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} +// CHECK-MODULE: VarDecl{{.*}} TestVarDeclThread 'int' tls{{$}} VisibleWhenImported __module_private__ int TestVarDeclPrivate; // CHECK-MODULE: VarDecl{{.*}} TestVarDeclPrivate 'int' __module_private__ 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,9 +29,8 @@ #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 {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} + // expected-note@foo.h:3 {{declaration here is not visible}} 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,9 +56,8 @@ #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 {{missing '#include "foo.h"'; 'in_header' must be declared before it is used}} + // expected-note@foo.h:3 {{declaration here is not visible}} 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}} diff --git a/clang/test/Modules/cxx-templates.cpp b/clang/test/Modules/cxx-templates.cpp --- a/clang/test/Modules/cxx-templates.cpp +++ b/clang/test/Modules/cxx-templates.cpp @@ -252,7 +252,7 @@ // CHECK-DUMP: ClassTemplateDecl {{.*}} <{{.*[/\\]}}cxx-templates-common.h:1:1, {{.*}}> col:{{.*}} in cxx_templates_common SomeTemplate // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate // CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' -// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition +// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition // CHECK-DUMP-NEXT: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor // CHECK-DUMP-NEXT: CopyConstructor @@ -263,7 +263,7 @@ // CHECK-DUMP-NEXT: TemplateArgument type 'char[2]' // CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} prev {{.*}} SomeTemplate // CHECK-DUMP-NEXT: TemplateArgument type 'char[1]' -// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate definition +// CHECK-DUMP: ClassTemplateSpecializationDecl {{.*}} SomeTemplate Visible definition // CHECK-DUMP-NEXT: DefinitionData // CHECK-DUMP-NEXT: DefaultConstructor // CHECK-DUMP-NEXT: CopyConstructor diff --git a/clang/test/Modules/cxx20-10-4-ex2.cpp b/clang/test/Modules/cxx20-10-4-ex2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/Modules/cxx20-10-4-ex2.cpp @@ -0,0 +1,60 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-interface.cpp \ +// RUN: -emit-module-interface -o M.pcm -Wno-unused-value +// RUN: %clang_cc1 -std=c++20 std-10-4-ex2-implementation.cpp \ +// RUN: -fmodule-file=M.pcm -fsyntax-only -verify +//--- std-10-4-ex2.h + +namespace N { +struct X {}; +int d(); +int e(); +inline int f(X, int = d()) { return e(); } +int g(X); +int h(X); +} // namespace N + +//--- std-10-4-ex2-interface.cpp + +module; + +#include "std-10-4-ex2.h" + +export module M; + +template int use_f() { + N::X x; // N::X, N, and :: are decl-reachable from use_f + return f(x, 123); // N::f is decl-reachable from use_f, + // N::e is indirectly decl-reachable from use_f + // because it is decl-reachable from N::f, and + // N::d is decl-reachable from use_f + // because it is decl-reachable from N::f + // even though it is not used in this call +} + +template int use_g() { + N::X x; // N::X, N, and :: are decl-reachable from use_g + return g((T(), x)); // N::g is not decl-reachable from use_g +} + +template int use_h() { + N::X x; // N::X, N, and :: are decl-reachable from use_h + return h((T(), x)); // N::h is not decl-reachable from use_h, but + // N::h is decl-reachable from use_h +} + +int k = use_h(); +// use_h is decl-reachable from k, so +// N::h is decl-reachable from k + +//--- std-10-4-ex2-implementation.cpp + +module M; + +int a = use_f(); +int b = use_g(); // expected-error@std-10-4-ex2-interface.cpp:20 {{use of undeclared identifier 'g'}} + // expected-note@-1 {{in instantiation of function template specialization 'use_g' requested here}} +int c = use_h(); diff --git a/clang/test/Modules/explicitly-specialized-template.cpp b/clang/test/Modules/explicitly-specialized-template.cpp --- a/clang/test/Modules/explicitly-specialized-template.cpp +++ b/clang/test/Modules/explicitly-specialized-template.cpp @@ -42,6 +42,6 @@ foo f; // expected-error {{'foo' must be declared before it is used}} // expected-note@* {{declaration here is not visible}} int bar() { - X x; - return x.print(); + X y; + return y.print(); } diff --git a/clang/test/Modules/template-function-specialization.cpp b/clang/test/Modules/template-function-specialization.cpp --- a/clang/test/Modules/template-function-specialization.cpp +++ b/clang/test/Modules/template-function-specialization.cpp @@ -51,7 +51,9 @@ foo2(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} // expected-note@* {{declaration here is not visible}} foo2(); // expected-error {{missing '#include'; 'foo2' must be declared before it is used}} + // expected-error@-1 {{missing '#include'; explicit specialization of 'foo2' must be declared before it is used}} // expected-note@* {{declaration here is not visible}} + // expected-note@* {{explicit specialization declared here is not reachable}} foo3(); foo3();