diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.cpp index f6fd321a613dc..8bc0f3fbb3c8d 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/unused-using-decls.cpp @@ -168,9 +168,7 @@ using n::K; using n::N; -// FIXME: Currently non-type template arguments are not supported. using n::Constant; -// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: using decl 'Constant' is unused using n::Q; diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp index 5f1bbee075b18..7d5f177c477bd 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/avoid-c-arrays.cpp @@ -211,11 +211,12 @@ T some_constant{}; // explicit instantiations template int some_constant[5]; -// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: do not declare C-style arrays, use 'std::array' instead [modernize-avoid-c-arrays] +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not declare C-style arrays, use 'std::array' instead [modernize-avoid-c-arrays] +// CHECK-MESSAGES: :[[@LINE-2]]:19: warning: do not declare C-style arrays, use 'std::array' instead [modernize-avoid-c-arrays] template int some_constant[4]; -// no diagnostic is expected here since explicit instantiations aren't represented as `TypeLoc` in the AST and we hence cannot match them as such +// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: do not declare C-style arrays, use 'std::array' instead [modernize-avoid-c-arrays] MyArrayType mk; // no diagnostic is expected here since no C-style array type got written here diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 3e2d287d1eb1f..db78d900c5ac8 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -191,6 +191,15 @@ Non-comprehensive list of changes in this release - Added #pragma loop licm(disable) for llvm.loop.licm.disable metadata +- Added a new ``ExplicitInstantiationDecl`` AST node to represent explicit + template instantiations (e.g., ``template void foo();`` or + ``extern template class S;``). Previously, source location information + for explicit instantiation statements was discarded after parsing. The new + node preserves the full source range including the ``extern`` and ``template`` + keywords, qualifiers, template arguments as written, and the declared type, + enabling tools such as language servers and refactoring engines to accurately + map source locations back to explicit instantiation sites. + New Compiler Flags ------------------ - New option ``-fms-anonymous-structs`` / ``-fno-ms-anonymous-structs`` added diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index ba1b58489c327..c22cfef09817a 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -104,6 +104,7 @@ class CXXRecordDecl; class DiagnosticsEngine; class DynTypedNodeList; class Expr; +class ExplicitInstantiationDecl; enum class FloatModeKind; class GlobalDecl; class IdentifierTable; @@ -656,6 +657,12 @@ class ASTContext : public RefCountedBase { llvm::DenseMap InstantiatedFromUnnamedFieldDecl; + /// Maps a canonical specialization Decl to all ExplicitInstantiationDecls + /// that reference it (declarations and definitions). + llvm::DenseMap> + ExplicitInstantiations; + /// Mapping that stores the methods overridden by a given C++ /// member function. /// @@ -1134,6 +1141,14 @@ class ASTContext : public RefCountedBase { /// Erase the attributes corresponding to the given declaration. void eraseDeclAttrs(const Decl *D); + /// Get all ExplicitInstantiationDecls for a given specialization. + ArrayRef + getExplicitInstantiationDecls(const NamedDecl *Spec) const; + + /// Add an ExplicitInstantiationDecl for a given specialization. + void addExplicitInstantiationDecl(const NamedDecl *Spec, + ExplicitInstantiationDecl *EID); + /// If this variable is an instantiated static data member of a /// class template specialization, returns the templated static data member /// from which it was instantiated. diff --git a/clang/include/clang/AST/ASTNodeTraverser.h b/clang/include/clang/AST/ASTNodeTraverser.h index 3be24ff868c2d..5e9463d54747d 100644 --- a/clang/include/clang/AST/ASTNodeTraverser.h +++ b/clang/include/clang/AST/ASTNodeTraverser.h @@ -683,6 +683,15 @@ class ASTNodeTraverser Visit(D->getMessage()); } + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D) { + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + Visit(TSI->getTypeLoc()); + for (unsigned I = 0, E = D->getNumTemplateArgs(); I != E; ++I) { + TemplateArgumentLoc Loc = D->getTemplateArg(I); + Visit(Loc.getArgument(), Loc.getSourceRange()); + } + } + void VisitFunctionTemplateDecl(const FunctionTemplateDecl *D) { dumpTemplateDecl(D); } diff --git a/clang/include/clang/AST/DeclTemplate.h b/clang/include/clang/AST/DeclTemplate.h index a4a1bb9c13c79..9fb41c87da732 100644 --- a/clang/include/clang/AST/DeclTemplate.h +++ b/clang/include/clang/AST/DeclTemplate.h @@ -3405,6 +3405,139 @@ getReplacedTemplateParameter(Decl *D, unsigned Index); /// If we have an implicit instantiation, adjust 'D' to refer to template. const Decl &adjustDeclToTemplate(const Decl &D); +/// Represents an explicit instantiation of a template entity in source code. +/// +/// \code +/// template void ns::foo(int); // function template +/// extern template struct ns::S; // class template (extern) +/// template int ns::bar; // variable template +/// template void ns::S::method(int); // member function +/// \endcode +class ExplicitInstantiationDecl final + : public Decl, + private llvm::TrailingObjects { + friend class ASTDeclReader; + friend class ASTDeclWriter; + friend TrailingObjects; + + /// The underlying specialization (low 3 bits: TSK). + llvm::PointerIntPair SpecAndTSK; + + /// TypeSourceInfo (low 2 bits: trailing-object flags). + /// Always non-null after construction. + /// - Class templates: TemplateSpecializationTypeLoc encoding keyword, + /// qualifier, template-name, and argument locations. + /// - Nested classes: TagTypeLoc encoding keyword, qualifier, and name. + /// - Function / variable templates: the declared type. + llvm::PointerIntPair TypeAndFlags; + + /// Location of the 'extern' keyword (invalid if not extern template). + SourceLocation ExternLoc; + + /// Location of the entity name (e.g., 'foo' in 'template void + /// ns::foo(int)'). + SourceLocation NameLoc; + + enum TrailingFlags : unsigned { + HasQualifierFlag = 1, + HasArgsAsWrittenFlag = 2, + }; + + size_t numTrailingObjects(OverloadToken) const { + return hasTrailingQualifier() ? 1 : 0; + } + + /// Raw access to the internal TypeSourceInfo. For class templates this is + /// a TemplateSpecializationTypeLoc; for nested classes a TagTypeLoc. + /// Public getTypeAsWritten() returns null for those cases. + TypeSourceInfo *getRawTypeSourceInfo() const { + return TypeAndFlags.getPointer(); + } + + /// Returns the trailing ASTTemplateArgumentListInfo pointer, or null. + const ASTTemplateArgumentListInfo *getTrailingArgsInfo() const { + if (!hasTrailingArgsAsWritten()) + return nullptr; + return *getTrailingObjects(); + } + + ExplicitInstantiationDecl( + DeclContext *DC, NamedDecl *Specialization, SourceLocation ExternLoc, + SourceLocation TemplateLoc, NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, SourceLocation NameLoc, + TypeSourceInfo *TypeAsWritten, TemplateSpecializationKind TSK); + + ExplicitInstantiationDecl(EmptyShell Empty) + : Decl(ExplicitInstantiation, Empty) {} + +public: + static ExplicitInstantiationDecl * + Create(ASTContext &C, DeclContext *DC, NamedDecl *Specialization, + SourceLocation ExternLoc, SourceLocation TemplateLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, + SourceLocation NameLoc, TypeSourceInfo *TypeAsWritten, + TemplateSpecializationKind TSK); + + static ExplicitInstantiationDecl * + CreateDeserialized(ASTContext &C, GlobalDeclID ID, unsigned TrailingFlags); + + NamedDecl *getSpecialization() const { return SpecAndTSK.getPointer(); } + + SourceRange getSourceRange() const override LLVM_READONLY; + SourceLocation getEndLoc() const LLVM_READONLY; + + SourceLocation getExternLoc() const { return ExternLoc; } + SourceLocation getTemplateLoc() const { return getLocation(); } + SourceLocation getNameLoc() const { return NameLoc; } + + /// For class templates / nested classes, the tag keyword location is + /// stored inside TypeSourceInfo; otherwise returns an invalid location. + SourceLocation getTagKWLoc() const; + + /// Whether the qualifier is stored as a trailing object (function / variable + /// templates) rather than inside TypeSourceInfo (class templates / nested + /// classes). + bool hasTrailingQualifier() const { + return TypeAndFlags.getInt() & HasQualifierFlag; + } + bool hasTrailingArgsAsWritten() const { + return TypeAndFlags.getInt() & HasArgsAsWrittenFlag; + } + + /// Returns the qualifier regardless of where it is stored. + /// For class templates / nested classes, it is extracted from TypeSourceInfo + /// (TemplateSpecializationTypeLoc or TagTypeLoc). + /// For function / variable templates, it comes from a trailing object. + NestedNameSpecifierLoc getQualifierLoc() const; + + /// Number of explicit template arguments, regardless of storage. + /// For class templates they come from TemplateSpecializationTypeLoc; + /// for function / variable templates from trailing + /// ASTTemplateArgumentListInfo. + unsigned getNumTemplateArgs() const; + TemplateArgumentLoc getTemplateArg(unsigned I) const; + SourceLocation getTemplateArgsLAngleLoc() const; + SourceLocation getTemplateArgsRAngleLoc() const; + + /// For function / variable templates, returns the declared type (return type + /// or variable type). For class templates and nested classes returns null — + /// the qualifier, tag keyword, and template arguments are accessible via + /// getQualifierLoc(), getTagKWLoc(), and getTemplateArg(). + TypeSourceInfo *getTypeAsWritten() const; + + TemplateSpecializationKind getTemplateSpecializationKind() const { + return static_cast(SpecAndTSK.getInt()); + } + + bool isExternTemplate() const { return ExternLoc.isValid(); } + + static bool classof(const Decl *D) { return classofKind(D->getKind()); } + static bool classofKind(Kind K) { return K == ExplicitInstantiation; } +}; + } // namespace clang #endif // LLVM_CLANG_AST_DECLTEMPLATE_H diff --git a/clang/include/clang/AST/JSONNodeDumper.h b/clang/include/clang/AST/JSONNodeDumper.h index 69dbdbbdb3ecd..4e8d1649bbf8b 100644 --- a/clang/include/clang/AST/JSONNodeDumper.h +++ b/clang/include/clang/AST/JSONNodeDumper.h @@ -268,6 +268,7 @@ class JSONNodeDumper void VisitLinkageSpecDecl(const LinkageSpecDecl *LSD); void VisitAccessSpecDecl(const AccessSpecDecl *ASD); void VisitFriendDecl(const FriendDecl *FD); + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D); void VisitObjCIvarDecl(const ObjCIvarDecl *D); void VisitObjCMethodDecl(const ObjCMethodDecl *D); diff --git a/clang/include/clang/AST/RecursiveASTVisitor.h b/clang/include/clang/AST/RecursiveASTVisitor.h index 1a14dd2c666b5..27f31efdb1f31 100644 --- a/clang/include/clang/AST/RecursiveASTVisitor.h +++ b/clang/include/clang/AST/RecursiveASTVisitor.h @@ -1748,6 +1748,17 @@ DEF_TRAVERSE_DECL(StaticAssertDecl, { TRY_TO(TraverseStmt(D->getMessage())); }) +DEF_TRAVERSE_DECL(ExplicitInstantiationDecl, { + // No double visiting: getTypeAsWritten() returns null for class + // templates/nested classes where the qualifier lives inside the TSI. + if (D->getQualifierLoc()) + TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); + if (TypeSourceInfo *TSI = D->getTypeAsWritten()) + TRY_TO(TraverseTypeLoc(TSI->getTypeLoc())); + for (unsigned I = 0, E = D->getNumTemplateArgs(); I != E; ++I) + TRY_TO(TraverseTemplateArgumentLoc(D->getTemplateArg(I))); +}) + DEF_TRAVERSE_DECL(TranslationUnitDecl, { // Code in an unnamed namespace shows up automatically in // decls_begin()/decls_end(). Thus we don't need to recurse on @@ -2027,8 +2038,10 @@ bool RecursiveASTVisitor::TraverseTemplateInstantiations( TRY_TO(TraverseDecl(RD)); break; - // FIXME: For now traverse explicit instantiations here. Change that - // once they are represented as dedicated nodes in the AST. + // Unlike class/variable template specializations, function template + // specializations are not independent children of the DeclContext — + // they are only reachable via FunctionTemplateDecl::specializations(). + // We must traverse them here so visitors can see the instantiated body. case TSK_ExplicitInstantiationDeclaration: case TSK_ExplicitInstantiationDefinition: TRY_TO(TraverseDecl(RD)); @@ -2196,14 +2209,16 @@ bool RecursiveASTVisitor::TraverseTemplateArgumentLocsHelper( the source code anywhere. (Note the instatiated *type* -- \ set -- is written, and will still get a callback of \ TemplateSpecializationType). For explicit instantiations \ - ("template set;"), we do need a callback, since this \ - is the only callback that's made for this instantiation. \ - We use getTemplateArgsAsWritten() to distinguish. */ \ + ("template set;"), the ExplicitInstantiationDecl node \ + handles traversal of template args and qualifier. \ + For explicit specializations ("template<> set {...};"), \ + we traverse template args here since there is no EID. */ \ if (const auto *ArgsWritten = D->getTemplateArgsAsWritten()) { \ assert(D->getTemplateSpecializationKind() != TSK_ImplicitInstantiation); \ - /* The args that remains unspecialized. */ \ - TRY_TO(TraverseTemplateArgumentLocsHelper( \ - ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs)); \ + if (D->getTemplateSpecializationKind() == TSK_ExplicitSpecialization) { \ + TRY_TO(TraverseTemplateArgumentLocsHelper( \ + ArgsWritten->getTemplateArgs(), ArgsWritten->NumTemplateArgs)); \ + } \ } \ \ if (getDerived().shouldVisitTemplateInstantiations() || \ @@ -2211,8 +2226,6 @@ bool RecursiveASTVisitor::TraverseTemplateArgumentLocsHelper( /* Traverse base definition for explicit specializations */ \ TRY_TO(Traverse##DECLKIND##Helper(D)); \ } else { \ - TRY_TO(TraverseNestedNameSpecifierLoc(D->getQualifierLoc())); \ - \ /* Returning from here skips traversing the \ declaration context of the *TemplateSpecializationDecl \ (embedded in the DEF_TRAVERSE_DECL() macro) \ diff --git a/clang/include/clang/AST/TextNodeDumper.h b/clang/include/clang/AST/TextNodeDumper.h index 32e83ebb5c8eb..6b7ac6fac8111 100644 --- a/clang/include/clang/AST/TextNodeDumper.h +++ b/clang/include/clang/AST/TextNodeDumper.h @@ -395,6 +395,7 @@ class TextNodeDumper void VisitLinkageSpecDecl(const LinkageSpecDecl *D); void VisitAccessSpecDecl(const AccessSpecDecl *D); void VisitFriendDecl(const FriendDecl *D); + void VisitExplicitInstantiationDecl(const ExplicitInstantiationDecl *D); void VisitObjCIvarDecl(const ObjCIvarDecl *D); void VisitObjCMethodDecl(const ObjCMethodDecl *D); void VisitObjCTypeParamDecl(const ObjCTypeParamDecl *D); diff --git a/clang/include/clang/AST/TypeBase.h b/clang/include/clang/AST/TypeBase.h index 8802b15d99034..b007b1ddb29c8 100644 --- a/clang/include/clang/AST/TypeBase.h +++ b/clang/include/clang/AST/TypeBase.h @@ -1134,6 +1134,11 @@ class QualType { /// Return true if this is a trivially copyable type bool isTriviallyCopyConstructibleType(const ASTContext &Context) const; + /// Returns true if the type uses postfix declarator syntax, i.e. the + /// declarator component appears after the name (arrays, functions). + /// Looks through pointer-like types to the pointee. + bool hasPostfixDeclaratorSyntax() const; + /// Returns true if it is a class and it might be dynamic. bool mayBeDynamicClass() const; diff --git a/clang/include/clang/Basic/DeclNodes.td b/clang/include/clang/Basic/DeclNodes.td index 04311055bb600..ffb58b43812dc 100644 --- a/clang/include/clang/Basic/DeclNodes.td +++ b/clang/include/clang/Basic/DeclNodes.td @@ -101,6 +101,7 @@ def AccessSpec : DeclNode; def Friend : DeclNode; def FriendTemplate : DeclNode; def StaticAssert : DeclNode; +def ExplicitInstantiation : DeclNode; def Block : DeclNode, DeclContext; def OutlinedFunction : DeclNode, DeclContext; def Captured : DeclNode, DeclContext; diff --git a/clang/include/clang/Serialization/ASTBitCodes.h b/clang/include/clang/Serialization/ASTBitCodes.h index 9b798ed484454..dcffe84df05f3 100644 --- a/clang/include/clang/Serialization/ASTBitCodes.h +++ b/clang/include/clang/Serialization/ASTBitCodes.h @@ -1543,7 +1543,10 @@ enum DeclCode { // An OpenACCRoutineDecl record. DECL_OPENACC_ROUTINE, - DECL_LAST = DECL_OPENACC_ROUTINE + /// An ExplicitInstantiationDecl record. + DECL_EXPLICIT_INSTANTIATION, + + DECL_LAST = DECL_EXPLICIT_INSTANTIATION }; /// Record codes for each kind of statement or expression. diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index ee7f823b014b2..efa1b9356772d 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -1496,6 +1496,21 @@ void ASTContext::eraseDeclAttrs(const Decl *D) { } } +ArrayRef +ASTContext::getExplicitInstantiationDecls(const NamedDecl *Spec) const { + auto It = + ExplicitInstantiations.find(cast(Spec->getCanonicalDecl())); + if (It != ExplicitInstantiations.end()) + return It->second; + return {}; +} + +void ASTContext::addExplicitInstantiationDecl(const NamedDecl *Spec, + ExplicitInstantiationDecl *EID) { + ExplicitInstantiations[cast(Spec->getCanonicalDecl())].push_back( + EID); +} + // FIXME: Remove ? MemberSpecializationInfo * ASTContext::getInstantiatedFromStaticDataMember(const VarDecl *Var) { diff --git a/clang/lib/AST/Decl.cpp b/clang/lib/AST/Decl.cpp index 87d5a1f3c524b..14b3af1d9e66c 100644 --- a/clang/lib/AST/Decl.cpp +++ b/clang/lib/AST/Decl.cpp @@ -2064,48 +2064,12 @@ SourceLocation DeclaratorDecl::getOuterLocStart() const { return getTemplateOrInnerLocStart(this); } -// Helper function: returns true if QT is or contains a type -// having a postfix component. -static bool typeIsPostfix(QualType QT) { - while (true) { - const Type* T = QT.getTypePtr(); - switch (T->getTypeClass()) { - default: - return false; - case Type::Pointer: - QT = cast(T)->getPointeeType(); - break; - case Type::BlockPointer: - QT = cast(T)->getPointeeType(); - break; - case Type::MemberPointer: - QT = cast(T)->getPointeeType(); - break; - case Type::LValueReference: - case Type::RValueReference: - QT = cast(T)->getPointeeType(); - break; - case Type::PackExpansion: - QT = cast(T)->getPattern(); - break; - case Type::Paren: - case Type::ConstantArray: - case Type::DependentSizedArray: - case Type::IncompleteArray: - case Type::VariableArray: - case Type::FunctionProto: - case Type::FunctionNoProto: - return true; - } - } -} - SourceRange DeclaratorDecl::getSourceRange() const { SourceLocation RangeEnd = getLocation(); if (TypeSourceInfo *TInfo = getTypeSourceInfo()) { // If the declaration has no name or the type extends past the name take the // end location of the type. - if (!getDeclName() || typeIsPostfix(TInfo->getType())) + if (!getDeclName() || TInfo->getType().hasPostfixDeclaratorSyntax()) RangeEnd = TInfo->getTypeLoc().getSourceRange().getEnd(); } return SourceRange(getOuterLocStart(), RangeEnd); @@ -5828,7 +5792,7 @@ TypeAliasDecl *TypeAliasDecl::CreateDeserialized(ASTContext &C, SourceRange TypedefDecl::getSourceRange() const { SourceLocation RangeEnd = getLocation(); if (TypeSourceInfo *TInfo = getTypeSourceInfo()) { - if (typeIsPostfix(TInfo->getType())) + if (TInfo->getType().hasPostfixDeclaratorSyntax()) RangeEnd = TInfo->getTypeLoc().getSourceRange().getEnd(); } return SourceRange(getBeginLoc(), RangeEnd); diff --git a/clang/lib/AST/DeclBase.cpp b/clang/lib/AST/DeclBase.cpp index 18b6a5b06bab8..2cbdff9da2e68 100644 --- a/clang/lib/AST/DeclBase.cpp +++ b/clang/lib/AST/DeclBase.cpp @@ -1019,6 +1019,7 @@ unsigned Decl::getIdentifierNamespaceForKind(Kind DeclKind) { case ImplicitConceptSpecialization: case OpenACCDeclare: case OpenACCRoutine: + case ExplicitInstantiation: // Never looked up by name. return 0; } diff --git a/clang/lib/AST/DeclPrinter.cpp b/clang/lib/AST/DeclPrinter.cpp index 5e377a6c0c247..d215202aa7deb 100644 --- a/clang/lib/AST/DeclPrinter.cpp +++ b/clang/lib/AST/DeclPrinter.cpp @@ -86,6 +86,7 @@ namespace { void VisitTemplateDecl(const TemplateDecl *D); void VisitFunctionTemplateDecl(FunctionTemplateDecl *D); void VisitClassTemplateDecl(ClassTemplateDecl *D); + void VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D); void VisitClassTemplateSpecializationDecl( ClassTemplateSpecializationDecl *D); void VisitClassTemplatePartialSpecializationDecl( @@ -1335,6 +1336,48 @@ void DeclPrinter::VisitClassTemplateDecl(ClassTemplateDecl *D) { } } +void DeclPrinter::VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D) { + if (D->isExternTemplate()) + Out << "extern "; + Out << "template "; + + NamedDecl *Spec = D->getSpecialization(); + + // Build the qualified name with template arguments. + std::string Name; + llvm::raw_string_ostream NameOS(Name); + if (D->getQualifierLoc()) + D->getQualifierLoc().getNestedNameSpecifier().print(NameOS, Policy); + Spec->printName(NameOS, Policy); + if (unsigned NumArgs = D->getNumTemplateArgs()) { + SmallVector Args; + for (unsigned I = 0; I < NumArgs; ++I) + Args.push_back(D->getTemplateArg(I)); + printTemplateArgumentList(NameOS, Args, Policy); + } + + if (auto *RD = dyn_cast(Spec)) { + Out << RD->getKindName() << " " << Name; + } else if (auto *FD = dyn_cast(Spec)) { + FD->getReturnType().print(Out, Policy); + Out << " " << Name << "("; + llvm::ListSeparator LS; + for (const ParmVarDecl *P : FD->parameters()) { + Out << LS; + P->print(Out, Policy); + } + if (FD->isVariadic()) { + Out << LS; + Out << "..."; + } + Out << ")"; + } else if (auto *TSI = D->getTypeAsWritten()) { + TSI->getType().print(Out, Policy, Name); + } else { + llvm_unreachable("unexpected specialization kind"); + } +} + void DeclPrinter::VisitClassTemplateSpecializationDecl( ClassTemplateSpecializationDecl *D) { Out << "template<> "; diff --git a/clang/lib/AST/DeclTemplate.cpp b/clang/lib/AST/DeclTemplate.cpp index 99d02fdc99e92..6567c8fa4d783 100644 --- a/clang/lib/AST/DeclTemplate.cpp +++ b/clang/lib/AST/DeclTemplate.cpp @@ -1784,3 +1784,132 @@ const Decl &clang::adjustDeclToTemplate(const Decl &D) { // FIXME: Adjust alias templates? return D; } + +ExplicitInstantiationDecl::ExplicitInstantiationDecl( + DeclContext *DC, NamedDecl *Specialization, SourceLocation ExternLoc, + SourceLocation TemplateLoc, NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, SourceLocation NameLoc, + TypeSourceInfo *TypeAsWritten, TemplateSpecializationKind TSK) + : Decl(ExplicitInstantiation, DC, TemplateLoc), + SpecAndTSK(Specialization, TSK), ExternLoc(ExternLoc), NameLoc(NameLoc) { + unsigned Flags = 0; + if (QualifierLoc) + Flags |= HasQualifierFlag; + if (ArgsAsWritten) + Flags |= HasArgsAsWrittenFlag; + // Set flags BEFORE writing trailing objects, because + // numTrailingObjects reads TypeAndFlags.getInt() to compute offsets. + TypeAndFlags.setPointerAndInt(TypeAsWritten, Flags); + if (QualifierLoc) + *getTrailingObjects() = QualifierLoc; + if (ArgsAsWritten) + *getTrailingObjects() = ArgsAsWritten; +} + +ExplicitInstantiationDecl *ExplicitInstantiationDecl::Create( + ASTContext &C, DeclContext *DC, NamedDecl *Specialization, + SourceLocation ExternLoc, SourceLocation TemplateLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, SourceLocation NameLoc, + TypeSourceInfo *TypeAsWritten, TemplateSpecializationKind TSK) { + unsigned Extra = additionalSizeToAlloc( + QualifierLoc ? 1 : 0, ArgsAsWritten ? 1 : 0); + return new (C, DC, Extra) ExplicitInstantiationDecl( + DC, Specialization, ExternLoc, TemplateLoc, QualifierLoc, ArgsAsWritten, + NameLoc, TypeAsWritten, TSK); +} + +ExplicitInstantiationDecl * +ExplicitInstantiationDecl::CreateDeserialized(ASTContext &C, GlobalDeclID ID, + unsigned TrailingFlags) { + unsigned Extra = additionalSizeToAlloc( + (TrailingFlags & HasQualifierFlag) ? 1 : 0, + (TrailingFlags & HasArgsAsWrittenFlag) ? 1 : 0); + auto *D = new (C, ID, Extra) ExplicitInstantiationDecl(EmptyShell()); + // Set the flags so the reader knows which trailing objects are present. + D->TypeAndFlags.setInt(TrailingFlags); + return D; +} + +SourceLocation ExplicitInstantiationDecl::getTagKWLoc() const { + if (auto *TSI = getRawTypeSourceInfo()) { + if (auto TL = TSI->getTypeLoc().getAs()) + return TL.getElaboratedKeywordLoc(); + if (auto TL = TSI->getTypeLoc().getAs()) + return TL.getElaboratedKeywordLoc(); + } + return SourceLocation(); +} + +NestedNameSpecifierLoc ExplicitInstantiationDecl::getQualifierLoc() const { + if (hasTrailingQualifier()) + return *getTrailingObjects(); + if (auto *TSI = getRawTypeSourceInfo()) + return TSI->getTypeLoc().getPrefix(); + return NestedNameSpecifierLoc(); +} + +TypeSourceInfo *ExplicitInstantiationDecl::getTypeAsWritten() const { + auto *TSI = getRawTypeSourceInfo(); + if (!TSI) + return nullptr; + TypeLoc TL = TSI->getTypeLoc(); + // For class templates and nested classes, the "type" is fully described by + // the unified accessors (getQualifierLoc, getTemplateArg, getTagKWLoc). + if (TL.getAs() || TL.getAs()) + return nullptr; + return TSI; +} + +unsigned ExplicitInstantiationDecl::getNumTemplateArgs() const { + if (const auto *Args = getTrailingArgsInfo()) + return Args->NumTemplateArgs; + if (auto *TSI = getRawTypeSourceInfo()) + if (auto TL = TSI->getTypeLoc().getAs()) + return TL.getNumArgs(); + return 0; +} + +TemplateArgumentLoc +ExplicitInstantiationDecl::getTemplateArg(unsigned I) const { + if (const auto *Args = getTrailingArgsInfo()) + return (*Args)[I]; + auto *TSI = getRawTypeSourceInfo(); + return TSI->getTypeLoc().castAs().getArgLoc(I); +} + +SourceLocation ExplicitInstantiationDecl::getTemplateArgsLAngleLoc() const { + if (const auto *Args = getTrailingArgsInfo()) + return Args->getLAngleLoc(); + if (auto *TSI = getRawTypeSourceInfo()) + if (auto TL = TSI->getTypeLoc().getAs()) + return TL.getLAngleLoc(); + return SourceLocation(); +} + +SourceLocation ExplicitInstantiationDecl::getTemplateArgsRAngleLoc() const { + if (const auto *Args = getTrailingArgsInfo()) + return Args->getRAngleLoc(); + if (auto *TSI = getRawTypeSourceInfo()) + if (auto TL = TSI->getTypeLoc().getAs()) + return TL.getRAngleLoc(); + return SourceLocation(); +} + +SourceLocation ExplicitInstantiationDecl::getEndLoc() const { + // For func/var templates with postfix type syntax (arrays, functions), + // the type extends past the name, so use the type's end location. + if (auto *TSI = getTypeAsWritten()) + if (TSI->getType().hasPostfixDeclaratorSyntax()) + return TSI->getTypeLoc().getEndLoc(); + // Otherwise, template args RAngleLoc or NameLoc. + SourceLocation RAngle = getTemplateArgsRAngleLoc(); + return RAngle.isValid() ? RAngle : NameLoc; +} + +SourceRange ExplicitInstantiationDecl::getSourceRange() const { + SourceLocation Begin = ExternLoc.isValid() ? ExternLoc : getLocation(); + return SourceRange(Begin, getEndLoc()); +} diff --git a/clang/lib/AST/JSONNodeDumper.cpp b/clang/lib/AST/JSONNodeDumper.cpp index 3138f95e6a83b..8373dd8e373e0 100644 --- a/clang/lib/AST/JSONNodeDumper.cpp +++ b/clang/lib/AST/JSONNodeDumper.cpp @@ -1119,6 +1119,32 @@ void JSONNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *ASD) { JOS.attribute("access", createAccessSpecifier(ASD->getAccess())); } +void JSONNodeDumper::VisitExplicitInstantiationDecl( + const ExplicitInstantiationDecl *D) { + attributeOnlyIfTrue("isExternTemplate", D->isExternTemplate()); + if (D->getSpecialization()) + JOS.attribute("specializationDeclId", + createPointerRepresentation(D->getSpecialization())); + switch (D->getTemplateSpecializationKind()) { + case TSK_Undeclared: + break; + case TSK_ImplicitInstantiation: + JOS.attribute("templateSpecializationKind", "implicit_instantiation"); + break; + case TSK_ExplicitSpecialization: + JOS.attribute("templateSpecializationKind", "explicit_specialization"); + break; + case TSK_ExplicitInstantiationDeclaration: + JOS.attribute("templateSpecializationKind", + "explicit_instantiation_declaration"); + break; + case TSK_ExplicitInstantiationDefinition: + JOS.attribute("templateSpecializationKind", + "explicit_instantiation_definition"); + break; + } +} + void JSONNodeDumper::VisitFriendDecl(const FriendDecl *FD) { if (const TypeSourceInfo *T = FD->getFriendType()) JOS.attribute("type", createQualType(T->getType())); diff --git a/clang/lib/AST/TextNodeDumper.cpp b/clang/lib/AST/TextNodeDumper.cpp index 250ec8b666e05..53982025dfd4d 100644 --- a/clang/lib/AST/TextNodeDumper.cpp +++ b/clang/lib/AST/TextNodeDumper.cpp @@ -2936,6 +2936,19 @@ void TextNodeDumper::VisitAccessSpecDecl(const AccessSpecDecl *D) { dumpAccessSpecifier(D->getAccess()); } +void TextNodeDumper::VisitExplicitInstantiationDecl( + const ExplicitInstantiationDecl *D) { + dumpTemplateSpecializationKind(D->getTemplateSpecializationKind()); + if (D->isExternTemplate()) + OS << " extern"; + if (D->getQualifierLoc()) + dumpNestedNameSpecifier(D->getQualifierLoc().getNestedNameSpecifier()); + if (const NamedDecl *Spec = D->getSpecialization()) { + OS << " '" << Spec->getDeclName() << "'"; + dumpDeclRef(Spec); + } +} + void TextNodeDumper::VisitFriendDecl(const FriendDecl *D) { if (TypeSourceInfo *T = D->getFriendType()) dumpType(T->getType()); diff --git a/clang/lib/AST/Type.cpp b/clang/lib/AST/Type.cpp index 78983fd38410d..a819a5f86a8b7 100644 --- a/clang/lib/AST/Type.cpp +++ b/clang/lib/AST/Type.cpp @@ -129,6 +129,41 @@ const IdentifierInfo *QualType::getBaseTypeIdentifier() const { return nullptr; } +bool QualType::hasPostfixDeclaratorSyntax() const { + QualType QT = *this; + while (true) { + const Type *T = QT.getTypePtr(); + switch (T->getTypeClass()) { + default: + return false; + case Type::Pointer: + QT = cast(T)->getPointeeType(); + break; + case Type::BlockPointer: + QT = cast(T)->getPointeeType(); + break; + case Type::MemberPointer: + QT = cast(T)->getPointeeType(); + break; + case Type::LValueReference: + case Type::RValueReference: + QT = cast(T)->getPointeeType(); + break; + case Type::PackExpansion: + QT = cast(T)->getPattern(); + break; + case Type::Paren: + case Type::ConstantArray: + case Type::DependentSizedArray: + case Type::IncompleteArray: + case Type::VariableArray: + case Type::FunctionProto: + case Type::FunctionNoProto: + return true; + } + } +} + bool QualType::mayBeDynamicClass() const { const auto *ClassDecl = getTypePtr()->getPointeeCXXRecordDecl(); return ClassDecl && ClassDecl->mayBeDynamicClass(); diff --git a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp index bfb962aaedbd9..58157b7f24aee 100644 --- a/clang/lib/CIR/CodeGen/CIRGenDecl.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenDecl.cpp @@ -831,6 +831,7 @@ void CIRGenFunction::emitDecl(const Decl &d, bool evaluateConditionDecl) { case Decl::Function: // void X(); case Decl::EnumConstant: // enum ? { X = ? } + case Decl::ExplicitInstantiation: case Decl::StaticAssert: // static_assert(X, ""); [C++0x] case Decl::Label: // __label__ x; case Decl::Import: diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.cpp b/clang/lib/CIR/CodeGen/CIRGenModule.cpp index 2f64fe45a694d..02578b534c239 100644 --- a/clang/lib/CIR/CodeGen/CIRGenModule.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenModule.cpp @@ -2111,6 +2111,7 @@ void CIRGenModule::emitTopLevelDecl(Decl *decl) { case Decl::Concept: case Decl::CXXDeductionGuide: case Decl::Empty: + case Decl::ExplicitInstantiation: case Decl::FunctionTemplate: case Decl::StaticAssert: case Decl::TypeAliasTemplate: diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index 748362105cb02..419b3c477e7b2 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -125,6 +125,7 @@ void CodeGenFunction::EmitDecl(const Decl &D, bool EvaluateConditionDecl) { case Decl::Function: // void X(); case Decl::EnumConstant: // enum ? { X = ? } case Decl::StaticAssert: // static_assert(X, ""); [C++0x] + case Decl::ExplicitInstantiation: case Decl::Label: // __label__ x; case Decl::Import: case Decl::MSGuid: // __declspec(uuid("...")) diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index bb399a71aa047..698230cb1c122 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -7777,6 +7777,7 @@ void CodeGenModule::EmitTopLevelDecl(Decl *D) { break; case Decl::StaticAssert: + case Decl::ExplicitInstantiation: // Nothing to do. break; diff --git a/clang/lib/Sema/SemaModule.cpp b/clang/lib/Sema/SemaModule.cpp index 7618c795d9d4c..67f46b64cf047 100644 --- a/clang/lib/Sema/SemaModule.cpp +++ b/clang/lib/Sema/SemaModule.cpp @@ -935,7 +935,7 @@ static bool checkExportedDecl(Sema &S, Decl *D, SourceLocation BlockStart) { // HLSL: export declaration is valid only on functions if (S.getLangOpts().HLSL) { // Export-within-export was already diagnosed in ActOnStartExportDecl - if (!isa(D)) { + if (!isa(D)) { S.Diag(D->getBeginLoc(), diag::err_hlsl_export_not_on_function); D->setInvalidDecl(); return false; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index c436b7018a2bd..dcdf6a6ff3d5c 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -9314,10 +9314,38 @@ static void StripImplicitInstantiation(NamedDecl *D, bool MinGW) { FD->setInlineSpecified(false); } +/// Create an ExplicitInstantiationDecl to record source-location info for an +/// explicit template instantiation statement, and add it to \p CurContext. +/// +/// For class templates / nested classes, the caller should build a +/// TypeSourceInfo that encodes the tag keyword, qualifier, name, and template +/// arguments, and pass empty QualifierLoc / null ArgsAsWritten. +/// +/// For function / variable templates, the caller should pass TypeAsWritten for +/// the declared type, and separate QualifierLoc / ArgsAsWritten. +static void addExplicitInstantiationDecl( + ASTContext &Context, DeclContext *CurContext, NamedDecl *Spec, + SourceLocation ExternLoc, SourceLocation TemplateLoc, + NestedNameSpecifierLoc QualifierLoc, + const ASTTemplateArgumentListInfo *ArgsAsWritten, SourceLocation NameLoc, + TypeSourceInfo *TypeAsWritten, TemplateSpecializationKind TSK) { + auto *EID = ExplicitInstantiationDecl::Create( + Context, CurContext, Spec, ExternLoc, TemplateLoc, QualifierLoc, + ArgsAsWritten, NameLoc, TypeAsWritten, TSK); + Context.addExplicitInstantiationDecl(Spec, EID); + CurContext->addDecl(EID); +} + /// Compute the diagnostic location for an explicit instantiation // declaration or definition. -static SourceLocation DiagLocForExplicitInstantiation( - NamedDecl* D, SourceLocation PointOfInstantiation) { +static SourceLocation +DiagLocForExplicitInstantiation(NamedDecl *D, + SourceLocation PointOfInstantiation) { + for (auto *EID : D->getASTContext().getExplicitInstantiationDecls(D)) + if (EID->getTemplateSpecializationKind() == + TSK_ExplicitInstantiationDefinition) + return EID->getTemplateLoc(); + // Explicit instantiations following a specialization have no effect and // hence no PointOfInstantiation. In that case, walk decl backwards // until a valid name loc is found. @@ -10406,6 +10434,15 @@ DeclResult Sema::ActOnExplicitInstantiation( if (HasNoEffect) { // Set the template specialization kind. Specialization->setTemplateSpecializationKind(TSK); + + ElaboratedTypeKeyword KW = TypeWithKeyword::getKeywordForTagTypeKind(Kind); + TypeSourceInfo *TSI = Context.getTemplateSpecializationTypeInfo( + KW, KWLoc, SS.getWithLocInContext(Context), SourceLocation(), Name, + TemplateNameLoc, TemplateArgs, CTAI.CanonicalConverted, + Context.getCanonicalTagType(Specialization)); + addExplicitInstantiationDecl(Context, CurContext, Specialization, ExternLoc, + TemplateLoc, NestedNameSpecifierLoc(), nullptr, + TemplateNameLoc, TSI, TSK); return Specialization; } @@ -10495,6 +10532,14 @@ DeclResult Sema::ActOnExplicitInstantiation( Specialization->setTemplateSpecializationKind(TSK); } + ElaboratedTypeKeyword KW = TypeWithKeyword::getKeywordForTagTypeKind(Kind); + TypeSourceInfo *TSI = Context.getTemplateSpecializationTypeInfo( + KW, KWLoc, SS.getWithLocInContext(Context), SourceLocation(), Name, + TemplateNameLoc, TemplateArgs, CTAI.CanonicalConverted, + Context.getCanonicalTagType(Specialization)); + addExplicitInstantiationDecl(Context, CurContext, Specialization, ExternLoc, + TemplateLoc, NestedNameSpecifierLoc(), nullptr, + TemplateNameLoc, TSI, TSK); return Specialization; } @@ -10569,8 +10614,21 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, MSInfo->getPointOfInstantiation(), HasNoEffect)) return true; - if (HasNoEffect) + if (HasNoEffect) { + TagTypeKind TagKind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec); + ElaboratedTypeKeyword KW = + TypeWithKeyword::getKeywordForTagTypeKind(TagKind); + QualType TagTy = Context.getTagType(KW, SS.getScopeRep(), Record, false); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(TagTy); + auto TL = TSI->getTypeLoc().castAs(); + TL.setElaboratedKeywordLoc(KWLoc); + TL.setQualifierLoc(SS.getWithLocInContext(Context)); + TL.setNameLoc(NameLoc); + addExplicitInstantiationDecl(Context, CurContext, Record, ExternLoc, + TemplateLoc, NestedNameSpecifierLoc(), + nullptr, NameLoc, TSI, TSK); return TagD; + } } CXXRecordDecl *RecordDef @@ -10606,10 +10664,17 @@ Sema::ActOnExplicitInstantiation(Scope *S, SourceLocation ExternLoc, if (TSK == TSK_ExplicitInstantiationDefinition) MarkVTableUsed(NameLoc, RecordDef, true); - // FIXME: We don't have any representation for explicit instantiations of - // member classes. Such a representation is not needed for compilation, but it - // should be available for clients that want to see all of the declarations in - // the source code. + TagTypeKind TagKind = TypeWithKeyword::getTagTypeKindForTypeSpec(TagSpec); + ElaboratedTypeKeyword KW = TypeWithKeyword::getKeywordForTagTypeKind(TagKind); + QualType TagTy = Context.getTagType(KW, SS.getScopeRep(), Record, false); + TypeSourceInfo *TSI = Context.CreateTypeSourceInfo(TagTy); + auto TL = TSI->getTypeLoc().castAs(); + TL.setElaboratedKeywordLoc(KWLoc); + TL.setQualifierLoc(SS.getWithLocInContext(Context)); + TL.setNameLoc(NameLoc); + addExplicitInstantiationDecl(Context, CurContext, Record, ExternLoc, + TemplateLoc, NestedNameSpecifierLoc(), nullptr, + NameLoc, TSI, TSK); return TagD; } @@ -10803,7 +10868,7 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, if (!HasNoEffect) { // Instantiate static data member or variable template. Prev->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); - if (auto *VTSD = dyn_cast(Prev)) { + if (auto *VTSD = dyn_cast(Prev)) { VTSD->setExternKeywordLoc(ExternLoc); VTSD->setTemplateKeywordLoc(TemplateLoc); } @@ -10827,8 +10892,14 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, return true; } - // FIXME: Create an ExplicitInstantiation node? - return (Decl*) nullptr; + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (auto *VTSD = dyn_cast(Prev)) + ArgsAsWritten = VTSD->getTemplateArgsAsWritten(); + addExplicitInstantiationDecl( + Context, CurContext, Prev, ExternLoc, TemplateLoc, + D.getCXXScopeSpec().getWithLocInContext(Context), ArgsAsWritten, + D.getIdentifierLoc(), T, TSK); + return (Decl *)nullptr; } // If the declarator is a template-id, translate the parser's template @@ -11005,10 +11076,17 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, HasNoEffect)) return true; - // FIXME: We may still want to build some representation of this - // explicit specialization. - if (HasNoEffect) - return (Decl*) nullptr; + if (HasNoEffect) { + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (HasExplicitTemplateArgs) + ArgsAsWritten = + ASTTemplateArgumentListInfo::Create(Context, TemplateArgs); + addExplicitInstantiationDecl( + Context, CurContext, Specialization, ExternLoc, TemplateLoc, + D.getCXXScopeSpec().getWithLocInContext(Context), ArgsAsWritten, + D.getIdentifierLoc(), T, TSK); + return (Decl *)nullptr; + } } // HACK: libc++ has a bug where it attempts to explicitly instantiate the @@ -11036,7 +11114,6 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, TSK = TSK_ExplicitInstantiationDeclaration; Specialization->setTemplateSpecializationKind(TSK, D.getIdentifierLoc()); - if (Specialization->isDefined()) { // Let the ASTConsumer know that this function has been explicitly // instantiated now, and its linkage might have changed. @@ -11065,8 +11142,14 @@ DeclResult Sema::ActOnExplicitInstantiation(Scope *S, : Specialization->getInstantiatedFromMemberFunction(), D.getIdentifierLoc(), D.getCXXScopeSpec().isSet(), TSK); - // FIXME: Create some kind of ExplicitInstantiationDecl here. - return (Decl*) nullptr; + const ASTTemplateArgumentListInfo *ArgsAsWritten = nullptr; + if (HasExplicitTemplateArgs) + ArgsAsWritten = ASTTemplateArgumentListInfo::Create(Context, TemplateArgs); + addExplicitInstantiationDecl(Context, CurContext, Specialization, ExternLoc, + TemplateLoc, + D.getCXXScopeSpec().getWithLocInContext(Context), + ArgsAsWritten, D.getIdentifierLoc(), T, TSK); + return (Decl *)nullptr; } TypeResult Sema::ActOnDependentTag(Scope *S, unsigned TagSpec, TagUseKind TUK, diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp index 09c2482168ab7..f44ce02196c5c 100644 --- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp @@ -2101,6 +2101,13 @@ Decl *TemplateDeclInstantiator::VisitStaticAssertDecl(StaticAssertDecl *D) { InstantiatedMessageExpr.get(), D->getRParenLoc(), D->isFailed()); } +Decl *TemplateDeclInstantiator::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + // ExplicitInstantiationDecl is a source-info-only node and should not + // appear inside a template pattern. Nothing to instantiate. + llvm_unreachable("ExplicitInstantiationDecl should not be instantiated"); +} + Decl *TemplateDeclInstantiator::VisitEnumDecl(EnumDecl *D) { EnumDecl *PrevDecl = nullptr; if (EnumDecl *PatternPrev = getPreviousDeclForInstantiation(D)) { diff --git a/clang/lib/Serialization/ASTCommon.cpp b/clang/lib/Serialization/ASTCommon.cpp index 69db02f2efc40..49e6fe8004cec 100644 --- a/clang/lib/Serialization/ASTCommon.cpp +++ b/clang/lib/Serialization/ASTCommon.cpp @@ -400,6 +400,7 @@ bool serialization::isRedeclarableDeclKind(unsigned Kind) { return true; // Never redeclarable. + case Decl::ExplicitInstantiation: case Decl::UsingDirective: case Decl::Label: case Decl::UnresolvedUsingTypename: diff --git a/clang/lib/Serialization/ASTReaderDecl.cpp b/clang/lib/Serialization/ASTReaderDecl.cpp index 9033ea55bc5e2..e3d80828c808f 100644 --- a/clang/lib/Serialization/ASTReaderDecl.cpp +++ b/clang/lib/Serialization/ASTReaderDecl.cpp @@ -405,6 +405,7 @@ class ASTDeclReader : public DeclVisitor { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D); void VisitBlockDecl(BlockDecl *BD); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *CD); @@ -2785,6 +2786,32 @@ void ASTDeclReader::VisitStaticAssertDecl(StaticAssertDecl *D) { D->RParenLoc = readSourceLocation(); } +void ASTDeclReader::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + // Note: trailing flags were already read by ReadDeclRecord and passed to + // CreateDeserialized, so TypeAndFlags.getInt() is already set. + VisitDecl(D); + auto *Spec = readDeclAs(); + D->SpecAndTSK.setPointer(Spec); + D->ExternLoc = readSourceLocation(); + D->NameLoc = readSourceLocation(); + TypeSourceInfo *TSI = readTypeSourceInfo(); + unsigned TSK = Record.readInt(); + D->SpecAndTSK.setInt(TSK); + D->TypeAndFlags.setPointer(TSI); // preserves trailing flags in int bits + // Read trailing objects. + if (D->hasTrailingQualifier()) + *D->getTrailingObjects() = + Record.readNestedNameSpecifierLoc(); + if (D->hasTrailingArgsAsWritten()) + *D->getTrailingObjects() = + Record.readASTTemplateArgumentListInfo(); + + // Rebuild the ASTContext map from specialization to EID. + if (Spec) + Reader.getContext().addExplicitInstantiationDecl(Spec, D); +} + void ASTDeclReader::VisitEmptyDecl(EmptyDecl *D) { VisitDecl(D); } @@ -4107,6 +4134,10 @@ Decl *ASTReader::ReadDeclRecord(GlobalDeclID ID) { case DECL_STATIC_ASSERT: D = StaticAssertDecl::CreateDeserialized(Context, ID); break; + case DECL_EXPLICIT_INSTANTIATION: + D = ExplicitInstantiationDecl::CreateDeserialized(Context, ID, + Record.readInt()); + break; case DECL_OBJC_METHOD: D = ObjCMethodDecl::CreateDeserialized(Context, ID); break; diff --git a/clang/lib/Serialization/ASTWriterDecl.cpp b/clang/lib/Serialization/ASTWriterDecl.cpp index e415ac1e47862..62956e4db08d2 100644 --- a/clang/lib/Serialization/ASTWriterDecl.cpp +++ b/clang/lib/Serialization/ASTWriterDecl.cpp @@ -144,6 +144,7 @@ namespace clang { void VisitFriendDecl(FriendDecl *D); void VisitFriendTemplateDecl(FriendTemplateDecl *D); void VisitStaticAssertDecl(StaticAssertDecl *D); + void VisitExplicitInstantiationDecl(ExplicitInstantiationDecl *D); void VisitBlockDecl(BlockDecl *D); void VisitOutlinedFunctionDecl(OutlinedFunctionDecl *D); void VisitCapturedDecl(CapturedDecl *D); @@ -918,6 +919,10 @@ void ASTDeclWriter::VisitFunctionDecl(FunctionDecl *D) { } } + // Ensure associated ExplicitInstantiationDecls survive reduced BMI. + for (auto *EID : Record.getASTContext().getExplicitInstantiationDecls(D)) + Writer.GetDeclRef(EID); + Record.push_back(D->param_size()); for (auto *P : D->parameters()) Record.AddDeclRef(P); @@ -1966,6 +1971,10 @@ void ASTDeclWriter::VisitClassTemplateSpecializationDecl( Writer.GetDeclRef(DG->getCanonicalDecl()); } + // Ensure associated ExplicitInstantiationDecls survive reduced BMI. + for (auto *EID : Record.getASTContext().getExplicitInstantiationDecls(D)) + Writer.GetDeclRef(EID); + Code = serialization::DECL_CLASS_TEMPLATE_SPECIALIZATION; } @@ -2035,6 +2044,10 @@ void ASTDeclWriter::VisitVarTemplateSpecializationDecl( Record.AddDeclRef(D->getSpecializedTemplate()->getCanonicalDecl()); } + // Ensure associated ExplicitInstantiationDecls survive reduced BMI. + for (auto *EID : Record.getASTContext().getExplicitInstantiationDecls(D)) + Writer.GetDeclRef(EID); + Code = serialization::DECL_VAR_TEMPLATE_SPECIALIZATION; } @@ -2176,6 +2189,31 @@ void ASTDeclWriter::VisitStaticAssertDecl(StaticAssertDecl *D) { Code = serialization::DECL_STATIC_ASSERT; } +void ASTDeclWriter::VisitExplicitInstantiationDecl( + ExplicitInstantiationDecl *D) { + // Trailing-object flags must be the first thing written so the reader can + // allocate the right amount of trailing storage in CreateDeserialized. + unsigned Flags = 0; + if (D->hasTrailingQualifier()) + Flags |= ExplicitInstantiationDecl::HasQualifierFlag; + if (D->hasTrailingArgsAsWritten()) + Flags |= ExplicitInstantiationDecl::HasArgsAsWrittenFlag; + Record.push_back(Flags); + + VisitDecl(D); + Record.AddDeclRef(D->getSpecialization()); + Record.AddSourceLocation(D->getExternLoc()); + Record.AddSourceLocation(D->getNameLoc()); + Record.AddTypeSourceInfo(D->getRawTypeSourceInfo()); + Record.push_back(D->getTemplateSpecializationKind()); + // Trailing objects. + if (D->hasTrailingQualifier()) + Record.AddNestedNameSpecifierLoc(D->getQualifierLoc()); + if (const auto *Args = D->getTrailingArgsInfo()) + Record.AddASTTemplateArgumentListInfo(Args); + Code = serialization::DECL_EXPLICIT_INSTANTIATION; +} + /// Emit the DeclContext part of a declaration context decl. void ASTDeclWriter::VisitDeclContext(DeclContext *DC) { static_assert(DeclContext::NumDeclContextBits == 13, diff --git a/clang/lib/Tooling/Syntax/BuildTree.cpp b/clang/lib/Tooling/Syntax/BuildTree.cpp index 9d49d72dea69b..4fd5c009353fa 100644 --- a/clang/lib/Tooling/Syntax/BuildTree.cpp +++ b/clang/lib/Tooling/Syntax/BuildTree.cpp @@ -737,6 +737,14 @@ class BuildTreeVisitor : public RecursiveASTVisitor { return true; } + // ExplicitInstantiationDecl is an auxiliary AST node that records source + // info. The syntax tree is already built by + // TraverseClassTemplateSpecializationDecl or by the parser for + // function/variable templates, so skip this node. + bool TraverseExplicitInstantiationDecl(ExplicitInstantiationDecl *) { + return true; + } + bool WalkUpFromTemplateDecl(TemplateDecl *S) { foldTemplateDeclaration( Builder.getDeclarationRange(S), diff --git a/clang/test/AST/ast-dump-templates-pattern.cpp b/clang/test/AST/ast-dump-templates-pattern.cpp index 624a9b1c7a94c..aaf8007c858f1 100644 --- a/clang/test/AST/ast-dump-templates-pattern.cpp +++ b/clang/test/AST/ast-dump-templates-pattern.cpp @@ -18,7 +18,11 @@ namespace TestClassRedecl { // CHECK: |-ClassTemplateDecl {{.+}} struct S { T x; }; +template void foo(T) {} +template T var = T(); +} + +// CHECK: template struct ns::S; +template struct ns::S; +// CHECK: extern template struct ns::S; +extern template struct ns::S; + +// CHECK: template void ns::foo(int); +template void ns::foo(int); +// CHECK: extern template void ns::foo(float); +extern template void ns::foo(float); + +template void bar(T) {} +// CHECK: template void bar(int); +template void bar(int); + +// CHECK: template int ns::var; +template int ns::var; +// CHECK: extern template float ns::var; +extern template float ns::var; + +template struct X { struct Inner {}; }; +// CHECK: template struct X::Inner; +template struct X::Inner; + +template struct Outer { + void method(); + template void f(U); + template static U var; + template struct Inner {}; +}; +template void Outer::method() {} +template template void Outer::f(U) {} +template template U Outer::var = U{}; + +// CHECK: template void Outer::method(); +template void Outer::method(); +// CHECK: template void Outer::f(double); +template void Outer::f(double); +// CHECK: template double Outer::var; +template double Outer::var; +// CHECK: template struct Outer::Inner; +template struct Outer::Inner; + +template struct A { + template struct B { + template void g(V); + }; +}; +template template template +void A::B::g(V) {} + +// CHECK: template void A::B::g(float); +template void A::B::g(float); diff --git a/clang/test/AST/explicit-instantiation-source-info.cpp b/clang/test/AST/explicit-instantiation-source-info.cpp new file mode 100644 index 0000000000000..3086fd261dd25 --- /dev/null +++ b/clang/test/AST/explicit-instantiation-source-info.cpp @@ -0,0 +1,214 @@ +// RUN: %clang_cc1 -fsyntax-only -ast-dump %s | FileCheck %s + +namespace ns { + template void foo(T x) {} + template T bar = T{}; + template struct S { + void method(T x) {} + static T sval; + static T arr[1]; + template void tmpl(U u) {} + template static U mvar; + template struct Nested {}; + struct Inner { T val; }; + }; + template T S::sval = T{}; + template T S::arr[1] = {}; + template template U S::mvar = U{}; + + template struct A { + template struct B { + template void deep(V v) {} + }; + }; +} + +// (a) function template +template void ns::foo(int); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'foo' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: Function {{.*}} 'foo' 'void (int)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (int)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:31 'int' +// CHECK-NEXT: BuiltinTypeLoc 'int' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' + +// (b) variable template +template int ns::bar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'int' +// CHECK-NEXT: BuiltinTypeLoc 'int' +// CHECK-NEXT: TemplateArgument type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' + +// (c) class template +template struct ns::S; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'S' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' +// CHECK-NEXT: TemplateArgument type 'int' +// CHECK-NEXT: BuiltinType {{.*}} 'int' + +// (d) member function +template void ns::S::method(long); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'method' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXMethod {{.*}} 'method' 'void (long)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (long)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:39 'long' +// CHECK-NEXT: BuiltinTypeLoc 'long' +// CHECK-NEXT: BuiltinTypeLoc 'void' + +// (e) static data member +template long ns::S::sval; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'sval' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: Var {{.*}} 'sval' 'long' +// CHECK-NEXT: BuiltinTypeLoc 'long' + +// (e2) static data member with postfix type (array) +template long ns::S::arr[1]; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'arr' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: Var {{.*}} 'arr' 'long[1]' +// CHECK-NEXT: ConstantArrayTypeLoc 'long[1]' 1 +// CHECK-NEXT: BuiltinTypeLoc 'long' + +// (f) member function template +template void ns::S::tmpl(double); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'tmpl' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXMethod {{.*}} 'tmpl' 'void (double)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:47 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// (g) nested class +template struct ns::S::Inner; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'Inner' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXRecord {{.*}} 'Inner' + +// extern template variants +extern template void ns::foo(float); +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'foo' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: Function {{.*}} 'foo' 'void (float)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:42 'float' +// CHECK-NEXT: BuiltinTypeLoc 'float' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' + +extern template struct ns::S; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'S' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' + +// extern template: variable template +extern template double ns::bar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'bar' +// CHECK-NEXT: NestedNameSpecifier Namespace {{.*}} 'ns' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// extern template: member function +extern template void ns::S::method(double); +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'method' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXMethod {{.*}} 'method' 'void (double)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (double)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:50 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: BuiltinTypeLoc 'void' + +// extern template: static data member +extern template double ns::S::sval; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'sval' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: Var {{.*}} 'sval' 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' + +// extern template: member function template +extern template void ns::S::tmpl(float); +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'tmpl' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXMethod {{.*}} 'tmpl' 'void (float)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:54 'float' +// CHECK-NEXT: BuiltinTypeLoc 'float' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' + +// extern template: nested class +extern template struct ns::S::Inner; +// CHECK: ExplicitInstantiationDecl {{.*}} col:8 explicit_instantiation_declaration extern 'Inner' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: CXXRecord {{.*}} 'Inner' + +// member variable template +template double ns::S::mvar; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'mvar' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: VarTemplateSpecialization {{.*}} 'mvar' 'double' +// CHECK-NEXT: BuiltinTypeLoc 'double' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// member class template +template struct ns::S::Nested; +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'Nested' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::S' +// CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'Nested' +// CHECK-NEXT: TemplateArgument type 'double' +// CHECK-NEXT: BuiltinType {{.*}} 'double' + +// deeply nested: A::B::deep +template void ns::A::B::deep(float); +// CHECK: ExplicitInstantiationDecl {{.*}} col:1 explicit_instantiation_definition 'deep' +// CHECK-NEXT: NestedNameSpecifier TypeSpec 'ns::A::B' +// CHECK-NEXT: CXXMethod {{.*}} 'deep' 'void (float)' +// CHECK-NEXT: FunctionProtoTypeLoc 'void (float)' cdecl +// CHECK-NEXT: ParmVarDecl {{.*}} col:55 'float' +// CHECK-NEXT: BuiltinTypeLoc 'float' +// CHECK-NEXT: BuiltinTypeLoc 'void' +// CHECK-NEXT: TemplateArgument type 'float' +// CHECK-NEXT: BuiltinType {{.*}} 'float' + +// Same-namespace explicit instantiation (no cross-namespace qualifier) +namespace ns { + template void foo(short); + // CHECK: ExplicitInstantiationDecl {{.*}} col:3 explicit_instantiation_definition 'foo' + // CHECK-NEXT: Function {{.*}} 'foo' 'void (short)' + // CHECK-NEXT: FunctionProtoTypeLoc 'void (short)' cdecl + // CHECK-NEXT: ParmVarDecl {{.*}} col:33 'short' + // CHECK-NEXT: BuiltinTypeLoc 'short' + // CHECK-NEXT: BuiltinTypeLoc 'void' + // CHECK-NEXT: TemplateArgument type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' + + template short bar; + // CHECK: ExplicitInstantiationDecl {{.*}} col:3 explicit_instantiation_definition 'bar' + // CHECK-NEXT: VarTemplateSpecialization {{.*}} 'bar' 'short' + // CHECK-NEXT: BuiltinTypeLoc 'short' + // CHECK-NEXT: TemplateArgument type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' + + template struct S; + // CHECK: ExplicitInstantiationDecl {{.*}} col:3 explicit_instantiation_definition 'S' + // CHECK-NEXT: ClassTemplateSpecialization {{.*}} 'S' + // CHECK-NEXT: TemplateArgument type 'short' + // CHECK-NEXT: BuiltinType {{.*}} 'short' +} diff --git a/clang/test/Modules/explicit-instantiation-in-module.cppm b/clang/test/Modules/explicit-instantiation-in-module.cppm new file mode 100644 index 0000000000000..c38095a9d7086 --- /dev/null +++ b/clang/test/Modules/explicit-instantiation-in-module.cppm @@ -0,0 +1,61 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// Full BMI: both EIDs survive. +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -I%t -emit-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -ast-dump-all 2>&1 | FileCheck %s --check-prefix=FULL +// +// Reduced BMI: both EIDs survive (linked from the specialization). +// RUN: %clang_cc1 -std=c++20 %t/M.cppm -I%t -emit-reduced-module-interface -o %t/M.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -ast-dump-all 2>&1 | FileCheck %s --check-prefix=REDUCED + +//--- header.h +#ifndef HEADER_H +#define HEADER_H +template +struct GMFStruct { + T value; + T get() const { return value; } +}; +#endif + +//--- M.cppm +module; +#include "header.h" + +// Explicit instantiation in GMF. +template struct GMFStruct; + +export module M; + +export template +struct PurvStruct { + T value; + T get() const { return value; } +}; + +// Explicit instantiation in module purview. +template struct PurvStruct; + +export using ::GMFStruct; + +//--- use.cpp +// expected-no-diagnostics +import M; + +// FULL: ExplicitInstantiationDecl {{.*}} imported in M. {{.*}} explicit_instantiation_definition 'GMFStruct' +// FULL: ExplicitInstantiationDecl {{.*}} imported in M {{.*}} explicit_instantiation_definition 'PurvStruct' + +// REDUCED: ExplicitInstantiationDecl {{.*}} imported in M. {{.*}} explicit_instantiation_definition 'GMFStruct' +// REDUCED: ExplicitInstantiationDecl {{.*}} imported in M {{.*}} explicit_instantiation_definition 'PurvStruct' + +void test() { + GMFStruct g; + g.value = 1; + + PurvStruct p; + p.value = 2; +} diff --git a/clang/test/Modules/explicit-instantiation-merge.cppm b/clang/test/Modules/explicit-instantiation-merge.cppm new file mode 100644 index 0000000000000..2ad373286a26c --- /dev/null +++ b/clang/test/Modules/explicit-instantiation-merge.cppm @@ -0,0 +1,62 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t +// +// Two modules both include the same header and explicitly instantiate the same +// specialization. After importing both, the EIDs should be merged and at least +// one should survive. +// +// RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 %t/B.cppm -I%t -emit-module-interface -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -ast-dump-all 2>&1 | FileCheck %s --check-prefix=CHECK-AST +// +// Verify cross-module duplicate diagnostic points to the module's EID location. +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/dup.cpp -verify -fsyntax-only +// +// RUN: %clang_cc1 -std=c++20 %t/A.cppm -I%t -emit-reduced-module-interface -o %t/A.pcm +// RUN: %clang_cc1 -std=c++20 %t/B.cppm -I%t -emit-reduced-module-interface -o %t/B.pcm +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -verify -fsyntax-only +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/use.cpp -ast-dump-all 2>&1 | FileCheck %s --check-prefix=CHECK-AST +// RUN: %clang_cc1 -std=c++20 -fprebuilt-module-path=%t %t/dup.cpp -verify -fsyntax-only + +//--- header.h +#ifndef HEADER_H +#define HEADER_H +template +struct S { + T value; +}; +#endif + +//--- A.cppm +module; +#include "header.h" +template struct S; // #A-inst +export module A; +export using ::S; + +//--- B.cppm +module; +#include "header.h" +template struct S; +export module B; +export using ::S; + +//--- use.cpp +// expected-no-diagnostics +import A; +import B; + +// CHECK-AST: ExplicitInstantiationDecl {{.*}} imported {{.*}} explicit_instantiation_definition 'S' + +void test() { + S s; + s.value = 42; +} + +//--- dup.cpp +import A; + +template struct S; // expected-error {{duplicate explicit instantiation of 'S'}} + // expected-note@A.cppm:3 {{previous explicit instantiation is here}} diff --git a/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp b/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp new file mode 100644 index 0000000000000..e4f08762175a9 --- /dev/null +++ b/clang/test/SemaTemplate/explicit-instantiation-diag-location.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_cc1 -fsyntax-only -verify %s + +// Verify that the "previous explicit instantiation" note points to the first +// explicit instantiation statement, not to the implicit instantiation site. + +namespace GH21133 { + +template void f() {} +void use_f() { f(); } +template void f(); // expected-note{{previous explicit instantiation is here}} +template void f(); // expected-error{{duplicate explicit instantiation of 'f'}} + +template struct S {}; +void use_S(S) {} +template struct S; // expected-note{{previous explicit instantiation is here}} +template struct S; // expected-error{{duplicate explicit instantiation of 'S'}} + +template T var = T{}; +int use_var = var; +template int var; // expected-note{{previous explicit instantiation is here}} +template int var; // expected-error{{duplicate explicit instantiation of 'var'}} + +namespace ns { + template void g() {} +} +void use_g() { ns::g(); } +template void ns::g(); // expected-note{{previous explicit instantiation is here}} +template void ns::g(); // expected-error{{duplicate explicit instantiation of 'g'}} + +template struct Outer { + template void f(U); + template static U var; + template struct Inner {}; +}; +template template void Outer::f(U) {} +template template U Outer::var = U{}; + +void use_members() { + Outer o; + o.f(1.0); + (void)Outer::var; + Outer::Inner inner; +} + +template void Outer::f(double); // expected-note{{previous explicit instantiation is here}} +template void Outer::f(double); // expected-error{{duplicate explicit instantiation of 'f'}} + +template double Outer::var; // expected-note{{previous explicit instantiation is here}} +template double Outer::var; // expected-error{{duplicate explicit instantiation of 'var'}} + +template struct Outer::Inner; // expected-note{{previous explicit instantiation is here}} +template struct Outer::Inner; // expected-error{{duplicate explicit instantiation of 'Inner'}} + +template struct A { + template struct B { + template void f(V); + }; +}; +template template template +void A::B::f(V) {} + +void use_nested() { A::B b; b.f(1.0f); } + +template void A::B::f(float); // expected-note{{previous explicit instantiation is here}} +template void A::B::f(float); // expected-error{{duplicate explicit instantiation of 'f'}} + +} // namespace GH21133 diff --git a/clang/tools/libclang/CIndex.cpp b/clang/tools/libclang/CIndex.cpp index f1532d0b064b3..086253cf477c7 100644 --- a/clang/tools/libclang/CIndex.cpp +++ b/clang/tools/libclang/CIndex.cpp @@ -7264,6 +7264,7 @@ CXCursor clang_getCursorDefinition(CXCursor C) { case Decl::FileScopeAsm: case Decl::TopLevelStmt: case Decl::StaticAssert: + case Decl::ExplicitInstantiation: case Decl::Block: case Decl::OutlinedFunction: case Decl::Captured: @@ -8844,6 +8845,7 @@ static CXLanguageKind getDeclLanguage(const Decl *D) { case Decl::NamespaceAlias: case Decl::NonTypeTemplateParm: case Decl::StaticAssert: + case Decl::ExplicitInstantiation: case Decl::TemplateTemplateParm: case Decl::TemplateTypeParm: case Decl::UnresolvedUsingTypename: