Skip to content

Commit a413c56

Browse files
authored
[Clang][Sema] Fix a bug on template partial specialization with issue on deduction of nontype template parameter (#90376)
Fix #68885 When build expression from a deduced argument whose kind is `Declaration` and `NTTPType`(which declared as `decltype(auto)`) is deduced as a reference type, `BuildExpressionFromDeclTemplateArgument` just create a `DeclRef`. This is incorrect while we get type from the expression since we can't get the original reference type from `DeclRef`. Creating a `SubstNonTypeTemplateParmExpr` expression and make the deduction correct. `Replacement` expression of `SubstNonTypeTemplateParmExpr` just helps the deduction and may not be same with the original expression. Co-authored-by: huqizhi <[email protected]>
1 parent eaee8aa commit a413c56

File tree

5 files changed

+50
-12
lines changed

5 files changed

+50
-12
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ Bug Fixes to C++ Support
612612
- Fix CTAD for ``std::initializer_list``. This allows ``std::initializer_list{1, 2, 3}`` to be deduced as
613613
``std::initializer_list<int>`` as intended.
614614
- Fix a bug on template partial specialization whose template parameter is `decltype(auto)`.
615+
- Fix a bug on template partial specialization with issue on deduction of nontype template parameter
616+
whose type is `decltype(auto)`. Fixes (#GH68885).
615617

616618
Bug Fixes to AST Handling
617619
^^^^^^^^^^^^^^^^^^^^^^^^^

clang/include/clang/Sema/Sema.h

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9249,7 +9249,8 @@ class Sema final : public SemaBase {
92499249
void NoteTemplateParameterLocation(const NamedDecl &Decl);
92509250

92519251
ExprResult BuildExpressionFromDeclTemplateArgument(
9252-
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc);
9252+
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
9253+
NamedDecl *TemplateParam = nullptr);
92539254
ExprResult
92549255
BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg,
92559256
SourceLocation Loc);
@@ -9572,9 +9573,10 @@ class Sema final : public SemaBase {
95729573

95739574
bool isSameOrCompatibleFunctionType(QualType Param, QualType Arg);
95749575

9575-
TemplateArgumentLoc getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
9576-
QualType NTTPType,
9577-
SourceLocation Loc);
9576+
TemplateArgumentLoc
9577+
getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, QualType NTTPType,
9578+
SourceLocation Loc,
9579+
NamedDecl *TemplateParam = nullptr);
95789580

95799581
/// Get a template argument mapping the given template parameter to itself,
95809582
/// e.g. for X in \c template<int X>, this would return an expression template

clang/lib/Sema/SemaTemplate.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8438,10 +8438,9 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) {
84388438
/// declaration and the type of its corresponding non-type template
84398439
/// parameter, produce an expression that properly refers to that
84408440
/// declaration.
8441-
ExprResult
8442-
Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
8443-
QualType ParamType,
8444-
SourceLocation Loc) {
8441+
ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
8442+
const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc,
8443+
NamedDecl *TemplateParam) {
84458444
// C++ [temp.param]p8:
84468445
//
84478446
// A non-type template-parameter of type "array of T" or
@@ -8508,6 +8507,18 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg,
85088507
} else {
85098508
assert(ParamType->isReferenceType() &&
85108509
"unexpected type for decl template argument");
8510+
if (NonTypeTemplateParmDecl *NTTP =
8511+
dyn_cast_if_present<NonTypeTemplateParmDecl>(TemplateParam)) {
8512+
QualType TemplateParamType = NTTP->getType();
8513+
const AutoType *AT = TemplateParamType->getAs<AutoType>();
8514+
if (AT && AT->isDecltypeAuto()) {
8515+
RefExpr = new (getASTContext()) SubstNonTypeTemplateParmExpr(
8516+
ParamType->getPointeeType(), RefExpr.get()->getValueKind(),
8517+
RefExpr.get()->getExprLoc(), RefExpr.get(), VD, NTTP->getIndex(),
8518+
/*PackIndex=*/std::nullopt,
8519+
/*RefParam=*/true);
8520+
}
8521+
}
85118522
}
85128523

85138524
// At this point we should have the right value category.

clang/lib/Sema/SemaTemplateDeduction.cpp

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2639,7 +2639,8 @@ static bool isSameTemplateArg(ASTContext &Context,
26392639
/// argument.
26402640
TemplateArgumentLoc
26412641
Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
2642-
QualType NTTPType, SourceLocation Loc) {
2642+
QualType NTTPType, SourceLocation Loc,
2643+
NamedDecl *TemplateParam) {
26432644
switch (Arg.getKind()) {
26442645
case TemplateArgument::Null:
26452646
llvm_unreachable("Can't get a NULL template argument here");
@@ -2651,7 +2652,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg,
26512652
case TemplateArgument::Declaration: {
26522653
if (NTTPType.isNull())
26532654
NTTPType = Arg.getParamTypeForDecl();
2654-
Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc)
2655+
Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc,
2656+
TemplateParam)
26552657
.getAs<Expr>();
26562658
return TemplateArgumentLoc(TemplateArgument(E), E);
26572659
}
@@ -2718,8 +2720,8 @@ static bool ConvertDeducedTemplateArgument(
27182720
// Convert the deduced template argument into a template
27192721
// argument that we can check, almost as if the user had written
27202722
// the template argument explicitly.
2721-
TemplateArgumentLoc ArgLoc =
2722-
S.getTrivialTemplateArgumentLoc(Arg, QualType(), Info.getLocation());
2723+
TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc(
2724+
Arg, QualType(), Info.getLocation(), Param);
27232725

27242726
// Check the template argument, converting it as necessary.
27252727
return S.CheckTemplateArgument(

clang/test/SemaCXX/PR68885.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s
2+
3+
// expected-no-diagnostics
4+
5+
template <decltype(auto) a>
6+
struct S {
7+
static constexpr int i = 42;
8+
};
9+
10+
template <decltype(auto) a> requires true
11+
struct S<a> {
12+
static constexpr int i = 0;
13+
};
14+
15+
static constexpr int a = 0;
16+
17+
void test() {
18+
static_assert(S<a>::i == 0);
19+
static_assert(S<(a)>::i == 0);
20+
static_assert(S<((a))>::i == 0);
21+
}

0 commit comments

Comments
 (0)