From f0e35786d31bb99f31a625a67c89c211cbe6dac1 Mon Sep 17 00:00:00 2001 From: Matheus Izvekov Date: Wed, 17 Sep 2025 18:12:36 -0300 Subject: [PATCH] [clang] check constant template parameters in dependent contexts This patch makes sure constant template parameters are checked even in dependent contexts. This can for example diagnose narrowings earlier, but this is permitted as these templates would have no valid instantiations. --- clang/docs/ReleaseNotes.rst | 3 ++- clang/lib/Sema/SemaTemplate.cpp | 5 ++-- clang/test/SemaTemplate/temp_arg_template.cpp | 24 ++++++++++++------- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 51e5973098c14..0eadc1c773597 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -241,7 +241,8 @@ Improvements to Clang's diagnostics - Fixed fix-it hint for fold expressions. Clang now correctly places the suggested right parenthesis when diagnosing malformed fold expressions. (#GH151787) - Added fix-it hint for when scoped enumerations require explicit conversions for binary operations. (#GH24265) - +- Constant template parameters are now type checked in template definitions, + including template template parameters. - Fixed an issue where emitted format-signedness diagnostics were not associated with an appropriate diagnostic id. Besides being incorrect from an API standpoint, this was user visible, e.g.: "format specifies type 'unsigned int' but the argument has type 'int' [-Wformat]" diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index d6b25c2d83613..faeba6ada0c6b 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -5482,9 +5482,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, if (NTTP->isParameterPack() && NTTP->isExpandedParameterPack()) NTTPType = NTTP->getExpansionType(ArgumentPackIndex); - if (NTTPType->isInstantiationDependentType() && - !isa(Template) && - !Template->getDeclContext()->isDependentContext()) { + if (NTTPType->isInstantiationDependentType()) { // Do substitution on the type of the non-type template parameter. InstantiatingTemplate Inst(*this, TemplateLoc, Template, NTTP, CTAI.SugaredConverted, @@ -5494,6 +5492,7 @@ bool Sema::CheckTemplateArgument(NamedDecl *Param, TemplateArgumentLoc &ArgLoc, MultiLevelTemplateArgumentList MLTAL(Template, CTAI.SugaredConverted, /*Final=*/true); + MLTAL.addOuterRetainedLevels(NTTP->getDepth()); // If the parameter is a pack expansion, expand this slice of the pack. if (auto *PET = NTTPType->getAs()) { Sema::ArgPackSubstIndexRAII SubstIndex(*this, ArgumentPackIndex); diff --git a/clang/test/SemaTemplate/temp_arg_template.cpp b/clang/test/SemaTemplate/temp_arg_template.cpp index aa53dba652050..431e19741ece9 100644 --- a/clang/test/SemaTemplate/temp_arg_template.cpp +++ b/clang/test/SemaTemplate/temp_arg_template.cpp @@ -117,16 +117,8 @@ namespace CheckDependentNonTypeParamTypes { X x; } void h() { - // FIXME: If we accept A at all, it's not obvious what should happen - // here. While parsing the template, we form - // X - // but in the final instantiation do we get - // B - // or - // B - // ? X x; - int check[x.value == 1234 ? 1 : -1]; + // expected-error@-1 {{evaluates to 1234, which cannot be narrowed to type 'unsigned char'}} } }; @@ -143,6 +135,20 @@ namespace CheckDependentNonTypeParamTypes { ab.g(); ab.h(); } + + template struct C { + template struct D {}; + using T = D; + // expected-error@-1 {{evaluates to 1234, which cannot be narrowed to type 'char'}} + }; + + template struct E { + template