[clang] [clang] Implement CWG1878 "`operator auto` template" (PR #78103)

Vlad Serebrennikov via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 18 04:51:08 PST 2024


https://github.com/Endilll updated https://github.com/llvm/llvm-project/pull/78103

>From 522c7dff31a6f63995877674f9f4282ae60f7aaa Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 14 Jan 2024 19:45:04 +0300
Subject: [PATCH 1/4] [clang] Implement CWG1878 "`operator auto` template"

C++14 introduced deduced return type for regular functions, but shortly after [CWG1878](https://wg21.link/cwg1878) was filed and resolved to disallow deduced return types in conversion function templates. So this patch diagnoses such usage of deduced return type in C++14 mode onwards.
---
 clang/docs/ReleaseNotes.rst                   |  2 ++
 .../clang/Basic/DiagnosticSemaKinds.td        |  3 +-
 clang/lib/Sema/SemaDeclCXX.cpp                | 10 ++++++-
 clang/test/CXX/drs/dr18xx.cpp                 | 26 +++++++++++++----
 .../SemaCXX/deduced-return-type-cxx14.cpp     | 28 +++++++++++++++----
 clang/www/cxx_dr_status.html                  |  2 +-
 6 files changed, 57 insertions(+), 14 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index 3cbce1be159437..271e641a5ccce0 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -560,6 +560,8 @@ Improvements to Clang's diagnostics
 - Clang now diagnoses unexpanded packs within the template argument lists of function template specializations.
 - Clang now diagnoses attempts to bind a bitfield to an NTTP of a reference type as erroneous
   converted constant expression and not as a reference to subobject.
+- Clang now diagnoses ``auto`` and ``decltype(auto)`` in declarations of conversion function template
+  (`CWG1878: <https://cplusplus.github.io/CWG/issues/1878.html>`_)
 
 
 Improvements to Clang's time-trace
diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td
index 1a79892e40030a..de99d753a13a6c 100644
--- a/clang/include/clang/Basic/DiagnosticSemaKinds.td
+++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td
@@ -2411,7 +2411,8 @@ def err_auto_not_allowed : Error<
   "|in type allocated by 'new'|in K&R-style function parameter"
   "|in template parameter|in friend declaration|in function prototype that is "
   "not a function declaration|in requires expression parameter"
-  "|in array declaration}1">;
+  "|in array declaration"
+  "|in declaration of conversion function template}1">;
 def err_dependent_deduced_tst : Error<
   "typename specifier refers to "
   "%select{class template|function template|variable template|alias template|"
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 36e53c684ac4dc..613dccbf445f91 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11323,8 +11323,16 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
   }
 
   if (FunctionTemplateDecl *ConversionTemplate
-                                = Conversion->getDescribedFunctionTemplate())
+                                = Conversion->getDescribedFunctionTemplate()) {
+    if (ConvType->isUndeducedAutoType()) {
+      Diag(Conversion->getTypeSpecStartLoc(), diag::err_auto_not_allowed)
+        << Conversion->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc().getSourceRange()
+        << llvm::to_underlying(Conversion->getConversionType()->getAs<AutoType>()->getKeyword())
+        << /* in declaration of conversion function template */ 24;
+    }
+
     return ConversionTemplate;
+  }
 
   return Conversion;
 }
diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp
index 175c39e8b73abb..8be94ed49e639d 100644
--- a/clang/test/CXX/drs/dr18xx.cpp
+++ b/clang/test/CXX/drs/dr18xx.cpp
@@ -1,10 +1,10 @@
 // RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx98 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx11-17,since-cxx11 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,cxx98-14,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
-// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,cxx98-14,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,cxx11-17,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx14,since-cxx17,since-cxx20,since-cxx11,since-cxx14 -fexceptions -Wno-deprecated-builtins -fcxx-exceptions -pedantic-errors
 
 #if __cplusplus == 199711L
 #define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
@@ -317,6 +317,22 @@ namespace dr1872 { // dr1872: 9
 #endif
 }
 
+namespace dr1878 { // dr1878: 18
+#if __cplusplus >= 201402L
+struct S {
+  template <typename T>
+  operator auto() const { return short(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator const auto() const { return int(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator decltype(auto)() const { return unsigned(); }
+  // since-cxx14-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+};
+#endif
+}
+
 namespace dr1881 { // dr1881: 7
   struct A { int a : 4; };
   struct B : A { int b : 3; };
diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
index eac9c587869f55..6533a9e561f248 100644
--- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
+++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
@@ -1,11 +1,11 @@
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,cxx20_23,cxx23    %s
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,cxx20_23,cxx23    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx14,cxx20_23,cxx23    %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx14,cxx20_23,cxx23    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_23 %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,cxx14_20,cxx20_23 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx20_23 %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx20_23 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14    %s
-// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,cxx14_20,cxx14    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx14    %s
+// RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx14    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
 auto f(); // expected-note {{previous}}
 int f(); // expected-error {{differ only in their return type}}
@@ -686,3 +686,19 @@ auto f(auto x) { // cxx14-error {{'auto' not allowed in function prototype}}
 }
 
 }
+
+struct DeducedTargetTypeOfConversionFunction {
+  operator auto() const { return char(); }
+  operator const auto() const { return float(); }
+  operator decltype(auto)() const { return double(0); }
+
+  template <typename T>
+  operator auto() const { return short(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator const auto() const { return int(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator decltype(auto)() const { return unsigned(); }
+  // since-cxx14-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+};
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 397bf1357d3cb3..c0b48193694230 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -11076,7 +11076,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/1878.html">1878</a></td>
     <td>CD4</td>
     <td><TT>operator auto</TT> template</td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="unreleased" align="center">Clang 18</td>
   </tr>
   <tr id="1879">
     <td><a href="https://cplusplus.github.io/CWG/issues/1879.html">1879</a></td>

>From 3c43f0fc34a6fea2484aebc74b87e9537d435c7e Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Sun, 14 Jan 2024 19:54:06 +0300
Subject: [PATCH 2/4] Run clang-format

---
 clang/lib/Sema/SemaDeclCXX.cpp | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 613dccbf445f91..0e7a43859c1aad 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11322,13 +11322,18 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
       << ClassType << ConvType;
   }
 
-  if (FunctionTemplateDecl *ConversionTemplate
-                                = Conversion->getDescribedFunctionTemplate()) {
+  if (FunctionTemplateDecl *ConversionTemplate =
+          Conversion->getDescribedFunctionTemplate()) {
     if (ConvType->isUndeducedAutoType()) {
       Diag(Conversion->getTypeSpecStartLoc(), diag::err_auto_not_allowed)
-        << Conversion->getTypeSourceInfo()->getTypeLoc().getContainedAutoTypeLoc().getSourceRange()
-        << llvm::to_underlying(Conversion->getConversionType()->getAs<AutoType>()->getKeyword())
-        << /* in declaration of conversion function template */ 24;
+          << Conversion->getTypeSourceInfo()
+                 ->getTypeLoc()
+                 .getContainedAutoTypeLoc()
+                 .getSourceRange()
+          << llvm::to_underlying(Conversion->getConversionType()
+                                     ->getAs<AutoType>()
+                                     ->getKeyword())
+          << /* in declaration of conversion function template */ 24;
     }
 
     return ConversionTemplate;

>From 119ab093ec8b2a1e2f3f4f009fafb86b4d9ee172 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Wed, 17 Jan 2024 14:38:14 +0300
Subject: [PATCH 3/4] Simplify extracting source range info for return type

---
 clang/lib/Sema/SemaDeclCXX.cpp | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0e7a43859c1aad..0b05c75a12b98c 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11326,10 +11326,7 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
           Conversion->getDescribedFunctionTemplate()) {
     if (ConvType->isUndeducedAutoType()) {
       Diag(Conversion->getTypeSpecStartLoc(), diag::err_auto_not_allowed)
-          << Conversion->getTypeSourceInfo()
-                 ->getTypeLoc()
-                 .getContainedAutoTypeLoc()
-                 .getSourceRange()
+          << getReturnTypeLoc(Conversion).getSourceRange()
           << llvm::to_underlying(Conversion->getConversionType()
                                      ->getAs<AutoType>()
                                      ->getKeyword())

>From 5558f5511790452b0a4ef605622a5995c107a945 Mon Sep 17 00:00:00 2001
From: Vlad Serebrennikov <serebrennikov.vladislav at gmail.com>
Date: Thu, 18 Jan 2024 15:50:49 +0300
Subject: [PATCH 4/4] Address Corentin's feedback

Add tests with constrained auto and references, adapt diagnostic emitter accordingly.
---
 clang/lib/Sema/SemaDeclCXX.cpp                |  1 +
 clang/test/CXX/drs/dr18xx.cpp                 | 16 ++++++++++
 .../SemaCXX/deduced-return-type-cxx14.cpp     | 32 ++++++++++++++++---
 3 files changed, 44 insertions(+), 5 deletions(-)

diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0b05c75a12b98c..2411ba50f23f04 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11328,6 +11328,7 @@ Decl *Sema::ActOnConversionDeclarator(CXXConversionDecl *Conversion) {
       Diag(Conversion->getTypeSpecStartLoc(), diag::err_auto_not_allowed)
           << getReturnTypeLoc(Conversion).getSourceRange()
           << llvm::to_underlying(Conversion->getConversionType()
+                                     ->getContainedDeducedType()
                                      ->getAs<AutoType>()
                                      ->getKeyword())
           << /* in declaration of conversion function template */ 24;
diff --git a/clang/test/CXX/drs/dr18xx.cpp b/clang/test/CXX/drs/dr18xx.cpp
index 8be94ed49e639d..e8bbd8e936a1a1 100644
--- a/clang/test/CXX/drs/dr18xx.cpp
+++ b/clang/test/CXX/drs/dr18xx.cpp
@@ -319,6 +319,11 @@ namespace dr1872 { // dr1872: 9
 
 namespace dr1878 { // dr1878: 18
 #if __cplusplus >= 201402L
+#if __cplusplus >= 202002L
+template <typename T>
+concept C = true;
+#endif
+
 struct S {
   template <typename T>
   operator auto() const { return short(); }
@@ -327,8 +332,19 @@ struct S {
   operator const auto() const { return int(); }
   // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
   template <typename T>
+  operator const auto&() const { return char(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
   operator decltype(auto)() const { return unsigned(); }
   // since-cxx14-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+#if __cplusplus >= 202002L
+  template <typename T>
+  operator C auto() const { return float(); }
+  // since-cxx20-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator C decltype(auto)() const { return double(); }
+  // since-cxx20-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+#endif
 };
 #endif
 }
diff --git a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
index 6533a9e561f248..c0d43911b8c717 100644
--- a/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
+++ b/clang/test/SemaCXX/deduced-return-type-cxx14.cpp
@@ -1,8 +1,8 @@
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx14,cxx20_23,cxx23    %s
-// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx14,cxx20_23,cxx23    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx20,since-cxx14,cxx20_23,cxx23    %s
+// RUN: %clang_cc1 -std=c++23 -fsyntax-only -verify=expected,since-cxx20,since-cxx14,cxx20_23,cxx23    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx20_23 %s
-// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx20_23 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx20,since-cxx14,cxx14_20,cxx20_23 %s
+// RUN: %clang_cc1 -std=c++20 -fsyntax-only -verify=expected,since-cxx20,since-cxx14,cxx14_20,cxx20_23 %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
 
 // RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx14    %s
 // RUN: %clang_cc1 -std=c++14 -fsyntax-only -verify=expected,since-cxx14,cxx14_20,cxx14    %s -fdelayed-template-parsing -DDELAYED_TEMPLATE_PARSING
@@ -687,10 +687,21 @@ auto f(auto x) { // cxx14-error {{'auto' not allowed in function prototype}}
 
 }
 
+#if __cplusplus >= 202002L
+template <typename T>
+concept C = true;
+#endif
+
 struct DeducedTargetTypeOfConversionFunction {
   operator auto() const { return char(); }
   operator const auto() const { return float(); }
-  operator decltype(auto)() const { return double(0); }
+  operator const auto&() const { return int(); }
+  // expected-warning at -1 {{returning reference to local temporary object}}
+  operator decltype(auto)() const { return double(); }
+#if __cplusplus >= 202002L
+  operator C auto() const { return unsigned(); }
+  operator C decltype(auto)() const { return long(); }
+#endif
 
   template <typename T>
   operator auto() const { return short(); }
@@ -699,6 +710,17 @@ struct DeducedTargetTypeOfConversionFunction {
   operator const auto() const { return int(); }
   // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
   template <typename T>
+  operator const auto&() const { return char(); }
+  // since-cxx14-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
   operator decltype(auto)() const { return unsigned(); }
   // since-cxx14-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+#if __cplusplus >= 202002L
+  template <typename T>
+  operator C auto() const { return float(); }
+  // since-cxx20-error at -1 {{'auto' not allowed in declaration of conversion function template}}
+  template <typename T>
+  operator C decltype(auto)() const { return double(); }
+  // since-cxx20-error at -1 {{'decltype(auto)' not allowed in declaration of conversion function template}}
+#endif
 };



More information about the cfe-commits mailing list