Skip to content

[clang] Allow parentheses around CTAD declarators #132829

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 4 commits into from
Apr 11, 2025
Merged

Conversation

offsetof
Copy link
Contributor

Fixes #39811

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

llvmbot commented Mar 24, 2025

@llvm/pr-subscribers-clang

Author: None (offsetof)

Changes

Fixes #39811


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

5 Files Affected:

  • (modified) clang/docs/ReleaseNotes.rst (+2)
  • (modified) clang/include/clang/Basic/DiagnosticSemaKinds.td (+3-4)
  • (modified) clang/lib/Sema/SemaType.cpp (+2-7)
  • (modified) clang/test/Parser/cxx1z-class-template-argument-deduction.cpp (+4-4)
  • (modified) clang/test/SemaCXX/ctad.cpp (+26)
diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index f919b66dd0e41..24c65dbef5869 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -358,6 +358,8 @@ Bug Fixes to C++ Support
 - Fixed a Clang regression in C++20 mode where unresolved dependent call expressions were created inside non-dependent contexts (#GH122892)
 - Clang now emits the ``-Wunused-variable`` warning when some structured bindings are unused
   and the ``[[maybe_unused]]`` attribute is not applied. (#GH125810)
+- Declarations using class template argument deduction with redundant
+  parentheses around the declarator are no longer rejected. (#GH39811)
 
 Bug Fixes to AST Handling
 ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index c77cde297dc32..a19975f6d6bdf 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2612,10 +2612,9 @@ def err_decltype_auto_initializer_list : Error<
   "cannot deduce 'decltype(auto)' from initializer list">;
 
 // C++17 deduced class template specialization types
-def err_deduced_class_template_compound_type : Error<
-  "cannot %select{form pointer to|form reference to|form array of|"
-  "form function returning|use parentheses when declaring variable with}0 "
-  "deduced class template specialization type">;
+def err_deduced_class_template_compound_type
+    : Error<"cannot form %select{pointer to|reference to|array of|function "
+            "returning}0 deduced class template specialization type">;
 def err_deduced_non_class_or_alias_template_specialization_type : Error<
   "%select{<error>|function template|variable template|alias template|"
   "template template parameter|concept|template}0 %1 requires template "
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index aec33303780a0..16315d93ef8ce 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -4282,8 +4282,8 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
 
   // If T is 'decltype(auto)', the only declarators we can have are parens
   // and at most one function declarator if this is a function declaration.
-  // If T is a deduced class template specialization type, we can have no
-  // declarator chunks at all.
+  // If T is a deduced class template specialization type, only parentheses
+  // are allowed.
   if (auto *DT = T->getAs<DeducedType>()) {
     const AutoType *AT = T->getAs<AutoType>();
     bool IsClassTemplateDeduction = isa<DeducedTemplateSpecializationType>(DT);
@@ -4297,11 +4297,6 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
         unsigned DiagKind = 0;
         switch (DeclChunk.Kind) {
         case DeclaratorChunk::Paren:
-          // FIXME: Rejecting this is a little silly.
-          if (IsClassTemplateDeduction) {
-            DiagKind = 4;
-            break;
-          }
           continue;
         case DeclaratorChunk::Function: {
           if (IsClassTemplateDeduction) {
diff --git a/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp b/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
index a1594333abae7..d29eed40b1864 100644
--- a/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
+++ b/clang/test/Parser/cxx1z-class-template-argument-deduction.cpp
@@ -39,7 +39,7 @@ namespace template_template_arg_pack {
   template<typename...> struct YP {};
 
   struct Z { template<typename T> struct Q {}; }; // expected-note 2{{here}}
-  
+
   template<typename T> using ZId = Z;
 
   template<typename ...Ts> struct A {
@@ -152,7 +152,7 @@ namespace decl {
   A a;
   A b = 0;
   const A c = 0;
-  A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}}
+  A (parens) = 0;
   A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
   A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}}
   A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}}
@@ -179,7 +179,7 @@ namespace typename_specifier {
   }
   typename ::A a = 0;
   const typename ::A b = 0;
-  typename ::A (parens) = 0; // expected-error {{cannot use parentheses when declaring variable with deduced class template specialization type}}
+  typename ::A (parens) = 0;
   typename ::A *p = 0; // expected-error {{cannot form pointer to deduced class template specialization type}}
   typename ::A &r = *p; // expected-error {{cannot form reference to deduced class template specialization type}}
   typename ::A arr[3] = 0; // expected-error {{cannot form array of deduced class template specialization type}}
@@ -217,7 +217,7 @@ namespace typename_specifier {
 }
 
 namespace parenthesized {
-  template<typename T> struct X { X(T); };                    
+  template<typename T> struct X { X(T); };
   auto n = (X([]{}));
 }
 
diff --git a/clang/test/SemaCXX/ctad.cpp b/clang/test/SemaCXX/ctad.cpp
index 00a861d0f567c..d727c7e66c34c 100644
--- a/clang/test/SemaCXX/ctad.cpp
+++ b/clang/test/SemaCXX/ctad.cpp
@@ -1,5 +1,31 @@
 // RUN: %clang_cc1 -fsyntax-only -verify -Wno-unused-value -std=c++20 %s
 
+namespace GH39811 {
+
+template<int = 0> class C {};
+
+C (a);
+C (b) = C();
+C (c) {};
+C (((((d)))));
+
+template<C (e)> class X;
+template<C (...f)> class Y;
+
+void test() {
+    C (g);
+    C (h) = C();
+    C (i) {};
+    (void)g;
+    (void)h;
+    (void)i;
+}
+
+C* (bad1); // expected-error {{cannot form pointer to deduced class template specialization type}}
+C (*bad2); // expected-error {{cannot form pointer to deduced class template specialization type}}
+
+}
+
 namespace GH64347 {
 
 template<typename X, typename Y> struct A { X x; Y y;};

Copy link
Member

@Sirraide Sirraide left a comment

Choose a reason for hiding this comment

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

So the implementation of this seems fine, but looking at how other compilers handle this, GCC and MSVC accept this, whereas EDG rejects. I agree w/ the comment that that is a bit silly to reject this, but can you check what the standard has to say about this? If this is disallowed by the standard, we might want to issue an extension warning (disabled by default) here.

Copy link
Member

@Sirraide Sirraide left a comment

Choose a reason for hiding this comment

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

Actually, I just checked #39811, and apparently this was a CWG issue that has been resolved, so in that case this seems fine.

@Sirraide
Copy link
Member

CC @Endilll Should this also add a test to cwg23xx.cpp?

@cor3ntin
Copy link
Contributor

CC @Endilll Should this also add a test to cwg23xx.cpp?

Yes, please!

@offsetof offsetof requested a review from Endilll as a code owner March 25, 2025 16:06
@offsetof
Copy link
Contributor Author

Added a test for CWG2376.

@@ -379,6 +379,20 @@ class C {
};
} // namespace cwg2370

namespace cwg2376 { // cwg2376: 21
#if __cpp_deduction_guides >= 201703
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
#if __cpp_deduction_guides >= 201703
#if __cplusplus >= 201703L

Replace feature-test macro with `__cplusplus`
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, thanks. Will you need me to merge that for you?

@offsetof
Copy link
Contributor Author

offsetof commented Apr 9, 2025

Yes please, thank you @cor3ntin

@cor3ntin cor3ntin merged commit 9604bdf into llvm:main Apr 11, 2025
12 checks passed
var-const pushed a commit to ldionne/llvm-project that referenced this pull request Apr 17, 2025
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.

CTAD on variable with parenthesized initializer
5 participants