Author: Qizhi Hu Date: 2024-04-30T16:15:06+08:00 New Revision: a413c563bdcaac08f7c325c7d69e19f924435e59
URL: https://github.com/llvm/llvm-project/commit/a413c563bdcaac08f7c325c7d69e19f924435e59 DIFF: https://github.com/llvm/llvm-project/commit/a413c563bdcaac08f7c325c7d69e19f924435e59.diff LOG: [Clang][Sema] Fix a bug on template partial specialization with issue on deduction of nontype template parameter (#90376) Fix https://github.com/llvm/llvm-project/issues/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 <836744...@qq.com> Added: clang/test/SemaCXX/PR68885.cpp Modified: clang/docs/ReleaseNotes.rst clang/include/clang/Sema/Sema.h clang/lib/Sema/SemaTemplate.cpp clang/lib/Sema/SemaTemplateDeduction.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 98c80b6017f604..1abc00a25f1f42 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -612,6 +612,8 @@ Bug Fixes to C++ Support - Fix CTAD for ``std::initializer_list``. This allows ``std::initializer_list{1, 2, 3}`` to be deduced as ``std::initializer_list<int>`` as intended. - Fix a bug on template partial specialization whose template parameter is `decltype(auto)`. +- Fix a bug on template partial specialization with issue on deduction of nontype template parameter + whose type is `decltype(auto)`. Fixes (#GH68885). Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index 1ca523ec88c2f9..809b9c4498f697 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -9249,7 +9249,8 @@ class Sema final : public SemaBase { void NoteTemplateParameterLocation(const NamedDecl &Decl); ExprResult BuildExpressionFromDeclTemplateArgument( - const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc); + const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc, + NamedDecl *TemplateParam = nullptr); ExprResult BuildExpressionFromNonTypeTemplateArgument(const TemplateArgument &Arg, SourceLocation Loc); @@ -9572,9 +9573,10 @@ class Sema final : public SemaBase { bool isSameOrCompatibleFunctionType(QualType Param, QualType Arg); - TemplateArgumentLoc getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, - QualType NTTPType, - SourceLocation Loc); + TemplateArgumentLoc + getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, QualType NTTPType, + SourceLocation Loc, + NamedDecl *TemplateParam = nullptr); /// Get a template argument mapping the given template parameter to itself, /// e.g. for X in \c template<int X>, this would return an expression template diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ed5507c0ec0100..3c2a5a4ac47e69 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -8438,10 +8438,9 @@ void Sema::NoteTemplateParameterLocation(const NamedDecl &Decl) { /// declaration and the type of its corresponding non-type template /// parameter, produce an expression that properly refers to that /// declaration. -ExprResult -Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, - QualType ParamType, - SourceLocation Loc) { +ExprResult Sema::BuildExpressionFromDeclTemplateArgument( + const TemplateArgument &Arg, QualType ParamType, SourceLocation Loc, + NamedDecl *TemplateParam) { // C++ [temp.param]p8: // // A non-type template-parameter of type "array of T" or @@ -8508,6 +8507,18 @@ Sema::BuildExpressionFromDeclTemplateArgument(const TemplateArgument &Arg, } else { assert(ParamType->isReferenceType() && "unexpected type for decl template argument"); + if (NonTypeTemplateParmDecl *NTTP = + dyn_cast_if_present<NonTypeTemplateParmDecl>(TemplateParam)) { + QualType TemplateParamType = NTTP->getType(); + const AutoType *AT = TemplateParamType->getAs<AutoType>(); + if (AT && AT->isDecltypeAuto()) { + RefExpr = new (getASTContext()) SubstNonTypeTemplateParmExpr( + ParamType->getPointeeType(), RefExpr.get()->getValueKind(), + RefExpr.get()->getExprLoc(), RefExpr.get(), VD, NTTP->getIndex(), + /*PackIndex=*/std::nullopt, + /*RefParam=*/true); + } + } } // At this point we should have the right value category. diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index c3815bca038554..e93f7bd842e444 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2639,7 +2639,8 @@ static bool isSameTemplateArg(ASTContext &Context, /// argument. TemplateArgumentLoc Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, - QualType NTTPType, SourceLocation Loc) { + QualType NTTPType, SourceLocation Loc, + NamedDecl *TemplateParam) { switch (Arg.getKind()) { case TemplateArgument::Null: llvm_unreachable("Can't get a NULL template argument here"); @@ -2651,7 +2652,8 @@ Sema::getTrivialTemplateArgumentLoc(const TemplateArgument &Arg, case TemplateArgument::Declaration: { if (NTTPType.isNull()) NTTPType = Arg.getParamTypeForDecl(); - Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc) + Expr *E = BuildExpressionFromDeclTemplateArgument(Arg, NTTPType, Loc, + TemplateParam) .getAs<Expr>(); return TemplateArgumentLoc(TemplateArgument(E), E); } @@ -2718,8 +2720,8 @@ static bool ConvertDeducedTemplateArgument( // Convert the deduced template argument into a template // argument that we can check, almost as if the user had written // the template argument explicitly. - TemplateArgumentLoc ArgLoc = - S.getTrivialTemplateArgumentLoc(Arg, QualType(), Info.getLocation()); + TemplateArgumentLoc ArgLoc = S.getTrivialTemplateArgumentLoc( + Arg, QualType(), Info.getLocation(), Param); // Check the template argument, converting it as necessary. return S.CheckTemplateArgument( diff --git a/clang/test/SemaCXX/PR68885.cpp b/clang/test/SemaCXX/PR68885.cpp new file mode 100644 index 00000000000000..4ff95771e2caf5 --- /dev/null +++ b/clang/test/SemaCXX/PR68885.cpp @@ -0,0 +1,21 @@ +// RUN: %clang_cc1 -verify -std=c++20 -fsyntax-only %s + +// expected-no-diagnostics + +template <decltype(auto) a> +struct S { + static constexpr int i = 42; +}; + +template <decltype(auto) a> requires true +struct S<a> { + static constexpr int i = 0; +}; + +static constexpr int a = 0; + +void test() { + static_assert(S<a>::i == 0); + static_assert(S<(a)>::i == 0); + static_assert(S<((a))>::i == 0); +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits