diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 0e9fcaa5fac6a..6df6bddc28948 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -184,6 +184,7 @@ Bug Fixes to C++ Support (``[[assume(expr)]]``) creates temporary objects. - Fix the dynamic_cast to final class optimization to correctly handle casts that are guaranteed to fail (#GH137518). +- Fix bug rejecting partial specialization of variable templates with auto NTTPs (#GH118190). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 2d8fdb5b766fc..784e82c5efa63 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4099,7 +4099,6 @@ static bool CheckTemplateSpecializationScope(Sema &S, NamedDecl *Specialized, static TemplateSpecializationKind getTemplateSpecializationKind(Decl *D); static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg, - NamedDecl *Param, unsigned Depth, unsigned Index) { switch (Arg.getKind()) { @@ -4139,8 +4138,9 @@ static bool isTemplateArgumentTemplateParameter(const TemplateArgument &Arg, } static bool isSameAsPrimaryTemplate(TemplateParameterList *Params, + TemplateParameterList *SpecParams, ArrayRef Args) { - if (Params->size() != Args.size()) + if (Params->size() != Args.size() || Params->size() != SpecParams->size()) return false; unsigned Depth = Params->getDepth(); @@ -4157,9 +4157,19 @@ static bool isSameAsPrimaryTemplate(TemplateParameterList *Params, Arg = Arg.pack_begin()->getPackExpansionPattern(); } - if (!isTemplateArgumentTemplateParameter(Arg, Params->getParam(I), Depth, - I)) + if (!isTemplateArgumentTemplateParameter(Arg, Depth, I)) return false; + + // For NTTPs further specialization is allowed via deduced types, so + // we need to make sure to only reject here if primary template and + // specialization use the same type for the NTTP. + if (auto *SpecNTTP = + dyn_cast(SpecParams->getParam(I))) { + auto *NTTP = dyn_cast(Params->getParam(I)); + if (!NTTP || NTTP->getType().getCanonicalType() != + SpecNTTP->getType().getCanonicalType()) + return false; + } } return true; @@ -4357,7 +4367,7 @@ DeclResult Sema::ActOnVarTemplateSpecialization( } if (isSameAsPrimaryTemplate(VarTemplate->getTemplateParameters(), - CTAI.CanonicalConverted) && + TemplateParams, CTAI.CanonicalConverted) && (!Context.getLangOpts().CPlusPlus20 || !TemplateParams->hasAssociatedConstraints())) { // C++ [temp.class.spec]p9b3: diff --git a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp index c35743b87adbc..9c25e26f43c36 100644 --- a/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp +++ b/clang/test/SemaTemplate/temp_arg_nontype_cxx1z.cpp @@ -621,3 +621,8 @@ namespace GH73460 { int j; template struct A; } // namespace GH73460 + +namespace GH118190 { + template int x; + template int x; +}