[clang] [Clang] Qualified functions can't decay into pointers (PR #90353)

Mital Ashok via cfe-commits cfe-commits at lists.llvm.org
Fri Jul 26 07:22:06 PDT 2024


https://github.com/MitalAshok updated https://github.com/llvm/llvm-project/pull/90353

>From d983badd09dcc227f5945f4b4759214b7b6adbf5 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Fri, 26 Jul 2024 08:44:19 +0100
Subject: [PATCH 1/5] [Clang] Qualified functions can't decay into pointers

---
 clang/docs/ReleaseNotes.rst                   |  3 ++
 clang/include/clang/AST/Type.h                |  4 +-
 clang/lib/AST/TypePrinter.cpp                 | 23 ++++++++++
 clang/lib/Sema/SemaDecl.cpp                   |  7 +++
 clang/lib/Sema/SemaDeclCXX.cpp                | 16 +++++--
 clang/lib/Sema/SemaTemplate.cpp               | 10 +++-
 clang/lib/Sema/SemaTemplateDeductionGuide.cpp | 14 +++++-
 clang/lib/Sema/SemaType.cpp                   | 46 +++++++------------
 .../dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp    |  5 +-
 .../CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp   |  5 +-
 clang/test/SemaCXX/function-type-qual.cpp     | 36 ++++++++++++++-
 clang/test/SemaCXX/type-traits.cpp            | 12 +++++
 12 files changed, 138 insertions(+), 43 deletions(-)

diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst
index b165f6e65636d..f25f178bccf7f 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -145,6 +145,9 @@ Bug Fixes in This Version
 - Fixed the definition of ``ATOMIC_FLAG_INIT`` in ``<stdatomic.h>`` so it can
   be used in C++.
 
+- cv- and ref- qualified function types no longer silently produce invalid pointer to
+  qualified function types when they implicitly decay in some places. Fixes (#GH27059).
+
 Bug Fixes to Compiler Builtins
 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index 72723c7c56e07..fa5dac96b996d 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -5377,6 +5377,8 @@ class FunctionProtoType final
     return static_cast<RefQualifierKind>(FunctionTypeBits.RefQualifier);
   }
 
+  std::string getFunctionQualifiersAsString() const;
+
   using param_type_iterator = const QualType *;
 
   ArrayRef<QualType> param_types() const {
@@ -7758,7 +7760,7 @@ inline bool QualType::isReferenceable() const {
   if (const auto *F = Self.getAs<FunctionProtoType>())
     return F->getMethodQuals().empty() && F->getRefQualifier() == RQ_None;
 
-  return false;
+  return Self.isFunctionType();
 }
 
 inline SplitQualType QualType::split() const {
diff --git a/clang/lib/AST/TypePrinter.cpp b/clang/lib/AST/TypePrinter.cpp
index ffec3ef9d2269..499e049923e2f 100644
--- a/clang/lib/AST/TypePrinter.cpp
+++ b/clang/lib/AST/TypePrinter.cpp
@@ -2605,3 +2605,26 @@ raw_ostream &clang::operator<<(raw_ostream &OS, QualType QT) {
   TypePrinter(LangOptions()).print(S.Ty, S.Quals, OS, /*PlaceHolder=*/"");
   return OS;
 }
+
+std::string FunctionProtoType::getFunctionQualifiersAsString() const {
+  std::string Quals = getMethodQuals().getAsString();
+
+  switch (getRefQualifier()) {
+  case RQ_None:
+    break;
+
+  case RQ_LValue:
+    if (!Quals.empty())
+      Quals += ' ';
+    Quals += '&';
+    break;
+
+  case RQ_RValue:
+    if (!Quals.empty())
+      Quals += ' ';
+    Quals += "&&";
+    break;
+  }
+
+  return Quals;
+}
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 575bd292f27de..123dc46c7d884 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15071,6 +15071,13 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
     T = Context.getLifetimeQualifiedType(T, lifetime);
   }
 
+  if (T->isFunctionType() && !T.isReferenceable()) {
+    Diag(NameLoc, diag::err_compound_qualified_function_type)
+        << 1 << true << T
+        << T->castAs<FunctionProtoType>()->getFunctionQualifiersAsString();
+    return nullptr;
+  }
+
   ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
                                          Context.getAdjustedParameterType(T),
                                          TSInfo, SC, nullptr);
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 1cca8ac9b9343..58b21a1d6f33e 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -11123,7 +11123,8 @@ void Sema::CheckConversionDeclarator(Declarator &D, QualType &R,
     D.setInvalidType();
   } else if (ConvType->isFunctionType()) {
     Diag(D.getIdentifierLoc(), diag::err_conv_function_to_function);
-    ConvType = Context.getPointerType(ConvType);
+    if (ConvType.isReferenceable())
+      ConvType = Context.getPointerType(ConvType);
     D.setInvalidType();
   }
 
@@ -16719,8 +16720,17 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo,
   // Arrays and functions decay.
   if (ExDeclType->isArrayType())
     ExDeclType = Context.getArrayDecayedType(ExDeclType);
-  else if (ExDeclType->isFunctionType())
-    ExDeclType = Context.getPointerType(ExDeclType);
+  else if (ExDeclType->isFunctionType()) {
+    if (ExDeclType.isReferenceable())
+      ExDeclType = Context.getPointerType(ExDeclType);
+    else {
+      Diag(Loc, diag::err_compound_qualified_function_type)
+          << 1 << true << ExDeclType
+          << ExDeclType->castAs<FunctionProtoType>()
+                 ->getFunctionQualifiersAsString();
+      Invalid = true;
+    }
+  }
 
   // C++ 15.3p1: The exception-declaration shall not denote an incomplete type.
   // The exception-declaration shall not denote a pointer or reference to an
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 87b1f98bbe5ac..7c38c594e809f 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1412,8 +1412,16 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
   //   A non-type template-parameter of type "array of T" or
   //   "function returning T" is adjusted to be of type "pointer to
   //   T" or "pointer to function returning T", respectively.
-  if (T->isArrayType() || T->isFunctionType())
+  if (T->isArrayType() || T->isFunctionType()) {
+    if (!T.isReferenceable()) {
+      // Pointer to cv- or ref- qualified type will be invalid
+      Diag(Loc, diag::err_compound_qualified_function_type)
+          << 1 << true << T
+          << T->castAs<FunctionProtoType>()->getFunctionQualifiersAsString();
+      return QualType();
+    }
     return Context.getDecayedType(T);
+  }
 
   // If T is a dependent type, we can't do the check now, so we
   // assume that it is well-formed. Note that stripping off the
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 0602d07c6b9b0..0d17bd2094e0e 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -620,8 +620,20 @@ struct ConvertConstructorToDeductionGuideTransform {
     }
     // Handle arrays and functions decay.
     auto NewType = NewDI->getType();
-    if (NewType->isArrayType() || NewType->isFunctionType())
+    if (NewType->isArrayType())
       NewType = SemaRef.Context.getDecayedType(NewType);
+    else if (NewType->isFunctionType()) {
+      // Reject cv- and ref-qualified function
+      if (!NewType.isReferenceable()) {
+        SemaRef.Diag(OldParam->getLocation(),
+                     diag::err_compound_qualified_function_type)
+            << 1 << true << NewType
+            << cast<FunctionProtoType>(NewType)
+                   ->getFunctionQualifiersAsString();
+        return nullptr;
+      }
+      NewType = SemaRef.Context.getDecayedType(NewType);
+    }
 
     ParmVarDecl *NewParam = ParmVarDecl::Create(
         SemaRef.Context, DC, OldParam->getInnerLocStart(),
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 6fa39cdccef2b..8b706a58aeeef 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1707,29 +1707,6 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type,
   return S.Context.getQualifiedType(type, qs);
 }
 
-static std::string getFunctionQualifiersAsString(const FunctionProtoType *FnTy){
-  std::string Quals = FnTy->getMethodQuals().getAsString();
-
-  switch (FnTy->getRefQualifier()) {
-  case RQ_None:
-    break;
-
-  case RQ_LValue:
-    if (!Quals.empty())
-      Quals += ' ';
-    Quals += '&';
-    break;
-
-  case RQ_RValue:
-    if (!Quals.empty())
-      Quals += ' ';
-    Quals += "&&";
-    break;
-  }
-
-  return Quals;
-}
-
 namespace {
 /// Kinds of declarator that cannot contain a qualified function type.
 ///
@@ -1755,8 +1732,8 @@ static bool checkQualifiedFunction(Sema &S, QualType T, SourceLocation Loc,
     return false;
 
   S.Diag(Loc, diag::err_compound_qualified_function_type)
-    << QFK << isa<FunctionType>(T.IgnoreParens()) << T
-    << getFunctionQualifiersAsString(FPT);
+      << QFK << isa<FunctionType>(T.IgnoreParens()) << T
+      << FPT->getFunctionQualifiersAsString();
   return true;
 }
 
@@ -1767,7 +1744,7 @@ bool Sema::CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc) {
     return false;
 
   Diag(Loc, diag::err_qualified_function_typeid)
-      << T << getFunctionQualifiersAsString(FPT);
+      << T << FPT->getFunctionQualifiersAsString();
   return true;
 }
 
@@ -2612,7 +2589,16 @@ QualType Sema::BuildFunctionType(QualType T,
 
   for (unsigned Idx = 0, Cnt = ParamTypes.size(); Idx < Cnt; ++Idx) {
     // FIXME: Loc is too inprecise here, should use proper locations for args.
-    QualType ParamType = Context.getAdjustedParameterType(ParamTypes[Idx]);
+    QualType ParamType = ParamTypes[Idx];
+    if (ParamType->isFunctionType() && !ParamType.isReferenceable()) {
+      Diag(Loc, diag::err_compound_qualified_function_type)
+          << 1 << true << ParamType
+          << ParamType->castAs<FunctionProtoType>()
+                 ->getFunctionQualifiersAsString();
+      Invalid = true;
+    } else
+      ParamType = Context.getAdjustedParameterType(ParamType);
+
     if (ParamType->isVoidType()) {
       Diag(Loc, diag::err_param_with_void_type);
       Invalid = true;
@@ -5507,9 +5493,9 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
       }
 
       S.Diag(Loc, diag::err_invalid_qualified_function_type)
-        << Kind << D.isFunctionDeclarator() << T
-        << getFunctionQualifiersAsString(FnTy)
-        << FixItHint::CreateRemoval(RemovalRange);
+          << Kind << D.isFunctionDeclarator() << T
+          << FnTy->getFunctionQualifiersAsString()
+          << FixItHint::CreateRemoval(RemovalRange);
 
       // Strip the cv-qualifiers and ref-qualifiers from the type.
       FunctionProtoType::ExtProtoInfo EPI = FnTy->getExtProtoInfo();
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
index d93cc8b90874d..8f01406f8bae5 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
@@ -53,11 +53,10 @@ void (X::*mpf2)() && = &X::f1;
 
 void (f() &&); // expected-error{{non-member function cannot have '&&' qualifier}}
 
-// FIXME: These are ill-formed.
 template<typename T> struct pass {
-  void f(T);
+  void f(T); // expected-error {{pointer to function type cannot have '&' qualifier}}
 };
-pass<func_type_lvalue> pass0;
+pass<func_type_lvalue> pass0; // expected-note {{in instantiation of template class 'pass<void () &>' requested here}}
 pass<func_type_lvalue> pass1;
 
 template<typename T, typename U> struct is_same { static const bool value = false; };
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
index a035086c9a127..82b2f8102217e 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
@@ -26,8 +26,7 @@ template<typename T> struct S {
 };
 S<F> s; // expected-note {{in instantiation of}}
 
-// FIXME: This is ill-formed.
 template<typename T> struct U {
-  void f(T);
+  void f(T); // expected-error {{pointer to function type cannot have 'const' qualifier}}
 };
-U<F> u;
+U<F> u; // expected-note {{in instantiation of}}
diff --git a/clang/test/SemaCXX/function-type-qual.cpp b/clang/test/SemaCXX/function-type-qual.cpp
index f4906f58abbae..aaf91aa6b9a18 100644
--- a/clang/test/SemaCXX/function-type-qual.cpp
+++ b/clang/test/SemaCXX/function-type-qual.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify %s
 
 void f() const; // expected-error {{non-member function cannot have 'const' qualifier}}
 void (*pf)() const; // expected-error {{pointer to function type cannot have 'const' qualifier}}
@@ -7,6 +7,9 @@ extern void (&rf)() const; // expected-error {{reference to function type cannot
 typedef void cfn() const;
 cfn f2; // expected-error {{non-member function of type 'cfn' (aka 'void () const') cannot have 'const' qualifier}}
 
+void decay1(void p() const); // expected-error {{non-member function cannot have 'const' qualifier}}
+void decay2(cfn p); // expected-error {{non-member function of type 'cfn' (aka 'void () const') cannot have 'const' qualifier}}
+
 class C {
   void f() const;
   cfn f2;
@@ -55,3 +58,34 @@ struct B {
   void operator delete[](void*) volatile; //expected-error {{static member function cannot have 'volatile' qualifier}}
 };
 }
+
+namespace GH27059 {
+template<typename T> int f(T); // #GH27059-f
+template<typename T, T> int g(); // #GH27059-g
+int x = f<void () const>(nullptr);
+// expected-error at -1 {{no matching function for call to 'f'}}
+//   expected-note@#GH27059-f {{candidate template ignored: substitution failure [with T = void () const]: pointer to function type cannot have 'const' qualifier}}
+int y = g<void () const, nullptr>();
+// expected-error at -1 {{no matching function for call to 'g'}}
+//   expected-note@#GH27059-g {{invalid explicitly-specified argument for 2nd template parameter}}
+
+template<typename T> int ff(void p(T)); // #GH27059-ff
+template<typename T, void(T)> int gg(); // #GH27059-gg
+int xx = ff<void () const>(nullptr);
+// expected-error at -1 {{no matching function for call to 'ff'}}
+//   expected-note@#GH27059-ff {{candidate template ignored: substitution failure [with T = void () const]: pointer to function type cannot have 'const' qualifier}}
+int yy = gg<void () const, nullptr>();
+// expected-error at -1 {{no matching function for call to 'gg'}}
+//   expected-note@#GH27059-gg {{invalid explicitly-specified argument for 2nd template parameter}}
+
+template<typename T>
+void catch_fn() {
+  try {
+  } catch (T) { // #GH27059-catch_fn
+  }
+}
+template void catch_fn<void()>();
+template void catch_fn<void() const>();
+// expected-error@#GH27059-catch_fn {{pointer to function type cannot have 'const' qualifier}}
+//   expected-note at -2 {{in instantiation of function template specialization 'GH27059::catch_fn<void () const>' requested here}}
+}
diff --git a/clang/test/SemaCXX/type-traits.cpp b/clang/test/SemaCXX/type-traits.cpp
index e131212bb1071..adb72b36de503 100644
--- a/clang/test/SemaCXX/type-traits.cpp
+++ b/clang/test/SemaCXX/type-traits.cpp
@@ -4353,6 +4353,12 @@ void add_pointer() {
   static_assert(__is_same(add_pointer_t<int()>, int (*)()));
   static_assert(__is_same(add_pointer_t<int (*)()>, int (**)()));
   static_assert(__is_same(add_pointer_t<int (&)()>, int (*)()));
+  static_assert(__is_same(add_pointer_t<int () const>, int () const));
+  static_assert(__is_same(add_pointer_t<int () &>, int () &));
+  static_assert(__is_same(add_pointer_t<int[]>, int(*)[]));
+  static_assert(__is_same(add_pointer_t<int[1]>, int(*)[1]));
+  static_assert(__is_same(add_pointer_t<int(&)[1]>, int(*)[1]));
+  static_assert(__is_same(add_pointer_t<int(&&)[1]>, int(*)[1]));
 
   static_assert(__is_same(add_pointer_t<S>, S *));
   static_assert(__is_same(add_pointer_t<const S>, const S *));
@@ -4557,6 +4563,12 @@ void check_decay() {
   static_assert(__is_same(decay_t<int (&)()>, int (*)()));
   static_assert(__is_same(decay_t<IntAr>, int *));
   static_assert(__is_same(decay_t<IntArNB>, int *));
+  static_assert(__is_same(decay_t<int () const>, int () const));
+  static_assert(__is_same(decay_t<int () &>, int () &));
+  static_assert(__is_same(decay_t<int[]>, int*));
+  static_assert(__is_same(decay_t<int[1]>, int*));
+  static_assert(__is_same(decay_t<int(&)[1]>, int*));
+  static_assert(__is_same(decay_t<int(&&)[1]>, int*));
 
   static_assert(__is_same(decay_t<S>, S));
   static_assert(__is_same(decay_t<S &>, S));

>From 27c4c0f8443f6f7db47130a69e2b3bf6929c9fbb Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Fri, 26 Jul 2024 12:02:40 +0100
Subject: [PATCH 2/5] Create helper functions
 Sema::CheckQualifiedFunctionForPointer and FunctionProtoType::hasQualifiers

Also add more tests
---
 clang/include/clang/AST/Type.h                |  7 ++-
 clang/include/clang/Sema/Sema.h               | 17 +++++++
 clang/lib/AST/ASTContext.cpp                  | 14 ++++++
 clang/lib/AST/MicrosoftMangle.cpp             |  2 +-
 clang/lib/Sema/SemaDecl.cpp                   |  6 +--
 clang/lib/Sema/SemaDeclCXX.cpp                | 11 ++---
 clang/lib/Sema/SemaTemplate.cpp               | 12 +++--
 clang/lib/Sema/SemaTemplateDeductionGuide.cpp |  9 +---
 clang/lib/Sema/SemaType.cpp                   | 45 +++++--------------
 .../dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp    |  2 +-
 .../CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp   |  2 +-
 clang/test/SemaCXX/function-type-qual.cpp     | 23 +++++++++-
 clang/test/SemaTemplate/ctad.cpp              |  1 +
 13 files changed, 84 insertions(+), 67 deletions(-)

diff --git a/clang/include/clang/AST/Type.h b/clang/include/clang/AST/Type.h
index fa5dac96b996d..40db997dac211 100644
--- a/clang/include/clang/AST/Type.h
+++ b/clang/include/clang/AST/Type.h
@@ -5377,6 +5377,11 @@ class FunctionProtoType final
     return static_cast<RefQualifierKind>(FunctionTypeBits.RefQualifier);
   }
 
+  // Whether this is a qualified function (i.e., the type of a member function)
+  bool hasQualifiers() const {
+    return getMethodQuals() || getRefQualifier() != RQ_None;
+  }
+
   std::string getFunctionQualifiersAsString() const;
 
   using param_type_iterator = const QualType *;
@@ -7758,7 +7763,7 @@ inline bool QualType::isReferenceable() const {
   if (Self.isObjectType() || Self.isReferenceType())
     return true;
   if (const auto *F = Self.getAs<FunctionProtoType>())
-    return F->getMethodQuals().empty() && F->getRefQualifier() == RQ_None;
+    return !F->hasQualifiers();
 
   return Self.isFunctionType();
 }
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 2ec6367eccea0..5ab03c878d6bd 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14644,6 +14644,23 @@ class Sema final : public SemaBase {
   QualType BuildAddressSpaceAttr(QualType &T, Expr *AddrSpace,
                                  SourceLocation AttrLoc);
 
+  /// Kinds of declarator that cannot contain a qualified function type.
+  ///
+  /// C++98 [dcl.fct]p4 / C++11 [dcl.fct]p6:
+  ///     a function type with a cv-qualifier or a ref-qualifier can only appear
+  ///     at the topmost level of a type.
+  ///
+  /// Parens and member pointers are permitted. We don't diagnose array and
+  /// function declarators, because they don't allow function types at all.
+  ///
+  /// The values of this enum are used in diagnostics.
+  enum QualifiedFunctionKind { QFK_BlockPointer, QFK_Pointer, QFK_Reference };
+
+  /// Check whether the type T is a qualified function type, and if it is,
+  /// diagnose that it cannot be contained within the given kind of declarator.
+  bool CheckQualifiedFunctionForPointer(QualType T, SourceLocation Loc,
+                                        QualifiedFunctionKind QFK);
+
   bool CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc);
 
   bool CheckFunctionReturnType(QualType T, SourceLocation Loc);
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index a465cdfcf3c89..6a52af87febf2 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3649,6 +3649,20 @@ QualType ASTContext::getComplexType(QualType T) const {
 /// getPointerType - Return the uniqued reference to the type for a pointer to
 /// the specified type.
 QualType ASTContext::getPointerType(QualType T) const {
+#ifndef NDEBUG
+  {
+    assert(!T->isReferenceType() &&
+           "Attempting to create pointer to reference type");
+    // FIXME: Get rid of creating pointers to qualified function types. It is
+    // used as an intermediary for building a CallExpr that is eventually
+    // converted into a CXXMemberCallExpr (and loses the pointer type)
+    // if (auto *FPT = T->getAs<FunctionProtoType>())
+    //   assert(!FPT->hasQualifiers() &&
+    //          "Attempting to create pointer to qualified function type; Use "
+    //          "Sema::CheckQualifiedFunctionForPointer to check for and "
+    //          "diagnose this");
+  }
+#endif
   // Unique pointers, to guarantee there is only one pointer of a particular
   // structure.
   llvm::FoldingSetNodeID ID;
diff --git a/clang/lib/AST/MicrosoftMangle.cpp b/clang/lib/AST/MicrosoftMangle.cpp
index e0d7c01ca3351..d3c94d65dc207 100644
--- a/clang/lib/AST/MicrosoftMangle.cpp
+++ b/clang/lib/AST/MicrosoftMangle.cpp
@@ -2790,7 +2790,7 @@ void MicrosoftCXXNameMangler::mangleType(const FunctionProtoType *T, Qualifiers,
   // Structors only appear in decls, so at this point we know it's not a
   // structor type.
   // FIXME: This may not be lambda-friendly.
-  if (T->getMethodQuals() || T->getRefQualifier() != RQ_None) {
+  if (T->hasQualifiers()) {
     Out << "$$A8@@";
     mangleFunctionType(T, /*D=*/nullptr, /*ForceThisQuals=*/true);
   } else {
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 123dc46c7d884..12d3063c95fb0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -15071,12 +15071,8 @@ ParmVarDecl *Sema::CheckParameter(DeclContext *DC, SourceLocation StartLoc,
     T = Context.getLifetimeQualifiedType(T, lifetime);
   }
 
-  if (T->isFunctionType() && !T.isReferenceable()) {
-    Diag(NameLoc, diag::err_compound_qualified_function_type)
-        << 1 << true << T
-        << T->castAs<FunctionProtoType>()->getFunctionQualifiersAsString();
+  if (CheckQualifiedFunctionForPointer(T, NameLoc, QFK_Pointer))
     return nullptr;
-  }
 
   ParmVarDecl *New = ParmVarDecl::Create(Context, DC, StartLoc, NameLoc, Name,
                                          Context.getAdjustedParameterType(T),
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 58b21a1d6f33e..2902b5e6aff34 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -16721,15 +16721,10 @@ VarDecl *Sema::BuildExceptionDeclaration(Scope *S, TypeSourceInfo *TInfo,
   if (ExDeclType->isArrayType())
     ExDeclType = Context.getArrayDecayedType(ExDeclType);
   else if (ExDeclType->isFunctionType()) {
-    if (ExDeclType.isReferenceable())
-      ExDeclType = Context.getPointerType(ExDeclType);
-    else {
-      Diag(Loc, diag::err_compound_qualified_function_type)
-          << 1 << true << ExDeclType
-          << ExDeclType->castAs<FunctionProtoType>()
-                 ->getFunctionQualifiersAsString();
+    if (CheckQualifiedFunctionForPointer(ExDeclType, Loc, QFK_Pointer))
       Invalid = true;
-    }
+    else
+      ExDeclType = Context.getPointerType(ExDeclType);
   }
 
   // C++ 15.3p1: The exception-declaration shall not denote an incomplete type.
diff --git a/clang/lib/Sema/SemaTemplate.cpp b/clang/lib/Sema/SemaTemplate.cpp
index 7c38c594e809f..fb50951b89ba9 100644
--- a/clang/lib/Sema/SemaTemplate.cpp
+++ b/clang/lib/Sema/SemaTemplate.cpp
@@ -1413,13 +1413,8 @@ QualType Sema::CheckNonTypeTemplateParameterType(QualType T,
   //   "function returning T" is adjusted to be of type "pointer to
   //   T" or "pointer to function returning T", respectively.
   if (T->isArrayType() || T->isFunctionType()) {
-    if (!T.isReferenceable()) {
-      // Pointer to cv- or ref- qualified type will be invalid
-      Diag(Loc, diag::err_compound_qualified_function_type)
-          << 1 << true << T
-          << T->castAs<FunctionProtoType>()->getFunctionQualifiersAsString();
+    if (CheckQualifiedFunctionForPointer(T, Loc, QFK_Pointer))
       return QualType();
-    }
     return Context.getDecayedType(T);
   }
 
@@ -7243,8 +7238,11 @@ ExprResult Sema::BuildExpressionFromDeclTemplateArgument(
   //   T" or "pointer to function returning T", respectively.
   if (ParamType->isArrayType())
     ParamType = Context.getArrayDecayedType(ParamType);
-  else if (ParamType->isFunctionType())
+  else if (ParamType->isFunctionType()) {
+    if (CheckQualifiedFunctionForPointer(ParamType, Loc, QFK_Pointer))
+      return ExprError();
     ParamType = Context.getPointerType(ParamType);
+  }
 
   // For a NULL non-type template argument, return nullptr casted to the
   // parameter's type.
diff --git a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
index 0d17bd2094e0e..81c6e460fb3fe 100644
--- a/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
+++ b/clang/lib/Sema/SemaTemplateDeductionGuide.cpp
@@ -624,14 +624,9 @@ struct ConvertConstructorToDeductionGuideTransform {
       NewType = SemaRef.Context.getDecayedType(NewType);
     else if (NewType->isFunctionType()) {
       // Reject cv- and ref-qualified function
-      if (!NewType.isReferenceable()) {
-        SemaRef.Diag(OldParam->getLocation(),
-                     diag::err_compound_qualified_function_type)
-            << 1 << true << NewType
-            << cast<FunctionProtoType>(NewType)
-                   ->getFunctionQualifiersAsString();
+      if (SemaRef.CheckQualifiedFunctionForPointer(
+              NewType, OldParam->getLocation(), Sema::QFK_Pointer))
         return nullptr;
-      }
       NewType = SemaRef.Context.getDecayedType(NewType);
     }
 
diff --git a/clang/lib/Sema/SemaType.cpp b/clang/lib/Sema/SemaType.cpp
index 8b706a58aeeef..48ad8efd97a2d 100644
--- a/clang/lib/Sema/SemaType.cpp
+++ b/clang/lib/Sema/SemaType.cpp
@@ -1707,31 +1707,14 @@ static QualType inferARCLifetimeForPointee(Sema &S, QualType type,
   return S.Context.getQualifiedType(type, qs);
 }
 
-namespace {
-/// Kinds of declarator that cannot contain a qualified function type.
-///
-/// C++98 [dcl.fct]p4 / C++11 [dcl.fct]p6:
-///     a function type with a cv-qualifier or a ref-qualifier can only appear
-///     at the topmost level of a type.
-///
-/// Parens and member pointers are permitted. We don't diagnose array and
-/// function declarators, because they don't allow function types at all.
-///
-/// The values of this enum are used in diagnostics.
-enum QualifiedFunctionKind { QFK_BlockPointer, QFK_Pointer, QFK_Reference };
-} // end anonymous namespace
-
-/// Check whether the type T is a qualified function type, and if it is,
-/// diagnose that it cannot be contained within the given kind of declarator.
-static bool checkQualifiedFunction(Sema &S, QualType T, SourceLocation Loc,
-                                   QualifiedFunctionKind QFK) {
+bool Sema::CheckQualifiedFunctionForPointer(QualType T, SourceLocation Loc,
+                                            QualifiedFunctionKind QFK) {
   // Does T refer to a function type with a cv-qualifier or a ref-qualifier?
   const FunctionProtoType *FPT = T->getAs<FunctionProtoType>();
-  if (!FPT ||
-      (FPT->getMethodQuals().empty() && FPT->getRefQualifier() == RQ_None))
+  if (!FPT || !FPT->hasQualifiers())
     return false;
 
-  S.Diag(Loc, diag::err_compound_qualified_function_type)
+  Diag(Loc, diag::err_compound_qualified_function_type)
       << QFK << isa<FunctionType>(T.IgnoreParens()) << T
       << FPT->getFunctionQualifiersAsString();
   return true;
@@ -1739,8 +1722,7 @@ static bool checkQualifiedFunction(Sema &S, QualType T, SourceLocation Loc,
 
 bool Sema::CheckQualifiedFunctionForTypeId(QualType T, SourceLocation Loc) {
   const FunctionProtoType *FPT = T->getAs<FunctionProtoType>();
-  if (!FPT ||
-      (FPT->getMethodQuals().empty() && FPT->getRefQualifier() == RQ_None))
+  if (!FPT || !FPT->hasQualifiers())
     return false;
 
   Diag(Loc, diag::err_qualified_function_typeid)
@@ -1779,7 +1761,7 @@ QualType Sema::BuildPointerType(QualType T,
     return QualType();
   }
 
-  if (checkQualifiedFunction(*this, T, Loc, QFK_Pointer))
+  if (CheckQualifiedFunctionForPointer(T, Loc, QFK_Pointer))
     return QualType();
 
   assert(!T->isObjCObjectType() && "Should build ObjCObjectPointerType");
@@ -1851,7 +1833,7 @@ QualType Sema::BuildReferenceType(QualType T, bool SpelledAsLValue,
     return QualType();
   }
 
-  if (checkQualifiedFunction(*this, T, Loc, QFK_Reference))
+  if (CheckQualifiedFunctionForPointer(T, Loc, QFK_Reference))
     return QualType();
 
   if (T->isFunctionType() && getLangOpts().OpenCL &&
@@ -2590,13 +2572,9 @@ QualType Sema::BuildFunctionType(QualType T,
   for (unsigned Idx = 0, Cnt = ParamTypes.size(); Idx < Cnt; ++Idx) {
     // FIXME: Loc is too inprecise here, should use proper locations for args.
     QualType ParamType = ParamTypes[Idx];
-    if (ParamType->isFunctionType() && !ParamType.isReferenceable()) {
-      Diag(Loc, diag::err_compound_qualified_function_type)
-          << 1 << true << ParamType
-          << ParamType->castAs<FunctionProtoType>()
-                 ->getFunctionQualifiersAsString();
+    if (CheckQualifiedFunctionForPointer(ParamType, Loc, QFK_Pointer))
       Invalid = true;
-    } else
+    else
       ParamType = Context.getAdjustedParameterType(ParamType);
 
     if (ParamType->isVoidType()) {
@@ -2697,7 +2675,7 @@ QualType Sema::BuildBlockPointerType(QualType T,
     return QualType();
   }
 
-  if (checkQualifiedFunction(*this, T, Loc, QFK_BlockPointer))
+  if (CheckQualifiedFunctionForPointer(T, Loc, QFK_BlockPointer))
     return QualType();
 
   if (getLangOpts().OpenCL)
@@ -4216,8 +4194,7 @@ static TypeSourceInfo *GetFullTypeForDeclarator(TypeProcessingState &state,
 
   // Does T refer to a function type with a cv-qualifier or a ref-qualifier?
   bool IsQualifiedFunction = T->isFunctionProtoType() &&
-      (!T->castAs<FunctionProtoType>()->getMethodQuals().empty() ||
-       T->castAs<FunctionProtoType>()->getRefQualifier() != RQ_None);
+                             T->castAs<FunctionProtoType>()->hasQualifiers();
 
   // 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.
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
index 8f01406f8bae5..8bb35efb04892 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6-0x.cpp
@@ -54,7 +54,7 @@ void (X::*mpf2)() && = &X::f1;
 void (f() &&); // expected-error{{non-member function cannot have '&&' qualifier}}
 
 template<typename T> struct pass {
-  void f(T); // expected-error {{pointer to function type cannot have '&' qualifier}}
+  void f(T); // expected-error {{pointer to function type 'void () &' cannot have '&' qualifier}}
 };
 pass<func_type_lvalue> pass0; // expected-note {{in instantiation of template class 'pass<void () &>' requested here}}
 pass<func_type_lvalue> pass1;
diff --git a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
index 82b2f8102217e..b0d855571be97 100644
--- a/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
+++ b/clang/test/CXX/dcl.decl/dcl.meaning/dcl.fct/p6.cpp
@@ -27,6 +27,6 @@ template<typename T> struct S {
 S<F> s; // expected-note {{in instantiation of}}
 
 template<typename T> struct U {
-  void f(T); // expected-error {{pointer to function type cannot have 'const' qualifier}}
+  void f(T); // expected-error {{pointer to function type 'void () const' cannot have 'const' qualifier}}
 };
 U<F> u; // expected-note {{in instantiation of}}
diff --git a/clang/test/SemaCXX/function-type-qual.cpp b/clang/test/SemaCXX/function-type-qual.cpp
index aaf91aa6b9a18..698f0df5eb1cd 100644
--- a/clang/test/SemaCXX/function-type-qual.cpp
+++ b/clang/test/SemaCXX/function-type-qual.cpp
@@ -1,4 +1,5 @@
-// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -std=c++11 -verify %s
+// RUN: %clang_cc1 -fcxx-exceptions -fsyntax-only -std=c++17 -verify %s
 
 void f() const; // expected-error {{non-member function cannot have 'const' qualifier}}
 void (*pf)() const; // expected-error {{pointer to function type cannot have 'const' qualifier}}
@@ -86,6 +87,24 @@ void catch_fn() {
 }
 template void catch_fn<void()>();
 template void catch_fn<void() const>();
-// expected-error@#GH27059-catch_fn {{pointer to function type cannot have 'const' qualifier}}
+// expected-error@#GH27059-catch_fn {{pointer to function type 'void () const' cannot have 'const' qualifier}}
 //   expected-note at -2 {{in instantiation of function template specialization 'GH27059::catch_fn<void () const>' requested here}}
+template<typename T = int()const, T = nullptr> void f1() {}
+template void f1();
+// expected-error at -1 {{explicit instantiation of 'f1' does not refer to a function template, variable template, member function, member class, or static data member}}
+//   expected-note at -3 {{candidate template ignored: substitution failure [with T = int () const]: pointer to function type cannot have 'const' qualifier}}
+
+#if __cplusplus >= 201703L
+template<typename T = void()const>
+struct X { // expected-note {{candidate function template not viable: requires 1 argument, but 0 were provided}} \
+              expected-note {{implicit deduction guide declared as 'template <typename T = void () const> X(X<T>) -> X<T>'}}
+  X(T = 1); // expected-note {{candidate template ignored: substitution failure [with T = void () const]: pointer to function type 'void () const' cannot have 'const' qualifier}} \
+               expected-note {{implicit deduction guide declared as 'template <typename T = void () const> X(T = <null expr>) -> X<T>'}}
+};
+void f2() {
+  X{};
+  // expected-error at -1 {{no viable constructor or deduction guide for deduction of template arguments of 'X'}}
+}
+#endif
+
 }
diff --git a/clang/test/SemaTemplate/ctad.cpp b/clang/test/SemaTemplate/ctad.cpp
index 1bf605f823bbe..34e0a1e7c73af 100644
--- a/clang/test/SemaTemplate/ctad.cpp
+++ b/clang/test/SemaTemplate/ctad.cpp
@@ -60,6 +60,7 @@ Y y(1);
 namespace NoCrashOnGettingDefaultArgLoc {
 template <typename>
 class A {
+  // FIXME: Print something better for the default argument than <null expr>
   A(int = 1); // expected-note {{candidate template ignored: couldn't infer template argumen}} \
               // expected-note {{implicit deduction guide declared as 'template <typename> D(int = <null expr>) -> D<type-parameter-0-0>'}}
 };

>From cb42df377c00ff84358ab4c6b00a615fa651523f Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Fri, 26 Jul 2024 13:34:00 +0100
Subject: [PATCH 3/5] Core issue tests

---
 clang/test/CXX/drs/cwg15xx.cpp | 48 ++++++++++++++++++++++------------
 clang/test/CXX/drs/cwg7xx.cpp  | 18 ++++++++++++-
 clang/www/cxx_dr_status.html   |  2 +-
 3 files changed, 49 insertions(+), 19 deletions(-)

diff --git a/clang/test/CXX/drs/cwg15xx.cpp b/clang/test/CXX/drs/cwg15xx.cpp
index 21a392a5141e3..961c25000111a 100644
--- a/clang/test/CXX/drs/cwg15xx.cpp
+++ b/clang/test/CXX/drs/cwg15xx.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,cxx11-14,cxx14-17 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
@@ -6,6 +6,11 @@
 // RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx23,since-cxx20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx23,since-cxx20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 
+#if __cplusplus == 199711L
+#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
+// cxx98-error at -1 {{variadic macros are a C99 feature}}
+#endif
+
 namespace cwg1512 { // cwg1512: 4
   void f(char *p) {
     if (p > 0) {}
@@ -556,24 +561,33 @@ auto CWG1579_lambda_invalid = []() -> GenericMoveOnly<char> {
 } // end namespace cwg1579
 
 namespace cwg1584 { // cwg1584: 7 drafting 2015-05
-#if __cplusplus >= 201103L
-  // Deducing function types from cv-qualified types
-  template<typename T> void f(const T *); // #cwg1584-f
-  template<typename T> void g(T *, const T * = 0);
-  template<typename T> void h(T *) { T::error; }
-  // since-cxx11-error at -1 {{type 'void ()' cannot be used prior to '::' because it has no members}}
-  //   since-cxx11-note@#cwg1584-h {{in instantiation of function template specialization 'cwg1584::h<void ()>' requested here}}
-  template<typename T> void h(const T *);
-  void i() {
-    f(&i);
-    // since-cxx11-error at -1 {{no matching function for call to 'f'}}
-    //   since-cxx11-note@#cwg1584-f {{candidate template ignored: could not match 'const T *' against 'void (*)()'}}
-    g(&i);
-    h(&i); // #cwg1584-h
-  }
-#endif
+// Deducing function types from cv-qualified types
+template<typename T> void f(const T *); // #cwg1584-f
+template<typename T> void g(T *, const T * = 0);
+template<typename T> void h(T *) { T::error; }
+// expected-error at -1 {{type 'void ()' cannot be used prior to '::' because it has no members}}
+//   expected-note@#cwg1584-h {{in instantiation of function template specialization 'cwg1584::h<void ()>' requested here}}
+template<typename T> void h(const T *);
+void i() {
+  f(&i);
+  // expected-error at -1 {{no matching function for call to 'f'}}
+  //   expected-note@#cwg1584-f {{candidate template ignored: could not match 'const T *' against 'void (*)()'}}
+  g(&i);
+  h(&i); // #cwg1584-h
 }
 
+template<typename T> struct tuple_size {
+  static const bool is_primary = true;
+};
+template<typename T> struct tuple_size<T const> : tuple_size<T> {
+  static const bool is_primary = false;
+};
+
+tuple_size<void()> t;
+static_assert(tuple_size<void()>::is_primary, "");
+static_assert(tuple_size<void()const>::is_primary, "");
+} // namespace cwg1584
+
 namespace cwg1589 {   // cwg1589: 3.7 c++11
 #if __cplusplus >= 201103L
   // Ambiguous ranking of list-initialization sequences
diff --git a/clang/test/CXX/drs/cwg7xx.cpp b/clang/test/CXX/drs/cwg7xx.cpp
index 6d93e2948dadb..114fe3b4d4e59 100644
--- a/clang/test/CXX/drs/cwg7xx.cpp
+++ b/clang/test/CXX/drs/cwg7xx.cpp
@@ -1,9 +1,14 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++98 %s -verify=expected,cxx98-14,cxx98-11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++98 %s -verify=expected,cxx98,cxx98-14,cxx98-11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 %s -verify=expected,cxx98-14,cxx98-11,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++14 %s -verify=expected,cxx98-14,since-cxx14,since-cxx11,cxx14 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++17 %s -verify=expected,since-cxx14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++2a %s -verify=expected,since-cxx14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 
+#if __cplusplus == 199711L
+#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
+// cxx98-error at -1 {{variadic macros are a C99 feature}}
+#endif
+
 namespace cwg705 { // cwg705: yes
   namespace N {
     struct S {};
@@ -71,6 +76,17 @@ namespace cwg712 { // cwg712: partial
 #endif
 }
 
+namespace cwg713 { // cwg713: yes
+static_assert(!__is_const(void()const), "");
+static_assert(!__is_const(void()const&), "");
+// cxx98-error at -1 {{reference qualifiers on functions are a C++11 extension}}
+static_assert(!__is_const(void()const volatile), "");
+static_assert(!__is_volatile(void()volatile), "");
+static_assert(!__is_volatile(void()volatile&), "");
+// cxx98-error at -1 {{reference qualifiers on functions are a C++11 extension}}
+static_assert(!__is_volatile(void()const volatile), "");
+} // namespace cwg713
+
 namespace cwg727 { // cwg727: partial
   struct A {
     template<typename T> struct C; // #cwg727-C
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 937f67981e296..3dd91be9ed9d3 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -4327,7 +4327,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/713.html">713</a></td>
     <td>CD2</td>
     <td>Unclear note about cv-qualified function types</td>
-    <td class="unknown" align="center">Unknown</td>
+    <td class="full" align="center">Yes</td>
   </tr>
   <tr id="714">
     <td><a href="https://cplusplus.github.io/CWG/issues/714.html">714</a></td>

>From 9f40b8752b9b3aef51fc572b838121b97fa792c6 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Fri, 26 Jul 2024 14:47:07 +0100
Subject: [PATCH 4/5] Revert "Core issue tests"

This reverts commit cb42df377c00ff84358ab4c6b00a615fa651523f.

Moving to a seperate PR
---
 clang/test/CXX/drs/cwg15xx.cpp | 48 ++++++++++++----------------------
 clang/test/CXX/drs/cwg7xx.cpp  | 18 +------------
 clang/www/cxx_dr_status.html   |  2 +-
 3 files changed, 19 insertions(+), 49 deletions(-)

diff --git a/clang/test/CXX/drs/cwg15xx.cpp b/clang/test/CXX/drs/cwg15xx.cpp
index 961c25000111a..21a392a5141e3 100644
--- a/clang/test/CXX/drs/cwg15xx.cpp
+++ b/clang/test/CXX/drs/cwg15xx.cpp
@@ -1,4 +1,4 @@
-// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected,cxx98 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -std=c++98 -triple x86_64-unknown-unknown %s -verify=expected -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,cxx11-14 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++14 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,cxx11-14,cxx14-17 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++17 -triple x86_64-unknown-unknown %s -verify=expected,cxx11-20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
@@ -6,11 +6,6 @@
 // RUN: %clang_cc1 -std=c++23 -triple x86_64-unknown-unknown %s -verify=expected,since-cxx23,since-cxx20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -std=c++2c -triple x86_64-unknown-unknown %s -verify=expected,since-cxx23,since-cxx20,since-cxx11,since-cxx17 -fexceptions -fcxx-exceptions -pedantic-errors
 
-#if __cplusplus == 199711L
-#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
-// cxx98-error at -1 {{variadic macros are a C99 feature}}
-#endif
-
 namespace cwg1512 { // cwg1512: 4
   void f(char *p) {
     if (p > 0) {}
@@ -561,33 +556,24 @@ auto CWG1579_lambda_invalid = []() -> GenericMoveOnly<char> {
 } // end namespace cwg1579
 
 namespace cwg1584 { // cwg1584: 7 drafting 2015-05
-// Deducing function types from cv-qualified types
-template<typename T> void f(const T *); // #cwg1584-f
-template<typename T> void g(T *, const T * = 0);
-template<typename T> void h(T *) { T::error; }
-// expected-error at -1 {{type 'void ()' cannot be used prior to '::' because it has no members}}
-//   expected-note@#cwg1584-h {{in instantiation of function template specialization 'cwg1584::h<void ()>' requested here}}
-template<typename T> void h(const T *);
-void i() {
-  f(&i);
-  // expected-error at -1 {{no matching function for call to 'f'}}
-  //   expected-note@#cwg1584-f {{candidate template ignored: could not match 'const T *' against 'void (*)()'}}
-  g(&i);
-  h(&i); // #cwg1584-h
+#if __cplusplus >= 201103L
+  // Deducing function types from cv-qualified types
+  template<typename T> void f(const T *); // #cwg1584-f
+  template<typename T> void g(T *, const T * = 0);
+  template<typename T> void h(T *) { T::error; }
+  // since-cxx11-error at -1 {{type 'void ()' cannot be used prior to '::' because it has no members}}
+  //   since-cxx11-note@#cwg1584-h {{in instantiation of function template specialization 'cwg1584::h<void ()>' requested here}}
+  template<typename T> void h(const T *);
+  void i() {
+    f(&i);
+    // since-cxx11-error at -1 {{no matching function for call to 'f'}}
+    //   since-cxx11-note@#cwg1584-f {{candidate template ignored: could not match 'const T *' against 'void (*)()'}}
+    g(&i);
+    h(&i); // #cwg1584-h
+  }
+#endif
 }
 
-template<typename T> struct tuple_size {
-  static const bool is_primary = true;
-};
-template<typename T> struct tuple_size<T const> : tuple_size<T> {
-  static const bool is_primary = false;
-};
-
-tuple_size<void()> t;
-static_assert(tuple_size<void()>::is_primary, "");
-static_assert(tuple_size<void()const>::is_primary, "");
-} // namespace cwg1584
-
 namespace cwg1589 {   // cwg1589: 3.7 c++11
 #if __cplusplus >= 201103L
   // Ambiguous ranking of list-initialization sequences
diff --git a/clang/test/CXX/drs/cwg7xx.cpp b/clang/test/CXX/drs/cwg7xx.cpp
index 114fe3b4d4e59..6d93e2948dadb 100644
--- a/clang/test/CXX/drs/cwg7xx.cpp
+++ b/clang/test/CXX/drs/cwg7xx.cpp
@@ -1,14 +1,9 @@
-// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++98 %s -verify=expected,cxx98,cxx98-14,cxx98-11 -fexceptions -fcxx-exceptions -pedantic-errors
+// RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++98 %s -verify=expected,cxx98-14,cxx98-11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++11 %s -verify=expected,cxx98-14,cxx98-11,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++14 %s -verify=expected,cxx98-14,since-cxx14,since-cxx11,cxx14 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++17 %s -verify=expected,since-cxx14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 // RUN: %clang_cc1 -triple %itanium_abi_triple -std=c++2a %s -verify=expected,since-cxx14,since-cxx11 -fexceptions -fcxx-exceptions -pedantic-errors
 
-#if __cplusplus == 199711L
-#define static_assert(...) __extension__ _Static_assert(__VA_ARGS__)
-// cxx98-error at -1 {{variadic macros are a C99 feature}}
-#endif
-
 namespace cwg705 { // cwg705: yes
   namespace N {
     struct S {};
@@ -76,17 +71,6 @@ namespace cwg712 { // cwg712: partial
 #endif
 }
 
-namespace cwg713 { // cwg713: yes
-static_assert(!__is_const(void()const), "");
-static_assert(!__is_const(void()const&), "");
-// cxx98-error at -1 {{reference qualifiers on functions are a C++11 extension}}
-static_assert(!__is_const(void()const volatile), "");
-static_assert(!__is_volatile(void()volatile), "");
-static_assert(!__is_volatile(void()volatile&), "");
-// cxx98-error at -1 {{reference qualifiers on functions are a C++11 extension}}
-static_assert(!__is_volatile(void()const volatile), "");
-} // namespace cwg713
-
 namespace cwg727 { // cwg727: partial
   struct A {
     template<typename T> struct C; // #cwg727-C
diff --git a/clang/www/cxx_dr_status.html b/clang/www/cxx_dr_status.html
index 3dd91be9ed9d3..937f67981e296 100755
--- a/clang/www/cxx_dr_status.html
+++ b/clang/www/cxx_dr_status.html
@@ -4327,7 +4327,7 @@ <h2 id="cxxdr">C++ defect report implementation status</h2>
     <td><a href="https://cplusplus.github.io/CWG/issues/713.html">713</a></td>
     <td>CD2</td>
     <td>Unclear note about cv-qualified function types</td>
-    <td class="full" align="center">Yes</td>
+    <td class="unknown" align="center">Unknown</td>
   </tr>
   <tr id="714">
     <td><a href="https://cplusplus.github.io/CWG/issues/714.html">714</a></td>

>From b836df02eac6800d5427f765e8d1be48e1662686 Mon Sep 17 00:00:00 2001
From: Mital Ashok <mital at mitalashok.co.uk>
Date: Fri, 26 Jul 2024 15:20:53 +0100
Subject: [PATCH 5/5] Fix test failure

---
 clang/lib/AST/ASTContext.cpp | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 6a52af87febf2..42245414f982c 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3651,8 +3651,11 @@ QualType ASTContext::getComplexType(QualType T) const {
 QualType ASTContext::getPointerType(QualType T) const {
 #ifndef NDEBUG
   {
-    assert(!T->isReferenceType() &&
-           "Attempting to create pointer to reference type");
+    // FIXME: test/Analysis/casts.cpp ends up trying to create a pointer to a
+    // reference type
+    // assert(!T->isReferenceType() &&
+    //        "Attempting to create pointer to reference type");
+
     // FIXME: Get rid of creating pointers to qualified function types. It is
     // used as an intermediary for building a CallExpr that is eventually
     // converted into a CXXMemberCallExpr (and loses the pointer type)



More information about the cfe-commits mailing list