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 @@ -2275,7 +2275,55 @@ // Determine whether the module M belongs to the current TU. bool isModuleUnitOfCurrentTU(const Module *M) const; + /// When checking for Global Module Fragment decls that need to be made + /// reachable we also need to consider the types used. This set is used to + /// ensure that we only visit each type once. + llvm::SmallPtrSet GMFTypesVisited; + + /// Examine a type, unwrapping any decls that might be in the GMF. + void findGMFReachableDeclsForType(const QualType &Q); + + /// 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. + void markGMFDeclsReachableFrom(Decl *D); + + // 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) { + // Try to use an early exit for the cases we do not need to consider. + if (!getLangOpts().CPlusPlusModules || !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"; @@ -1747,8 +1767,7 @@ dumpType(D->getType()); if (D->isMutable()) OS << " mutable"; - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitVarDecl(const VarDecl *D) { @@ -1767,8 +1786,7 @@ OS << " tls_dynamic"; break; } - if (D->isModulePrivate()) - OS << " __module_private__"; + dumpModuleOwnership(OS, D); if (D->isNRVOVariable()) OS << " nrvo"; if (D->isInline()) @@ -1944,6 +1962,7 @@ { ColorScope Color(OS, ShowColors, DeclKindNameColor); OS << "DefinitionData"; + dumpModuleOwnership(OS, D); } #define FLAG(fn, name) \ if (D->fn()) \ @@ -1983,6 +2002,7 @@ FLAG(hasConstexprDefaultConstructor, constexpr); FLAG(needsImplicitDefaultConstructor, needs_implicit); FLAG(defaultedDefaultConstructorIsConstexpr, defaulted_is_constexpr); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2001,6 +2021,7 @@ if (!D->needsOverloadResolutionForCopyConstructor()) FLAG(defaultedCopyConstructorIsDeleted, defaulted_is_deleted); FLAG(implicitCopyConstructorHasConstParam, implicit_has_const_param); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2018,6 +2039,7 @@ needs_overload_resolution); if (!D->needsOverloadResolutionForMoveConstructor()) FLAG(defaultedMoveConstructorIsDeleted, defaulted_is_deleted); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2033,6 +2055,7 @@ FLAG(needsImplicitCopyAssignment, needs_implicit); FLAG(needsOverloadResolutionForCopyAssignment, needs_overload_resolution); FLAG(implicitCopyAssignmentHasConstParam, implicit_has_const_param); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2047,6 +2070,7 @@ FLAG(hasUserDeclaredMoveAssignment, user_declared); FLAG(needsImplicitMoveAssignment, needs_implicit); FLAG(needsOverloadResolutionForMoveAssignment, needs_overload_resolution); + dumpModuleOwnership(OS, D); }); AddChild([=] { @@ -2064,6 +2088,7 @@ FLAG(needsOverloadResolutionForDestructor, needs_overload_resolution); if (!D->needsOverloadResolutionForDestructor()) FLAG(defaultedDestructorIsDeleted, defaulted_is_deleted); + dumpModuleOwnership(OS, D); }); }); @@ -2075,20 +2100,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) { @@ -2112,6 +2141,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitNonTypeTemplateParmDecl( @@ -2121,6 +2151,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitTemplateTemplateParmDecl( @@ -2129,6 +2160,7 @@ if (D->isParameterPack()) OS << " ..."; dumpName(D); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitUsingDecl(const UsingDecl *D) { @@ -2136,6 +2168,7 @@ if (D->getQualifier()) D->getQualifier()->print(OS, D->getASTContext().getPrintingPolicy()); OS << D->getDeclName(); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitUsingEnumDecl(const UsingEnumDecl *D) { @@ -2163,6 +2196,7 @@ void TextNodeDumper::VisitUsingShadowDecl(const UsingShadowDecl *D) { OS << ' '; dumpBareDeclRef(D->getTargetDecl()); + dumpModuleOwnership(OS, D); } void TextNodeDumper::VisitConstructorUsingShadowDecl( @@ -2209,6 +2243,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 @@ -1105,6 +1105,9 @@ 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. + GMFTypesVisited.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 @@ -4164,7 +4164,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. @@ -4580,7 +4580,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 @@ -13503,7 +13503,7 @@ ? Constructor->getEndLoc() : Constructor->getLocation(); Constructor->setBody(new (Context) CompoundStmt(Loc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13682,7 +13682,7 @@ } Constructor->setBody(new (Context) CompoundStmt(InitLoc)); - Constructor->markUsed(Context); + markDeclUsed(Constructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Constructor); @@ -13793,7 +13793,7 @@ ? Destructor->getEndLoc() : Destructor->getLocation(); Destructor->setBody(new (Context) CompoundStmt(Loc)); - Destructor->markUsed(Context); + markDeclUsed(Destructor, Context); if (ASTMutationListener *L = getASTMutationListener()) { L->CompletedImplicitDefinition(Destructor); @@ -14652,7 +14652,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); @@ -15022,7 +15022,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); @@ -15166,7 +15166,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); CopyConstructor->setBody( ActOnCompoundStmt(Loc, Loc, None, /*isStmtExpr=*/false).getAs()); - CopyConstructor->markUsed(Context); + markDeclUsed(CopyConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15292,7 +15292,7 @@ Sema::CompoundScopeRAII CompoundScope(*this); MoveConstructor->setBody(ActOnCompoundStmt( Loc, Loc, None, /*isStmtExpr=*/ false).getAs()); - MoveConstructor->markUsed(Context); + markDeclUsed(MoveConstructor, Context); } if (ASTMutationListener *L = getASTMutationListener()) { @@ -15343,7 +15343,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())); @@ -15355,7 +15355,7 @@ Stmt *Return = BuildReturnStmt(Conv->getLocation(), FunctionRef).get(); Conv->setBody(CompoundStmt::Create(Context, Return, FPOptionsOverride(), Conv->getLocation(), Conv->getLocation())); - Conv->markUsed(Context); + markDeclUsed(Conv, Context); Conv->setReferenced(); if (ASTMutationListener *L = getASTMutationListener()) { @@ -15410,7 +15410,7 @@ Stmt *ReturnS = Return.get(); Conv->setBody(CompoundStmt::Create(Context, ReturnS, FPOptionsOverride(), 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" @@ -15828,7 +15829,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)); @@ -17913,7 +17914,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. @@ -18146,7 +18147,7 @@ } } - Func->markUsed(Context); + markDeclUsed(Func, Context); } } @@ -18217,7 +18218,7 @@ } } - Var->markUsed(SemaRef.Context); + SemaRef.markDeclUsed(Var, SemaRef.Context); } void Sema::MarkCaptureUsedInEnclosingContext(VarDecl *Capture, @@ -19373,7 +19374,7 @@ assert((!E || isa(E) || isa(E) || isa(E)) && "Invalid Expr argument to DoMarkVarDeclReferenced"); - Var->setReferenced(); + SemaRef.setDeclReferenced(Var); if (Var->isInvalidDecl()) return; @@ -19649,7 +19650,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. @@ -910,6 +914,16 @@ // no declarations (transitively), in which case it's ill-formed. diagExportedUnnamedDecl(*this, UnnamedDeclKind::Context, Child, BlockStart); + } else if (getLangOpts().CPlusPlusModules && GlobalModuleFragment) { + // An export must be in module purview, so mark any used GMF content. + if (auto *USD = dyn_cast(Child)) { + NamedDecl *Target = USD->getUnderlyingDecl(); + if (Target->hasOwningModule() && + Target->getOwningModule()->isGlobalModule()) { + // Mark the target of the using declaration as reachable. + markGMFDeclsReachableFrom(Target); + } + } } } } @@ -958,3 +972,180 @@ return M->isSubModuleOf(CurrentModuleUnit->getTopLevelModule()); } + +// A helper to process a used type to find GMF reachable decls which it uses. + +void Sema::findGMFReachableDeclsForType(const QualType &Q) { + // If we have a reference, mark the underlying type. + auto QR = Q.getNonReferenceType(); + const Type *T = QR.getTypePtr(); + assert(T && "missing type pointer?"); + // Only visit each Type once. + if (!GMFTypesVisited.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 *PT = dyn_cast(T)) { + 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 *TP = dyn_cast(T)) { + markGMFDeclsReachableFrom(TP->getDecl()); + } else if (auto *DN = dyn_cast(T)) { + return; + } else if (auto *ST = dyn_cast(T)) { + findGMFReachableDeclsForType(ST->getReplacementType()); + } else if (auto *ET = dyn_cast(T)) { + findGMFReachableDeclsForType(ET->getNamedType()); + } 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(QualType(NNS->getAsType(), 0)); + } 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()) + 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) { + + // Do not alter the ownership kind unless it is ModuleDiscardable. + if (!Orig->isModuleDiscardable()) + return; + + // Only visit each Decl once, when the state is changed. + assert(Orig->getOwningModule() && Orig->getOwningModule()->isGlobalModule() && + "should not have a discardable decl outside the GMF"); + Orig->setModuleOwnershipKind( + Decl::ModuleOwnershipKind::ReachableWhenImported); + + // Now process the decl to determine if any dependent decls should also be + // updated. + + 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()); + 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()); + if (!TS->isCompleteDefinition()) + return; + for (auto B : TS->bases()) + findGMFReachableDeclsForType(B.getType()); + 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()) + findGMFReachableDeclsForType(B.getType()); + return; + } + + if (auto *TTA = dyn_cast(Orig)) { + if (TTA->hasDefaultArgument()) + findGMFReachableDeclsForType(TTA->getDefaultArgument()); + return; + } + + if (auto *PD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(PD->getOriginalType()); + if (auto *E = PD->getDefaultArg()) + findGMFReachableDeclExprs(E); + return; + } + + if (auto *VD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(VD->getType()); + if (VD->hasInit()) + findGMFReachableDeclExprs(VD->getInit()); + return; + } + + if (auto *FD = dyn_cast(Orig)) { + findGMFReachableDeclsForType(FD->getType()); + if (auto *Init = FD->getInClassInitializer()) + findGMFReachableDeclExprs(Init); + return; + } + + if (auto *FD = dyn_cast(Orig)) { + if (auto *DD = FD->getFriendDecl()) + markGMFDeclsReachableFrom(DD); + return; + } + + if (auto *ED = dyn_cast(Orig)) { + if (auto *E = dyn_cast(ED->getDeclContext())) + markGMFDeclsReachableFrom(E); + 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 @@ -1519,7 +1519,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); @@ -3146,7 +3146,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 @@ -2759,7 +2759,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. @@ -3274,7 +3274,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; } @@ -5214,8 +5214,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; } @@ -628,9 +629,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. @@ -641,6 +643,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/CXX/class/class.friend/p7-cxx20.cpp b/clang/test/CXX/class/class.friend/p7-cxx20.cpp --- a/clang/test/CXX/class/class.friend/p7-cxx20.cpp +++ b/clang/test/CXX/class/class.friend/p7-cxx20.cpp @@ -48,10 +48,10 @@ class Z { friend void z(){}; }; -// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M. hidden class A definition -// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A -// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:15 in M. -// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:15 in M. hidden a 'void ()' implicit-inline +// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M. hidden class A{{( ModuleDiscardable)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A{{( ModuleDiscardable)?}} +// CHECK-MOD-NEXT: | `-FriendDecl {{.*}} col:15 in M.{{( ModuleDiscardable)?}} +// CHECK-MOD-NEXT: | `-FunctionDecl {{.*}} parent {{.*}} col:15 in M. hidden a 'void ()'{{( ModuleDiscardable)?}} implicit-inline // CHECK-MOD: `-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition // CHECK-MOD: |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} diff --git a/clang/test/CXX/class/class.mfct/p1-cxx20.cpp b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp --- a/clang/test/CXX/class/class.mfct/p1-cxx20.cpp +++ b/clang/test/CXX/class/class.mfct/p1-cxx20.cpp @@ -48,9 +48,9 @@ void z(){}; }; -// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M. hidden class A definition -// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A -// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:8 in M. hidden a 'void ()' implicit-inline +// CHECK-MOD: |-CXXRecordDecl {{.*}} <./header.h:2:1, line:4:1> line:2:7 in M. hidden class A{{( ModuleDiscardable)?}} definition +// CHECK-MOD: | |-CXXRecordDecl {{.*}} col:7 in M. hidden implicit class A{{( ModuleDiscardable)?}} +// CHECK-MOD-NEXT: | `-CXXMethodDecl {{.*}} col:8 in M. hidden a 'void ()'{{( ModuleDiscardable)?}} implicit-inline // CHECK-MOD: `-CXXRecordDecl {{.*}} line:6:7 in M hidden class Z{{( ReachableWhenImported)?}} definition // CHECK-MOD: |-CXXRecordDecl {{.*}} col:7 in M hidden implicit class Z{{( ReachableWhenImported)?}} diff --git a/clang/test/CXX/module/module.global.frag/cxx20-10-4-ex2.cpp b/clang/test/CXX/module/module.global.frag/cxx20-10-4-ex2.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.global.frag/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/CXX/module/module.global.frag/p3-p4.cpp b/clang/test/CXX/module/module.global.frag/p3-p4.cpp new file mode 100644 --- /dev/null +++ b/clang/test/CXX/module/module.global.frag/p3-p4.cpp @@ -0,0 +1,54 @@ +// RUN: rm -rf %t +// RUN: split-file %s %t +// RUN: cd %t + +// RUN: %clang_cc1 -std=c++20 M.cpp -emit-module-interface -o M.pcm +// RUN: %clang_cc1 -std=c++20 M.cpp -ast-dump | FileCheck --match-full-lines %s +// RUN: %clang_cc1 -std=c++20 M-impl.cpp -fmodule-file=M.pcm -fsyntax-only -verify + +//--- p3-p4.h +int f(); +int g(); +int h(); +int j(); +typedef int GMFInt; + +//--- M.cpp +module; +#include "p3-p4.h" + +export module M; + +export int use_f() { return f(); } + +export using ::h; + +namespace N { +export using ::j; +} + +GMFInt k(int); + +// CHECK: |-FunctionDecl {{.*}} <./p3-p4.h:1:1, col:7> col:5 in M. hidden used f 'int ()' ReachableWhenImported +// CHECK: |-FunctionDecl {{.*}} col:5 in M. hidden g 'int ()' ModuleDiscardable +// CHECK: |-FunctionDecl {{.*}} col:5 in M. hidden h 'int ()' ReachableWhenImported +// CHECK: |-FunctionDecl {{.*}} col:5 in M. hidden j 'int ()' ReachableWhenImported +// CHECK: |-TypedefDecl {{.*}} col:13 in M. hidden referenced GMFInt 'int' ReachableWhenImported + +// CHECK: |-ExportDecl {{.*}} col:1 in M +// CHECK-NEXT: | `-FunctionDecl {{.*}} col:12 in M hidden use_f 'int ()' VisibleWhenImported + +// CHECK: |-ExportDecl {{.*}} col:1 in M +// CHECK-NEXT: | |-UsingDecl {{.*}} col:16 in M hidden ::h VisibleWhenImported +// CHECK-NEXT: | `-UsingShadowDecl {{.*}} col:16 in M hidden implicit Function {{.*}} 'h' 'int ()' VisibleWhenImported + +// CHECK: |-NamespaceDecl {{.*}} line:10:11 in M hidden N +// CHECK-NEXT: | `-ExportDecl {{.*}} col:1 in M +// CHECK-NEXT: | |-UsingDecl {{.*}} col:16 in M hidden ::j VisibleWhenImported +// CHECK-NEXT: | `-UsingShadowDecl {{.*}} col:16 in M hidden implicit Function {{.*}} 'j' 'int ()' VisibleWhenImported + +//--- M-impl.cpp +import M; +int a = j(); // expected-error {{missing '#include'; 'j' must be declared before it is used}} + // expected-note@p3-p4.h:4 {{declaration here is not visible}} +int b = N::j(); // should be OK, UsingShadowDecl is 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