Skip to content

[Clang][Sema] Handle invalid variable template specialization whose type depends on itself #134522

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
May 7, 2025

Conversation

zwuis
Copy link
Contributor

@zwuis zwuis commented Apr 6, 2025

Fixes #51347
Fixes #55872

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Apr 6, 2025
@llvmbot
Copy link
Member

llvmbot commented Apr 6, 2025

@llvm/pr-subscribers-clang

Author: Yanzuo Liu (zwuis)

Changes

Fixes #51347


Full diff: https://github.com/llvm/llvm-project/pull/134522.diff

4 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+7)
  • (modified) clang/test/SemaTemplate/instantiate-var-template.cpp (+18)
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'}}
+  }
+}

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@zyn0217 zyn0217 requested review from cor3ntin and erichkeane April 6, 2025 10:27

namespace GH51347 {
template <typename T>
auto p = p<T>; // expected-error {{the type of variable template specialization 'p<int>'}}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are there cases where this pattern is legal and we should allow this anyway? If not, should we diagnose this not at instantiation, but earlier at definition of the variable template?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this pattern is always illegal. But it's a big work (compared with this PR) to recognize this pattern (as something like current instantiation).

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It should be the same/similar as when we recognize the auto x= x +1 or int x = x + 1; (one is an error, the other is a warning).

This is probably fine if this isn't a primary template, right?

Generally, Clang templates try to diagnose things as absolute early as possible so we get ONE error instead of one-per-instantiation.

That said, it is perhaps worth spending some time to see if there is a reason we can't, or, by language rule, shouldn't diagnose this on auto p = p <T>;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems that [temp.res.general]/6 allows us to eagerly diagnose this...

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thanks, it looks much better

Diag(TemplateNameLoc,
diag::err_auto_variable_cannot_appear_in_own_initializer)
<< diag::ParsingInitFor::VarTemplate << Var << Var->getType()
<< SourceRange(TemplateNameLoc, TemplateArgs.getRAngleLoc());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm also wondering if it is necessary to draw underlines under the RHS of the declaration - would that be noisy if the declaration is too long?

Copy link
Contributor

@zyn0217 zyn0217 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! This looks nice

@erichkeane are you happy with the current state?

Copy link
Contributor

@cor3ntin cor3ntin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@cor3ntin
Copy link
Contributor

cor3ntin commented May 6, 2025

@zwuis Do you need me to merge that for you?

@zwuis
Copy link
Contributor Author

zwuis commented May 7, 2025

Yes. Please help me merge this PR.

Thank you for your review!

@zyn0217 zyn0217 merged commit 91f1830 into llvm:main May 7, 2025
7 of 11 checks passed
GeorgeARM pushed a commit to GeorgeARM/llvm-project that referenced this pull request May 7, 2025
@shafik
Copy link
Collaborator

shafik commented May 9, 2025

This was flagged in the following issue: #139067 (comment)

cor3ntin pushed a commit that referenced this pull request May 14, 2025
…primary variable template `std::format_kind` with libstdc++ (#139560)

#134522 triggers compilation error with libstdc++, in which primary
variable template `std::format_kind` is defined like

```cpp
template <typename R>
constexpr auto format_kind =
__primary_template_not_defined(
  format_kind<R>
);
```

See #139067 or <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=120190>.

This PR disables checking template id in initializer of primary variable
template `std::format_kind` in libstdc++ (by checking `__GLIBCXX__`).

Fixes #139067
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:frontend Language frontend issues, e.g. anything involving "Sema" clang Clang issues not falling into any other category
Projects
None yet
7 participants