https://github.com/zwuis updated https://github.com/llvm/llvm-project/pull/134522
>From ccab3dc1f18ffeda9acb07c0bd5f80f65cc788b9 Mon Sep 17 00:00:00 2001 From: Yanzuo Liu <zw...@outlook.com> Date: Sun, 6 Apr 2025 15:06:56 +0800 Subject: [PATCH 1/3] Handle invalid variable template specialization whose type depends on itself --- clang/docs/ReleaseNotes.rst | 2 ++ .../include/clang/Basic/DiagnosticSemaKinds.td | 3 +++ clang/lib/Sema/SemaTemplate.cpp | 7 +++++++ .../SemaTemplate/instantiate-var-template.cpp | 18 ++++++++++++++++++ 4 files changed, 30 insertions(+) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 5217e04b5e83f..9e0fe8a94729b 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -414,6 +414,8 @@ Bug Fixes to C++ Support - Clang now issues an error when placement new is used to modify a const-qualified variable in a ``constexpr`` function. (#GH131432) - Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806) +- No longer crashes when instantiating invalid variable template specialization + whose type depends on itself. (#GH51347) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 393bfecf9a36b..0c1da40dba388 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -5259,6 +5259,9 @@ def err_template_member_noparams : Error< "extraneous 'template<>' in declaration of member %0">; def err_template_tag_noparams : Error< "extraneous 'template<>' in declaration of %0 %1">; +def err_var_template_spec_type_depends_on_self : Error< + "the type of variable template specialization %0 declared with deduced type " + "%1 depends on itself">; def warn_unqualified_call_to_std_cast_function : Warning< "unqualified call to '%0'">, InGroup<DiagGroup<"unqualified-std-cast-call">>; diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 153f44f8ec67a..ce54dbbb3b9fe 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4377,6 +4377,13 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, if (VarTemplateSpecializationDecl *Spec = Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) { checkSpecializationReachability(TemplateNameLoc, Spec); + if (Spec->getType()->isUndeducedType()) { + // We are substituting the initializer of this variable template + // specialization. + Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self) + << Spec << Spec->getType(); + return true; + } // If we already have a variable template specialization, return it. return Spec; } diff --git a/clang/test/SemaTemplate/instantiate-var-template.cpp b/clang/test/SemaTemplate/instantiate-var-template.cpp index 60d3bd3b59f53..34a1b9710814b 100644 --- a/clang/test/SemaTemplate/instantiate-var-template.cpp +++ b/clang/test/SemaTemplate/instantiate-var-template.cpp @@ -47,3 +47,21 @@ namespace InvalidInsertPos { template<> int v<int, 0>; int k = v<int, 500>; } + +namespace GH51347 { + template <typename T> + auto p = p<T>; // expected-error {{the type of variable template specialization 'p<int>'}} + + auto x = p<int>; // expected-note {{in instantiation of variable template specialization 'GH51347::p'}} +} + +namespace GH97881_comment { + template <bool B> + auto g = sizeof(g<!B>); + // expected-error@-1 {{the type of variable template specialization 'g<false>'}} + // expected-note@-2 {{in instantiation of variable template specialization 'GH97881_comment::g'}} + + void test() { + (void)sizeof(g<false>); // expected-note {{in instantiation of variable template specialization 'GH97881_comment::g'}} + } +} >From c23525e5ff672e679b76717033205e84d1c70c02 Mon Sep 17 00:00:00 2001 From: Yanzuo Liu <zw...@outlook.com> Date: Mon, 28 Apr 2025 23:03:20 +0800 Subject: [PATCH 2/3] Recognize things like current instantiation --- .../clang/Basic/DiagnosticSemaKinds.td | 11 ++- clang/lib/Sema/SemaExpr.cpp | 3 +- clang/lib/Sema/SemaTemplate.cpp | 68 +++++++++++++++++-- .../cxx1y-variable-templates_top_level.cpp | 17 +++++ .../SemaTemplate/instantiate-var-template.cpp | 7 -- 5 files changed, 90 insertions(+), 16 deletions(-) diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 0c1da40dba388..5afd824fad4f1 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -2495,9 +2495,14 @@ def note_implicit_deduction_guide : Note<"implicit deduction guide declared as ' def warn_cxx98_compat_auto_type_specifier : Warning< "'auto' type specifier is incompatible with C++98">, InGroup<CXX98Compat>, DefaultIgnore; -def err_auto_variable_cannot_appear_in_own_initializer : Error< - "variable %0 declared with deduced type %1 " - "cannot appear in its own initializer">; +def err_auto_variable_cannot_appear_in_own_initializer + : Error< + "%enum_select<ParsingInitFor>{%Var{variable}|" + "%VarTemplate{variable template}|" + "%VarTemplatePartialSpec{variable template partial specialization}|" + "%VarTemplateExplicitSpec{variable template explicit " + "specialization}}0 %1 " + "declared with deduced type %2 cannot appear in its own initializer">; def err_binding_cannot_appear_in_own_initializer : Error< "binding %0 cannot appear in the initializer of its own " "decomposition declaration">; diff --git a/clang/lib/Sema/SemaExpr.cpp b/clang/lib/Sema/SemaExpr.cpp index e7f418ae6802e..82b581d7d1dc2 100644 --- a/clang/lib/Sema/SemaExpr.cpp +++ b/clang/lib/Sema/SemaExpr.cpp @@ -251,7 +251,8 @@ bool Sema::DiagnoseUseOfDecl(NamedDecl *D, ArrayRef<SourceLocation> Locs, << D->getDeclName(); } else { Diag(Loc, diag::err_auto_variable_cannot_appear_in_own_initializer) - << D->getDeclName() << cast<VarDecl>(D)->getType(); + << diag::ParsingInitFor::Var << D->getDeclName() + << cast<VarDecl>(D)->getType(); } return true; } diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index ce54dbbb3b9fe..2212d8eca9c39 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4348,6 +4348,23 @@ struct PartialSpecMatchResult { VarTemplatePartialSpecializationDecl *Partial; TemplateArgumentList *Args; }; + +struct TemplateArgEqualityComparator { + const ASTContext &Context; + + bool operator()(const TemplateArgument &Canonical, + const TemplateArgument &Unknown) const { + llvm::FoldingSetNodeID ID1, ID2; + Canonical.Profile(ID1, Context); + Context.getCanonicalTemplateArgument(Unknown).Profile(ID2, Context); +#ifndef NDEBUG + llvm::FoldingSetNodeID ID3; + Context.getCanonicalTemplateArgument(Canonical).Profile(ID3, Context); + assert(ID1 == ID3); +#endif + return ID1 == ID2; + } +}; } // end anonymous namespace DeclResult @@ -4368,8 +4385,40 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, // Produce a placeholder value if the specialization is dependent. if (Template->getDeclContext()->isDependentContext() || TemplateSpecializationType::anyDependentTemplateArguments( - TemplateArgs, CTAI.CanonicalConverted)) + TemplateArgs, CTAI.CanonicalConverted)) { + if (ParsingInitForAutoVars.empty()) + return DeclResult(); + + if (VarDecl *Var = Template->getTemplatedDecl(); + ParsingInitForAutoVars.count(Var) && + llvm::equal( + CTAI.CanonicalConverted, + Template->getTemplateParameters()->getInjectedTemplateArgs(Context), + TemplateArgEqualityComparator{Context})) { + Diag(TemplateNameLoc, + diag::err_auto_variable_cannot_appear_in_own_initializer) + << diag::ParsingInitFor::VarTemplate << Var << Var->getType() + << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + return true; + } + + SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs; + Template->getPartialSpecializations(PartialSpecs); + for (VarTemplatePartialSpecializationDecl *Partial : PartialSpecs) + if (ParsingInitForAutoVars.count(Partial) && + llvm::equal(CTAI.CanonicalConverted, + Partial->getTemplateArgs().asArray(), + TemplateArgEqualityComparator{Context})) { + Diag(TemplateNameLoc, + diag::err_auto_variable_cannot_appear_in_own_initializer) + << diag::ParsingInitFor::VarTemplatePartialSpec << Partial + << Partial->getType() + << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + return true; + } + return DeclResult(); + } // Find the variable template specialization declaration that // corresponds to these arguments. @@ -4378,10 +4427,19 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, Template->findSpecialization(CTAI.CanonicalConverted, InsertPos)) { checkSpecializationReachability(TemplateNameLoc, Spec); if (Spec->getType()->isUndeducedType()) { - // We are substituting the initializer of this variable template - // specialization. - Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self) - << Spec << Spec->getType(); + if (ParsingInitForAutoVars.count(Spec)) + Diag(TemplateNameLoc, + diag::err_auto_variable_cannot_appear_in_own_initializer) + << diag::ParsingInitFor::VarTemplateExplicitSpec << Spec + << Spec->getType() + << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + else + // We are substituting the initializer of this variable template + // specialization. + Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self) + << Spec << Spec->getType() + << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + return true; } // If we already have a variable template specialization, return it. diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp index 6fc2032ee7fb4..1fe0ce9aabf29 100644 --- a/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp +++ b/clang/test/SemaCXX/cxx1y-variable-templates_top_level.cpp @@ -492,4 +492,21 @@ static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[3] == 3, ""); static_assert(C<int, 0,1,2,3,4>::VALUEARRAY[0] == 0, ""); } + +namespace appear_in_its_own_init { +template <class T> +auto GH51347 = GH51347<T>; // expected-error {{variable template 'GH51347' declared with deduced type 'auto' cannot appear in its own initializer}} + +template <class T, class... Ts> +auto a = [] { + using U = T; + a<U, Ts...>; // expected-error {{variable template 'a' declared with deduced type 'auto' cannot appear in its own initializer}} +}; + +template <int...> int b; +template <int I> +auto b<I, I * 2, 5> = b<I, I * 2, 5l>; // expected-error {{variable template partial specialization 'b<I, I * 2, 5>' declared with deduced type 'auto' cannot appear in its own initializer}} +template <> auto b<0, 0, 0> = b<0, 0, 0>; // expected-error {{variable template explicit specialization 'b<0, 0, 0>' declared with deduced type 'auto' cannot appear in its own initializer}} +} + #endif diff --git a/clang/test/SemaTemplate/instantiate-var-template.cpp b/clang/test/SemaTemplate/instantiate-var-template.cpp index 34a1b9710814b..50b7219af4bea 100644 --- a/clang/test/SemaTemplate/instantiate-var-template.cpp +++ b/clang/test/SemaTemplate/instantiate-var-template.cpp @@ -48,13 +48,6 @@ namespace InvalidInsertPos { int k = v<int, 500>; } -namespace GH51347 { - template <typename T> - auto p = p<T>; // expected-error {{the type of variable template specialization 'p<int>'}} - - auto x = p<int>; // expected-note {{in instantiation of variable template specialization 'GH51347::p'}} -} - namespace GH97881_comment { template <bool B> auto g = sizeof(g<!B>); >From 50d40656f62ae98d428db474ba683cbb2e98179f Mon Sep 17 00:00:00 2001 From: Yanzuo Liu <zw...@outlook.com> Date: Tue, 29 Apr 2025 23:50:39 +0800 Subject: [PATCH 3/3] Introduce `ASTContext::isSameTemplateArgument`, remove underlines in diagnostic messages, and add test --- clang/docs/ReleaseNotes.rst | 2 +- clang/include/clang/AST/ASTContext.h | 5 ++ clang/lib/AST/ASTContext.cpp | 48 +++++++++++++++++++ clang/lib/Sema/SemaTemplate.cpp | 38 +++++---------- clang/lib/Sema/SemaTemplateDeduction.cpp | 24 ++-------- .../cxx1y-variable-templates_in_class.cpp | 20 ++++++++ 6 files changed, 88 insertions(+), 49 deletions(-) diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 9e0fe8a94729b..4c0513f6a849f 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -415,7 +415,7 @@ Bug Fixes to C++ Support in a ``constexpr`` function. (#GH131432) - Clang now emits a warning when class template argument deduction for alias templates is used in C++17. (#GH133806) - No longer crashes when instantiating invalid variable template specialization - whose type depends on itself. (#GH51347) + whose type depends on itself. (#GH51347), (#GH55872) Bug Fixes to AST Handling ^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h index 3ff9f308f3a5e..99ab20473df92 100644 --- a/clang/include/clang/AST/ASTContext.h +++ b/clang/include/clang/AST/ASTContext.h @@ -2939,6 +2939,11 @@ class ASTContext : public RefCountedBase<ASTContext> { TemplateArgument getCanonicalTemplateArgument(const TemplateArgument &Arg) const; + /// Determine whether the given template arguments \p Arg1 and \p Arg2 are + /// equivalent. + bool isSameTemplateArgument(const TemplateArgument &Arg1, + const TemplateArgument &Arg2) const; + /// Type Query functions. If the type is an instance of the specified class, /// return the Type pointer for the underlying maximally pretty type. This /// is a member of ASTContext because this may need to do some amount of diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp index 1b6b3d06ddc1e..3e43c529cf676 100644 --- a/clang/lib/AST/ASTContext.cpp +++ b/clang/lib/AST/ASTContext.cpp @@ -7594,6 +7594,54 @@ ASTContext::getCanonicalTemplateArgument(const TemplateArgument &Arg) const { llvm_unreachable("Unhandled template argument kind"); } +bool ASTContext::isSameTemplateArgument(const TemplateArgument &Arg1, + const TemplateArgument &Arg2) const { + if (Arg1.getKind() != Arg2.getKind()) + return false; + + switch (Arg1.getKind()) { + case TemplateArgument::Null: + return true; + + case TemplateArgument::Type: + return hasSameType(Arg1.getAsType(), Arg2.getAsType()); + + case TemplateArgument::Declaration: + return Arg1.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl() == + Arg2.getAsDecl()->getUnderlyingDecl()->getCanonicalDecl(); + + case TemplateArgument::NullPtr: + return hasSameType(Arg1.getNullPtrType(), Arg2.getNullPtrType()); + + case TemplateArgument::Template: + case TemplateArgument::TemplateExpansion: + return getCanonicalTemplateName(Arg1.getAsTemplateOrTemplatePattern()) == + getCanonicalTemplateName(Arg2.getAsTemplateOrTemplatePattern()); + + case TemplateArgument::Integral: + return Arg1.getAsIntegral() == Arg2.getAsIntegral(); + + case TemplateArgument::StructuralValue: + return Arg1.structurallyEquals(Arg2); + + case TemplateArgument::Expression: { + llvm::FoldingSetNodeID ID1, ID2; + Arg1.getAsExpr()->Profile(ID1, *this, /*Canonical=*/true); + Arg2.getAsExpr()->Profile(ID2, *this, /*Canonical=*/true); + return ID1 == ID2; + } + + case TemplateArgument::Pack: + return llvm::equal( + Arg1.getPackAsArray(), Arg2.getPackAsArray(), + [&](const TemplateArgument &Arg1, const TemplateArgument &Arg2) { + return isSameTemplateArgument(Arg1, Arg2); + }); + } + + llvm_unreachable("Unhandled template argument kind"); +} + NestedNameSpecifier * ASTContext::getCanonicalNestedNameSpecifier(NestedNameSpecifier *NNS) const { if (!NNS) diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp index 2212d8eca9c39..94c69e1a18e6a 100644 --- a/clang/lib/Sema/SemaTemplate.cpp +++ b/clang/lib/Sema/SemaTemplate.cpp @@ -4348,23 +4348,6 @@ struct PartialSpecMatchResult { VarTemplatePartialSpecializationDecl *Partial; TemplateArgumentList *Args; }; - -struct TemplateArgEqualityComparator { - const ASTContext &Context; - - bool operator()(const TemplateArgument &Canonical, - const TemplateArgument &Unknown) const { - llvm::FoldingSetNodeID ID1, ID2; - Canonical.Profile(ID1, Context); - Context.getCanonicalTemplateArgument(Unknown).Profile(ID2, Context); -#ifndef NDEBUG - llvm::FoldingSetNodeID ID3; - Context.getCanonicalTemplateArgument(Canonical).Profile(ID3, Context); - assert(ID1 == ID3); -#endif - return ID1 == ID2; - } -}; } // end anonymous namespace DeclResult @@ -4389,16 +4372,20 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, if (ParsingInitForAutoVars.empty()) return DeclResult(); + auto IsSameTemplateArg = [&](const TemplateArgument &Arg1, + const TemplateArgument &Arg2) { + return Context.isSameTemplateArgument(Arg1, Arg2); + }; + if (VarDecl *Var = Template->getTemplatedDecl(); ParsingInitForAutoVars.count(Var) && llvm::equal( CTAI.CanonicalConverted, Template->getTemplateParameters()->getInjectedTemplateArgs(Context), - TemplateArgEqualityComparator{Context})) { + IsSameTemplateArg)) { Diag(TemplateNameLoc, diag::err_auto_variable_cannot_appear_in_own_initializer) - << diag::ParsingInitFor::VarTemplate << Var << Var->getType() - << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + << diag::ParsingInitFor::VarTemplate << Var << Var->getType(); return true; } @@ -4408,12 +4395,11 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, if (ParsingInitForAutoVars.count(Partial) && llvm::equal(CTAI.CanonicalConverted, Partial->getTemplateArgs().asArray(), - TemplateArgEqualityComparator{Context})) { + IsSameTemplateArg)) { Diag(TemplateNameLoc, diag::err_auto_variable_cannot_appear_in_own_initializer) << diag::ParsingInitFor::VarTemplatePartialSpec << Partial - << Partial->getType() - << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + << Partial->getType(); return true; } @@ -4431,14 +4417,12 @@ Sema::CheckVarTemplateId(VarTemplateDecl *Template, SourceLocation TemplateLoc, Diag(TemplateNameLoc, diag::err_auto_variable_cannot_appear_in_own_initializer) << diag::ParsingInitFor::VarTemplateExplicitSpec << Spec - << Spec->getType() - << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + << Spec->getType(); else // We are substituting the initializer of this variable template // specialization. Diag(TemplateNameLoc, diag::err_var_template_spec_type_depends_on_self) - << Spec << Spec->getType() - << SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc()); + << Spec << Spec->getType(); return true; } diff --git a/clang/lib/Sema/SemaTemplateDeduction.cpp b/clang/lib/Sema/SemaTemplateDeduction.cpp index 170b9f05002b1..4a23b6efc4da6 100644 --- a/clang/lib/Sema/SemaTemplateDeduction.cpp +++ b/clang/lib/Sema/SemaTemplateDeduction.cpp @@ -2844,35 +2844,17 @@ static bool isSameTemplateArg(ASTContext &Context, llvm_unreachable("Comparing NULL template argument"); case TemplateArgument::Type: - return Context.getCanonicalType(X.getAsType()) == - Context.getCanonicalType(Y.getAsType()); - case TemplateArgument::Declaration: - return isSameDeclaration(X.getAsDecl(), Y.getAsDecl()); - case TemplateArgument::NullPtr: - return Context.hasSameType(X.getNullPtrType(), Y.getNullPtrType()); - case TemplateArgument::Template: case TemplateArgument::TemplateExpansion: - return Context.getCanonicalTemplateName( - X.getAsTemplateOrTemplatePattern()).getAsVoidPointer() == - Context.getCanonicalTemplateName( - Y.getAsTemplateOrTemplatePattern()).getAsVoidPointer(); + case TemplateArgument::StructuralValue: + case TemplateArgument::Expression: + return Context.isSameTemplateArgument(X, Y); case TemplateArgument::Integral: return hasSameExtendedValue(X.getAsIntegral(), Y.getAsIntegral()); - case TemplateArgument::StructuralValue: - return X.structurallyEquals(Y); - - case TemplateArgument::Expression: { - llvm::FoldingSetNodeID XID, YID; - X.getAsExpr()->Profile(XID, Context, true); - Y.getAsExpr()->Profile(YID, Context, true); - return XID == YID; - } - case TemplateArgument::Pack: { unsigned PackIterationSize = X.pack_size(); if (X.pack_size() != Y.pack_size()) { diff --git a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp index eafadb07b29e1..57a48fac56cd6 100644 --- a/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp +++ b/clang/test/SemaCXX/cxx1y-variable-templates_in_class.cpp @@ -412,6 +412,26 @@ namespace dependent_static_var_template { } int cf() { return F<int>(); } + +#ifdef CPP1Y + namespace GH55872 { + struct s { + template<typename T> + static CONST auto f = [] { return T::template g<s>; }; + // expected-note@-1 {{in instantiation of static data member 'dependent_static_var_template::GH55872::t::g' requested here}} + // expected-note@-2 {{while substituting into a lambda expression here}} + }; + + struct t { + template<typename T> + static CONST auto g = [] { return T::template f<t>; }; + // expected-error@-1 {{the type of variable template specialization 'f<dependent_static_var_template::GH55872::t>' declared with deduced type 'const auto' depends on itself}} + // expected-note@-2 {{while substituting into a lambda expression here}} + }; + + void test() { s::f<t>()(); } // expected-note {{in instantiation of static data member 'dependent_static_var_template::GH55872::s::f' requested here}} + } +#endif } #ifndef PRECXX11 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits