Index: clang/include/clang/AST/ASTContext.h =================================================================== --- clang/include/clang/AST/ASTContext.h +++ clang/include/clang/AST/ASTContext.h @@ -2664,6 +2664,11 @@ /// that they may be used in declarations of the same template. bool isSameTemplateParameter(const NamedDecl *X, const NamedDecl *Y) const; + /// Determine whether two default template arguments are similar enough + /// that they may be used in declarations of the same template. + bool isSameDefaultTemplateArgument(const NamedDecl *X, + const NamedDecl *Y) const; + /// Retrieve the "canonical" template argument. /// /// The canonical template argument is the simplest template argument Index: clang/include/clang/AST/DeclTemplate.h =================================================================== --- clang/include/clang/AST/DeclTemplate.h +++ clang/include/clang/AST/DeclTemplate.h @@ -15,6 +15,7 @@ #define LLVM_CLANG_AST_DECLTEMPLATE_H #include "clang/AST/ASTConcept.h" +#include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclBase.h" #include "clang/AST/DeclCXX.h" @@ -373,11 +374,19 @@ /// Set that the default argument was inherited from another parameter. void setInherited(const ASTContext &C, ParmDecl *InheritedFrom) { - assert(!isInherited() && "default argument already inherited"); InheritedFrom = getParmOwningDefaultArg(InheritedFrom); if (!isSet()) ValueOrInherited = InheritedFrom; - else + else if (auto *D = ValueOrInherited.template dyn_cast()) { + assert(C.isSameDefaultTemplateArgument(D, InheritedFrom)); + ValueOrInherited = + new (allocateDefaultArgStorageChain(C)) Chain{InheritedFrom, get()}; + } else if (auto *Inherited = + ValueOrInherited.template dyn_cast()) { + assert(C.isSameDefaultTemplateArgument(Inherited->PrevDeclWithDefaultArg, + InheritedFrom)); + Inherited->PrevDeclWithDefaultArg = InheritedFrom; + } else ValueOrInherited = new (allocateDefaultArgStorageChain(C)) Chain{InheritedFrom, ValueOrInherited.template get()}; } Index: clang/lib/AST/ASTContext.cpp =================================================================== --- clang/lib/AST/ASTContext.cpp +++ clang/lib/AST/ASTContext.cpp @@ -6294,6 +6294,45 @@ return true; } +bool ASTContext::isSameDefaultTemplateArgument(const NamedDecl *X, + const NamedDecl *Y) const { + // If the type parameter isn't the same already, we don't need to check the + // default argument further. + if (!isSameTemplateParameter(X, Y)) + return false; + + if (auto *TTPX = dyn_cast(X)) { + auto *TTPY = cast(Y); + if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) + return false; + + return hasSameType(TTPX->getDefaultArgument(), TTPY->getDefaultArgument()); + } + + if (auto *NTTPX = dyn_cast(X)) { + auto *NTTPY = cast(Y); + if (!NTTPX->hasDefaultArgument() || !NTTPY->hasDefaultArgument()) + return false; + + Expr *DefaultArgumentX = NTTPX->getDefaultArgument()->IgnoreImpCasts(); + Expr *DefaultArgumentY = NTTPY->getDefaultArgument()->IgnoreImpCasts(); + llvm::FoldingSetNodeID XID, YID; + DefaultArgumentX->Profile(XID, *this, /*Canonical=*/true); + DefaultArgumentY->Profile(YID, *this, /*Canonical=*/true); + return XID == YID; + } + + auto *TTPX = cast(X); + auto *TTPY = cast(Y); + + if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) + return false; + + const TemplateArgument &TAX = TTPX->getDefaultArgument().getArgument(); + const TemplateArgument &TAY = TTPY->getDefaultArgument().getArgument(); + return hasSameTemplateName(TAX.getAsTemplate(), TAY.getAsTemplate()); +} + static NamespaceDecl *getNamespace(const NestedNameSpecifier *X) { if (auto *NS = X->getAsNamespace()) return NS; Index: clang/lib/Sema/SemaTemplate.cpp =================================================================== --- clang/lib/Sema/SemaTemplate.cpp +++ clang/lib/Sema/SemaTemplate.cpp @@ -2643,46 +2643,6 @@ return false; } -static bool hasSameDefaultTemplateArgument(NamedDecl *X, NamedDecl *Y) { - ASTContext &C = X->getASTContext(); - // If the type parameter isn't the same already, we don't need to check the - // default argument further. - if (!C.isSameTemplateParameter(X, Y)) - return false; - - if (auto *TTPX = dyn_cast(X)) { - auto *TTPY = cast(Y); - if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) - return false; - - return C.hasSameType(TTPX->getDefaultArgument(), - TTPY->getDefaultArgument()); - } - - if (auto *NTTPX = dyn_cast(X)) { - auto *NTTPY = cast(Y); - if (!NTTPX->hasDefaultArgument() || !NTTPY->hasDefaultArgument()) - return false; - - Expr *DefaultArgumentX = NTTPX->getDefaultArgument()->IgnoreImpCasts(); - Expr *DefaultArgumentY = NTTPY->getDefaultArgument()->IgnoreImpCasts(); - llvm::FoldingSetNodeID XID, YID; - DefaultArgumentX->Profile(XID, C, /*Canonical=*/true); - DefaultArgumentY->Profile(YID, C, /*Canonical=*/true); - return XID == YID; - } - - auto *TTPX = cast(X); - auto *TTPY = cast(Y); - - if (!TTPX->hasDefaultArgument() || !TTPY->hasDefaultArgument()) - return false; - - const TemplateArgument &TAX = TTPX->getDefaultArgument().getArgument(); - const TemplateArgument &TAY = TTPY->getDefaultArgument().getArgument(); - return C.hasSameTemplateName(TAX.getAsTemplate(), TAY.getAsTemplate()); -} - /// Checks the validity of a template parameter list, possibly /// considering the template parameter list from a previous /// declaration. @@ -2780,7 +2740,8 @@ if (!OldTypeParm->getOwningModule() || isModuleUnitOfCurrentTU(OldTypeParm->getOwningModule())) RedundantDefaultArg = true; - else if (!hasSameDefaultTemplateArgument(OldTypeParm, NewTypeParm)) { + else if (!getASTContext().isSameDefaultTemplateArgument(OldTypeParm, + NewTypeParm)) { InconsistentDefaultArg = true; PrevModuleName = OldTypeParm->getImportedOwningModule()->getFullModuleName(); @@ -2832,8 +2793,8 @@ if (!OldNonTypeParm->getOwningModule() || isModuleUnitOfCurrentTU(OldNonTypeParm->getOwningModule())) RedundantDefaultArg = true; - else if (!hasSameDefaultTemplateArgument(OldNonTypeParm, - NewNonTypeParm)) { + else if (!getASTContext().isSameDefaultTemplateArgument( + OldNonTypeParm, NewNonTypeParm)) { InconsistentDefaultArg = true; PrevModuleName = OldNonTypeParm->getImportedOwningModule()->getFullModuleName(); @@ -2884,8 +2845,8 @@ if (!OldTemplateParm->getOwningModule() || isModuleUnitOfCurrentTU(OldTemplateParm->getOwningModule())) RedundantDefaultArg = true; - else if (!hasSameDefaultTemplateArgument(OldTemplateParm, - NewTemplateParm)) { + else if (!getASTContext().isSameDefaultTemplateArgument( + OldTemplateParm, NewTemplateParm)) { InconsistentDefaultArg = true; PrevModuleName = OldTemplateParm->getImportedOwningModule()->getFullModuleName(); Index: clang/test/Modules/InheritDefaultArguments.cppm =================================================================== --- clang/test/Modules/InheritDefaultArguments.cppm +++ clang/test/Modules/InheritDefaultArguments.cppm @@ -10,7 +10,10 @@ class Templ; template -class Templ {}; +class Templ { +public: + Templ(T t) {} +}; template Templ(T t) -> Templ; @@ -26,3 +29,6 @@ #include "foo.h" export module X; import A; +void foo() { + Templ t(0); +} Index: clang/test/Modules/Inputs/PR31469/textual.h =================================================================== --- clang/test/Modules/Inputs/PR31469/textual.h +++ clang/test/Modules/Inputs/PR31469/textual.h @@ -4,7 +4,7 @@ template class allocator; template > class list; template class __list_iterator { - //template friend class list; // causes another crash in ASTDeclReader::attachPreviousDecl + template friend class list; template friend class list; }; template class __list_imp {};