Skip to content
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

[clang] Exclude non-template classes when checking if constraint refers to containing template arguments #74265

Merged
merged 3 commits into from
Dec 7, 2023

Conversation

antangelo
Copy link
Contributor

When checking if the constraint uses any enclosing template parameters for [temp.friend]p9, if a containing record is used as argument, we assume that the constraint depends on enclosing template parameters without checking if the record is templated. The reproducer from the bug is included as a test case.

I would appreciate if someone could double check my interpretation of the standard here as I'm not the most experienced with it.

Fixes #71595

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:frontend Language frontend issues, e.g. anything involving "Sema" labels Dec 4, 2023
@llvmbot
Copy link
Collaborator

llvmbot commented Dec 4, 2023

@llvm/pr-subscribers-clang

Author: None (antangelo)

Changes

When checking if the constraint uses any enclosing template parameters for [temp.friend]p9, if a containing record is used as argument, we assume that the constraint depends on enclosing template parameters without checking if the record is templated. The reproducer from the bug is included as a test case.

I would appreciate if someone could double check my interpretation of the standard here as I'm not the most experienced with it.

Fixes #71595


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

3 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+3)
  • (modified) clang/lib/Sema/SemaTemplate.cpp (+2)
  • (added) clang/test/SemaTemplate/GH71595.cpp (+34)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index c7a948fd3fae5..852b908dd1594 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -651,6 +651,9 @@ Bug Fixes in This Version
 - Fixed false positive error emitted by clang when performing qualified name
   lookup and the current class instantiation has dependent bases.
   Fixes (`#13826 <https://github.com/llvm/llvm-project/issues/13826>`_)
+- Fix a ``clang-17`` regression where a templated friend with constraints is not 
+  properly applied when its parameters reference an enclosing non-template class.
+  Fixes (`#71595 <https://github.com/llvm/llvm-project/issues/71595>`_)
 
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 09bbf14d39af5..aec802b289a2d 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1714,6 +1714,8 @@ class ConstraintRefersToContainingTemplateChecker
   // Friend, likely because it was referred to without its template arguments.
   void CheckIfContainingRecord(const CXXRecordDecl *CheckingRD) {
     CheckingRD = CheckingRD->getMostRecentDecl();
+    if (!CheckingRD->isTemplated())
+      return;
 
     for (const DeclContext *DC = Friend->getLexicalDeclContext();
          DC && !DC->isFileContext(); DC = DC->getParent())
diff --git a/clang/test/SemaTemplate/GH71595.cpp b/clang/test/SemaTemplate/GH71595.cpp
new file mode 100644
index 0000000000000..7d34d1bf054e4
--- /dev/null
+++ b/clang/test/SemaTemplate/GH71595.cpp
@@ -0,0 +1,34 @@
+// RUN: %clang_cc1 -std=c++20 -verify %s
+
+template<class T, class U>
+concept C = true;
+
+class non_temp {
+    template<C<non_temp> T>
+    friend void f();
+
+    non_temp();
+};
+
+template<C<non_temp> T>
+void f() {
+    auto v = non_temp();
+}
+
+template<class A>
+class temp {
+    template<C<temp> T>
+    friend void g();
+
+    temp(); // expected-note {{implicitly declared private here}}
+};
+
+template<C<temp<int>> T>
+void g() {
+    auto v = temp<T>(); // expected-error {{calling a private constructor of class 'temp<int>'}}
+}
+
+void h() {
+    f<int>();
+    g<int>(); // expected-note {{in instantiation of function template specialization 'g<int>' requested here}}
+}

template<class A>
class temp {
template<C<temp> T>
friend void g();
Copy link
Collaborator

Choose a reason for hiding this comment

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

We should actually be erroring on this IIRC. The fact that this is not a definition should be an error. GCC says: error: friend function template with constraints that depend on outer template parameters must be a definition

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Do you want me to address this as part of this patch or as a followup?

Copy link
Collaborator

Choose a reason for hiding this comment

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

That can be a followup.

@@ -1714,6 +1714,8 @@ class ConstraintRefersToContainingTemplateChecker
// Friend, likely because it was referred to without its template arguments.
void CheckIfContainingRecord(const CXXRecordDecl *CheckingRD) {
CheckingRD = CheckingRD->getMostRecentDecl();
if (!CheckingRD->isTemplated())
Copy link
Collaborator

Choose a reason for hiding this comment

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

So this might not be the 'right' thing here. "isTemplated" doesn't mean "this is a template", it means "this, or something that contains it is a template".

So:

template <typename T>
struct Parent {
   struct Child {
    };
};

In this case, Parent is a class template, but Child is still isTemplated.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

What do you think about this example, which exercises this issue in the above case? https://godbolt.org/z/Yz1WEqM7M

If I switch from isTemplated() to getDescribedTemplate() to only check CheckingRD itself, then clang will accept that code. Otherwise, as the patch is now, the example is rejected. What should be the correct behavior?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hmm... I THINK that should be rejected, since you can't name Child without naming Parent? But I don't think the standard is explicit about that.

So perhaps isTemplated is what we want here.

I didn't think it too far through when making the comment, but I'm glad you put together a good example.

@antangelo antangelo merged commit fed4e3a into llvm:main Dec 7, 2023
5 checks passed
@antangelo antangelo deleted the constraint-friend branch December 7, 2023 01:18
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
Development

Successfully merging this pull request may close these issues.

[17 regression][C++20] templated friend not working if the concept use contains the enclosing class
3 participants