[clang] [Clang] [Sema] Improve support for `__restrict`-qualified member functions (PR #83855)

via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 9 05:35:47 PDT 2024


https://github.com/Sirraide updated https://github.com/llvm/llvm-project/pull/83855

>From eb5ebe31657fc82fe3810beff8d4cfff2201bf54 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 1 Mar 2024 13:56:32 +0100
Subject: [PATCH 01/13] [Clang] [Sema] Strip `__restrict` in cv-qualifier-list
 of member functions in canonical type

---
 clang/lib/AST/ASTContext.cpp | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 5a8fae76a43a4d..91f1ccf429521f 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -4418,7 +4418,8 @@ QualType ASTContext::getFunctionTypeInternal(
 
   // Determine whether the type being created is already canonical or not.
   bool isCanonical = !Unique && IsCanonicalExceptionSpec &&
-                     isCanonicalResultType(ResultTy) && !EPI.HasTrailingReturn;
+                     isCanonicalResultType(ResultTy) &&
+                     !EPI.HasTrailingReturn && !EPI.TypeQuals.hasRestrict();
   for (unsigned i = 0; i != NumArgs && isCanonical; ++i)
     if (!ArgArray[i].isCanonicalAsParam())
       isCanonical = false;
@@ -4439,6 +4440,7 @@ QualType ASTContext::getFunctionTypeInternal(
     llvm::SmallVector<QualType, 8> ExceptionTypeStorage;
     FunctionProtoType::ExtProtoInfo CanonicalEPI = EPI;
     CanonicalEPI.HasTrailingReturn = false;
+    CanonicalEPI.TypeQuals.removeRestrict();
 
     if (IsCanonicalExceptionSpec) {
       // Exception spec is already OK.

>From 68e633ea0d26195b2aa4ced5f541497e8e5ae13a Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Fri, 1 Mar 2024 15:05:17 +0100
Subject: [PATCH 02/13] [Clang] Tests for new `__restrict` behaviour

---
 clang/test/CodeGenCXX/mangle-ms-cxx11.cpp     | 15 +++++---
 clang/test/CodeGenCXX/mangle-ms.cpp           | 36 +++++++++++++++++++
 .../SemaCXX/addr-of-overloaded-function.cpp   |  8 -----
 .../restrict-member-function-matching.cpp     | 21 +++++++++++
 4 files changed, 67 insertions(+), 13 deletions(-)
 create mode 100644 clang/test/SemaCXX/restrict-member-function-matching.cpp

diff --git a/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp b/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
index 312c70cc740eb3..4900c4ad272bc3 100644
--- a/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
@@ -16,7 +16,8 @@ S<B> b;
 
 using C = int () __restrict;
 S<C> c;
-// CHECK-DAG: @"?c at FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ at 1@A"
+// FIXME: `__restrict` needs to be included in the mangled name:
+// FIXME-CHECK-DAG: @"?c at FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ at 1@A"
 
 using D = int () const &;
 S<D> d;
@@ -28,7 +29,8 @@ S<E> e;
 
 using F = int () __restrict &;
 S<F> f;
-// CHECK-DAG: @"?f at FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ at 1@A"
+// FIXME: See comment above.
+// FIXME-CHECK-DAG: @"?f at FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ at 1@A"
 
 using G = int () const &&;
 S<G> g;
@@ -40,7 +42,8 @@ S<H> h;
 
 using I = int () __restrict &&;
 S<I> i;
-// CHECK-DAG: @"?i at FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ at 1@A"
+// FIXME: See comment above.
+// FIXME-CHECK-DAG: @"?i at FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ at 1@A"
 
 using J = int ();
 S<J> j;
@@ -205,9 +208,11 @@ struct A {
   void foo() __restrict &&;
 };
 void A::foo() __restrict & {}
-// CHECK-DAG: @"?foo at A@PR19361@@QIGAEXXZ"
+// FIXME: See comment above
+// FIXME-CHECK-DAG: @"?foo at A@PR19361@@QIGAEXXZ"
 void A::foo() __restrict && {}
-// CHECK-DAG: @"?foo at A@PR19361@@QIHAEXXZ"
+// FIXME: See comment above
+// FIXME-CHECK-DAG: @"?foo at A@PR19361@@QIHAEXXZ"
 }
 
 int operator"" _deg(long double) { return 0; }
diff --git a/clang/test/CodeGenCXX/mangle-ms.cpp b/clang/test/CodeGenCXX/mangle-ms.cpp
index cf69a83bbdf8c6..4cd28970f8094c 100644
--- a/clang/test/CodeGenCXX/mangle-ms.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms.cpp
@@ -504,3 +504,39 @@ void runOnFunction() {
 }
 // CHECK-DAG: call {{.*}} @"??0?$L at V?$H at PAH@PR26029@@@PR26029@@QAE at XZ"
 }
+
+namespace CVRMemberFunctionQuals {
+struct S {
+  void a() const;
+  void b() volatile;
+  void c() __restrict;
+  void d() const volatile;
+  void e() const __restrict;
+  void f() volatile __restrict;
+  void g() const volatile __restrict;
+
+  void h();
+};
+
+// X64-DAG: define dso_local void @"?a at S@CVRMemberFunctionQuals@@QEBAXXZ"
+// X64-DAG: define dso_local void @"?b at S@CVRMemberFunctionQuals@@QECAXXZ"
+// X64-DAG: define dso_local void @"?c at S@CVRMemberFunctionQuals@@QEIAAXXZ"
+// X64-DAG: define dso_local void @"?d at S@CVRMemberFunctionQuals@@QEDAXXZ"
+// X64-DAG: define dso_local void @"?e at S@CVRMemberFunctionQuals@@QEIBAXXZ"
+// X64-DAG: define dso_local void @"?f at S@CVRMemberFunctionQuals@@QEICAXXZ"
+// X64-DAG: define dso_local void @"?g at S@CVRMemberFunctionQuals@@QEIDAXXZ"
+void S::a() const {}
+void S::b() volatile {}
+void S::c() __restrict {}
+void S::d() const volatile {}
+void S::e() const __restrict {}
+void S::f() volatile __restrict {}
+void S::g() const volatile __restrict {}
+
+// MSVC allows a mismatch in `__restrict`-qualification between a function
+// declaration and definition and includes the qualifier in the ABI only if
+// it is present in the declaration.
+//
+// X64-DAG: define dso_local void @"?h at S@CVRMemberFunctionQuals@@QEAAXXZ"
+void S::h() __restrict {}
+}
diff --git a/clang/test/SemaCXX/addr-of-overloaded-function.cpp b/clang/test/SemaCXX/addr-of-overloaded-function.cpp
index dd1c3462c8c1f4..0d387ce1fb133d 100644
--- a/clang/test/SemaCXX/addr-of-overloaded-function.cpp
+++ b/clang/test/SemaCXX/addr-of-overloaded-function.cpp
@@ -211,11 +211,7 @@ namespace test1 {
     void N() {};
     void C() const {};
     void V() volatile {};
-    void R() __restrict {};
     void CV() const volatile {};
-    void CR() const __restrict {};
-    void VR() volatile __restrict {};
-    void CVR() const volatile __restrict {};
   };
 
 
@@ -223,11 +219,7 @@ namespace test1 {
     void (Qualifiers::*X)();
     X = &Qualifiers::C; // expected-error-re {{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} const': different qualifiers (unqualified vs 'const')}}
     X = &Qualifiers::V; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} volatile': different qualifiers (unqualified vs 'volatile')}}
-    X = &Qualifiers::R; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} __restrict': different qualifiers (unqualified vs '__restrict')}}
     X = &Qualifiers::CV; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} const volatile': different qualifiers (unqualified vs 'const volatile')}}
-    X = &Qualifiers::CR; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} const __restrict': different qualifiers (unqualified vs 'const __restrict')}}
-    X = &Qualifiers::VR; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} volatile __restrict': different qualifiers (unqualified vs 'volatile __restrict')}}
-    X = &Qualifiers::CVR; // expected-error-re{{assigning to 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}}' from incompatible type 'void (test1::Qualifiers::*)(){{( __attribute__\(\(thiscall\)\))?}} const volatile __restrict': different qualifiers (unqualified vs 'const volatile __restrict')}}
   }
 
   struct Dummy {
diff --git a/clang/test/SemaCXX/restrict-member-function-matching.cpp b/clang/test/SemaCXX/restrict-member-function-matching.cpp
new file mode 100644
index 00000000000000..e3e1edad718bc4
--- /dev/null
+++ b/clang/test/SemaCXX/restrict-member-function-matching.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+struct S{ void a(); };
+
+// GCC allows '__restrict' in the cv-qualifier-seq of member functions but
+// ignores it for pretty much everything (except that the type of 'this' in
+// is '__restrict' iff the *definition* is '__restrict' as well).
+static_assert(__is_same(void (S::*) (), void (S::*) () __restrict));
+
+namespace gh11039 {
+class foo {
+  int member[4];
+
+  void bar(int * a);
+};
+
+void foo::bar(int * a) __restrict {
+  member[3] = *a;
+}
+}
\ No newline at end of file

>From 20d7a072f4fe4b98d3af67f30c7cc56deb1cfdea Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Sat, 2 Mar 2024 09:34:52 +0100
Subject: [PATCH 03/13] [Clang] Update `__restrict` tests

---
 clang/test/CodeGenCXX/mangle-ms-cxx11.cpp     | 15 ++++-------
 .../restrict-member-function-matching.cpp     | 26 ++++++++++++++++---
 2 files changed, 28 insertions(+), 13 deletions(-)

diff --git a/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp b/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
index 4900c4ad272bc3..312c70cc740eb3 100644
--- a/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
+++ b/clang/test/CodeGenCXX/mangle-ms-cxx11.cpp
@@ -16,8 +16,7 @@ S<B> b;
 
 using C = int () __restrict;
 S<C> c;
-// FIXME: `__restrict` needs to be included in the mangled name:
-// FIXME-CHECK-DAG: @"?c at FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ at 1@A"
+// CHECK-DAG: @"?c at FTypeWithQuals@@3U?$S@$$A8@@IAAHXZ at 1@A"
 
 using D = int () const &;
 S<D> d;
@@ -29,8 +28,7 @@ S<E> e;
 
 using F = int () __restrict &;
 S<F> f;
-// FIXME: See comment above.
-// FIXME-CHECK-DAG: @"?f at FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ at 1@A"
+// CHECK-DAG: @"?f at FTypeWithQuals@@3U?$S@$$A8@@IGAAHXZ at 1@A"
 
 using G = int () const &&;
 S<G> g;
@@ -42,8 +40,7 @@ S<H> h;
 
 using I = int () __restrict &&;
 S<I> i;
-// FIXME: See comment above.
-// FIXME-CHECK-DAG: @"?i at FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ at 1@A"
+// CHECK-DAG: @"?i at FTypeWithQuals@@3U?$S@$$A8@@IHAAHXZ at 1@A"
 
 using J = int ();
 S<J> j;
@@ -208,11 +205,9 @@ struct A {
   void foo() __restrict &&;
 };
 void A::foo() __restrict & {}
-// FIXME: See comment above
-// FIXME-CHECK-DAG: @"?foo at A@PR19361@@QIGAEXXZ"
+// CHECK-DAG: @"?foo at A@PR19361@@QIGAEXXZ"
 void A::foo() __restrict && {}
-// FIXME: See comment above
-// FIXME-CHECK-DAG: @"?foo at A@PR19361@@QIHAEXXZ"
+// CHECK-DAG: @"?foo at A@PR19361@@QIHAEXXZ"
 }
 
 int operator"" _deg(long double) { return 0; }
diff --git a/clang/test/SemaCXX/restrict-member-function-matching.cpp b/clang/test/SemaCXX/restrict-member-function-matching.cpp
index e3e1edad718bc4..a538c9b55b603e 100644
--- a/clang/test/SemaCXX/restrict-member-function-matching.cpp
+++ b/clang/test/SemaCXX/restrict-member-function-matching.cpp
@@ -1,12 +1,32 @@
 // RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -triple=x86_64-pc-win32 -fms-compatibility %s
 // expected-no-diagnostics
 
 struct S{ void a(); };
 
-// GCC allows '__restrict' in the cv-qualifier-seq of member functions but
-// ignores it for pretty much everything (except that the type of 'this' in
-// is '__restrict' iff the *definition* is '__restrict' as well).
+template <typename A, typename B>
+struct is_same {
+    static constexpr bool value = false;
+};
+
+template <typename A>
+struct is_same<A, A> {
+    static constexpr bool value = true;
+};
+
+// GCC and MSVC allows '__restrict' in the cv-qualifier-seq of member functions
+// but ignore it for the purpose of matching and type comparisons.
 static_assert(__is_same(void (S::*) (), void (S::*) () __restrict));
+static_assert(__is_same(void (S::*) () &, void (S::*) () __restrict &));
+static_assert(__is_same(void (S::*) () &&, void (S::*) () __restrict &&));
+static_assert(is_same<void (S::*) (), void (S::*) () __restrict>::value);
+static_assert(is_same<void (S::*) () &, void (S::*) () __restrict &>::value);
+static_assert(is_same<void (S::*) () &&, void (S::*) () __restrict &&>::value);
+
+// Non-member function types with '__restrict' are distinct types.
+using A = void () __restrict;
+using B = void ();
+static_assert(!is_same<A, B>::value);
 
 namespace gh11039 {
 class foo {

>From 8f868c8485e5e05df2437eca075d17eec208f8e4 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Sat, 2 Mar 2024 20:29:02 +0100
Subject: [PATCH 04/13] [Clang] WIP: Improved support for `__restrict` member
 functions

---
 clang/include/clang/AST/ASTContext.h          |  4 +
 clang/lib/AST/ASTContext.cpp                  | 27 +++++--
 clang/lib/Sema/SemaDecl.cpp                   | 43 ++++++++--
 clang/lib/Sema/SemaDeclCXX.cpp                | 20 +++++
 .../restrict-member-function-matching.cpp     | 38 +++++++--
 clang/test/SemaCXX/restrict-this.cpp          | 78 ++++++++++++++++++-
 6 files changed, 191 insertions(+), 19 deletions(-)

diff --git a/clang/include/clang/AST/ASTContext.h b/clang/include/clang/AST/ASTContext.h
index ff6b64c7f72d57..5b0a59be08519d 100644
--- a/clang/include/clang/AST/ASTContext.h
+++ b/clang/include/clang/AST/ASTContext.h
@@ -1435,6 +1435,10 @@ class ASTContext : public RefCountedBase<ASTContext> {
   /// The class \p Cls is a \c Type because it could be a dependent name.
   QualType getMemberPointerType(QualType T, const Type *Cls) const;
 
+private:
+  QualType getMemberPointerTypeInternal(QualType T, const Type *Cls) const;
+
+public:
   /// Return a non-unique reference to the type for a variable array of
   /// the specified element type.
   QualType getVariableArrayType(QualType EltTy, Expr *NumElts,
diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index 91f1ccf429521f..abc5e3a9840d85 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3479,9 +3479,7 @@ QualType ASTContext::getRValueReferenceType(QualType T) const {
   return QualType(New, 0);
 }
 
-/// getMemberPointerType - Return the uniqued reference to the type for a
-/// member pointer to the specified type, in the specified class.
-QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
+QualType ASTContext::getMemberPointerTypeInternal(QualType T, const Type *Cls) const {
   // Unique pointers, to guarantee there is only one pointer of a particular
   // structure.
   llvm::FoldingSetNodeID ID;
@@ -3510,6 +3508,26 @@ QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
   return QualType(New, 0);
 }
 
+/// getMemberPointerType - Return the uniqued reference to the type for a
+/// member pointer to the specified type, in the specified class.
+QualType ASTContext::getMemberPointerType(QualType T, const Type *Cls) const {
+  bool Paren = isa<ParenType>(T);
+  T = T.IgnoreParens();
+
+  // GCC and MSVC effectively drop '__restrict' from the function type,
+  // so do the same as well.
+  if (auto *FPT = dyn_cast<FunctionProtoType>(T);
+      FPT && FPT->getMethodQuals().hasRestrict()) {
+    FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+    EPI.TypeQuals.removeRestrict();
+    T = getFunctionType(FPT->getReturnType(), FPT->getParamTypes(), EPI);
+  }
+
+  if (Paren)
+    T = getParenType(T);
+  return getMemberPointerTypeInternal(T, Cls);
+}
+
 /// getConstantArrayType - Return the unique reference to the type for an
 /// array of the specified element type.
 QualType ASTContext::getConstantArrayType(QualType EltTy,
@@ -4419,7 +4437,7 @@ QualType ASTContext::getFunctionTypeInternal(
   // Determine whether the type being created is already canonical or not.
   bool isCanonical = !Unique && IsCanonicalExceptionSpec &&
                      isCanonicalResultType(ResultTy) &&
-                     !EPI.HasTrailingReturn && !EPI.TypeQuals.hasRestrict();
+                     !EPI.HasTrailingReturn;
   for (unsigned i = 0; i != NumArgs && isCanonical; ++i)
     if (!ArgArray[i].isCanonicalAsParam())
       isCanonical = false;
@@ -4440,7 +4458,6 @@ QualType ASTContext::getFunctionTypeInternal(
     llvm::SmallVector<QualType, 8> ExceptionTypeStorage;
     FunctionProtoType::ExtProtoInfo CanonicalEPI = EPI;
     CanonicalEPI.HasTrailingReturn = false;
-    CanonicalEPI.TypeQuals.removeRestrict();
 
     if (IsCanonicalExceptionSpec) {
       // Exception spec is already OK.
diff --git a/clang/lib/Sema/SemaDecl.cpp b/clang/lib/Sema/SemaDecl.cpp
index 9fdd8eb236d1ee..c110e8a35d4ce0 100644
--- a/clang/lib/Sema/SemaDecl.cpp
+++ b/clang/lib/Sema/SemaDecl.cpp
@@ -3975,6 +3975,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
 
     const CXXMethodDecl *OldMethod = dyn_cast<CXXMethodDecl>(Old);
     CXXMethodDecl *NewMethod = dyn_cast<CXXMethodDecl>(New);
+    const FunctionDecl *RemoveRestrictFrom = nullptr; // See below.
     if (OldMethod && NewMethod) {
       // Preserve triviality.
       NewMethod->setTrivial(OldMethod->isTrivial());
@@ -4041,6 +4042,14 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
           << getSpecialMember(OldMethod);
         return true;
       }
+
+      // GCC and MSVC allow a mismatch in '__restrict'-qualification between
+      // member function declarations, so remove it if it is present on one
+      // but not the other.
+      auto OldQuals = OldMethod->getMethodQualifiers();
+      auto NewQuals = NewMethod->getMethodQualifiers();
+      if (OldQuals.hasRestrict() != NewQuals.hasRestrict())
+        RemoveRestrictFrom = OldQuals.hasRestrict() ? Old : New;
     }
 
     // C++1z [over.load]p2
@@ -4086,12 +4095,32 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
 
     // noreturn should now match unless the old type info didn't have it.
     QualType OldQTypeForComparison = OldQType;
-    if (!OldTypeInfo.getNoReturn() && NewTypeInfo.getNoReturn()) {
-      auto *OldType = OldQType->castAs<FunctionProtoType>();
-      const FunctionType *OldTypeForComparison
-        = Context.adjustFunctionType(OldType, OldTypeInfo.withNoReturn(true));
-      OldQTypeForComparison = QualType(OldTypeForComparison, 0);
-      assert(OldQTypeForComparison.isCanonical());
+    QualType NewQTypeForComparison = NewQType;
+
+    // Helper to adjust the EPI of a function type.
+    auto AdjustFunctionType = [&](QualType QT, auto Adjust) -> QualType {
+      const auto *FPT = QT->castAs<FunctionProtoType>();
+      FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+      Adjust(EPI);
+      QualType Adjusted = Context.getFunctionType(FPT->getReturnType(),
+                                                  FPT->getParamTypes(), EPI);
+      assert(Adjusted.isCanonical());
+      return Adjusted;
+    };
+
+    bool AddNoReturn = !OldTypeInfo.getNoReturn() && NewTypeInfo.getNoReturn();
+    if (AddNoReturn || RemoveRestrictFrom == Old) {
+      auto Adjust = [&](FunctionProtoType::ExtProtoInfo &EPI) {
+        EPI.ExtInfo = OldTypeInfo.withNoReturn(AddNoReturn);
+        if (RemoveRestrictFrom == Old)
+          EPI.TypeQuals.removeRestrict();
+      };
+      OldQTypeForComparison = AdjustFunctionType(OldQType, Adjust);
+    }
+
+    if (RemoveRestrictFrom == New) {
+      auto Adjust = [](auto &EPI) { EPI.TypeQuals.removeRestrict(); };
+      NewQTypeForComparison = AdjustFunctionType(NewQType, Adjust);
     }
 
     if (haveIncompatibleLanguageLinkages(Old, New)) {
@@ -4118,7 +4147,7 @@ bool Sema::MergeFunctionDecl(FunctionDecl *New, NamedDecl *&OldD, Scope *S,
     // CheckEquivalentExceptionSpec, and we don't want follow-on diagnostics
     // about incompatible types under -fms-compatibility.
     if (Context.hasSameFunctionTypeIgnoringExceptionSpec(OldQTypeForComparison,
-                                                         NewQType))
+                                                         NewQTypeForComparison))
       return MergeCompatibleFunctionDecls(New, Old, S, MergeTypeWithOld);
 
     // If the types are imprecise (due to dependent constructs in friends or
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index d4e1dc67cb50a1..0dc4e133673abf 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -725,6 +725,26 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old,
       Old->isDefined(OldDefinition, true))
     CheckForFunctionRedefinition(New, OldDefinition);
 
+  // Both GCC and MSVC allow a mismatch in '__restrict'-qualification between
+  // the old and new declarations' cv-qualifier-seq's. Unfortunately, they
+  // also handle this mismatch differently: GCC considers '__restrict' on the
+  // definition to be authoritative (i.e. the type of 'this' is '__restrict'
+  // in the body of a definition, if that definition is '__restrict'-qualified),
+  // while MSVC does the opposite: it only looks at the first declaration).
+  //
+  // To support this behaviour, copy '__restrict' from the old declaration to
+  // the new one in MSVC mode only.
+  if (isa<CXXMethodDecl>(New) and getLangOpts().MSVCCompat) {
+    auto NewType = New->getType()->castAs<FunctionProtoType>();
+    auto Quals = Old->getType()->castAs<FunctionProtoType>()->getMethodQuals();
+    if (Quals.hasRestrict() && !NewType->getMethodQuals().hasRestrict()) {
+      FunctionProtoType::ExtProtoInfo EPI = NewType->getExtProtoInfo();
+      EPI.TypeQuals.addRestrict();
+      New->setType(Context.getFunctionType(NewType->getReturnType(),
+                                               NewType->getParamTypes(), EPI));
+    }
+  }
+
   return Invalid;
 }
 
diff --git a/clang/test/SemaCXX/restrict-member-function-matching.cpp b/clang/test/SemaCXX/restrict-member-function-matching.cpp
index a538c9b55b603e..5df5180f3d8e48 100644
--- a/clang/test/SemaCXX/restrict-member-function-matching.cpp
+++ b/clang/test/SemaCXX/restrict-member-function-matching.cpp
@@ -1,8 +1,11 @@
-// RUN: %clang_cc1 -fsyntax-only -verify %s
-// RUN: %clang_cc1 -fsyntax-only -verify -triple=x86_64-pc-win32 -fms-compatibility %s
+// RUN: %clang_cc1 -fsyntax-only -verify -DMSVC=false %s
+// RUN: %clang_cc1 -fsyntax-only -verify -triple=x86_64-pc-win32 -fms-compatibility -DMSVC=true %s
 // expected-no-diagnostics
 
-struct S{ void a(); };
+// GCC and MSVC allows '__restrict' in the cv-qualifier-seq of member functions
+// but ignore it for the purpose of matching and type comparisons. To match this
+// behaviour, we always strip restrict from pointers to member functions as well
+// as from member function declarations.
 
 template <typename A, typename B>
 struct is_same {
@@ -14,15 +17,38 @@ struct is_same<A, A> {
     static constexpr bool value = true;
 };
 
-// GCC and MSVC allows '__restrict' in the cv-qualifier-seq of member functions
-// but ignore it for the purpose of matching and type comparisons.
+struct S {
+  void a() __restrict;
+  void b() __restrict;
+  void c();
+};
+
+void S::a() __restrict { }
+void S::b() { }
+void S::c() __restrict { }
+
 static_assert(__is_same(void (S::*) (), void (S::*) () __restrict));
 static_assert(__is_same(void (S::*) () &, void (S::*) () __restrict &));
 static_assert(__is_same(void (S::*) () &&, void (S::*) () __restrict &&));
+
 static_assert(is_same<void (S::*) (), void (S::*) () __restrict>::value);
 static_assert(is_same<void (S::*) () &, void (S::*) () __restrict &>::value);
 static_assert(is_same<void (S::*) () &&, void (S::*) () __restrict &&>::value);
 
+static_assert(__is_same(decltype(&S::a), void (S::*) () __restrict));
+static_assert(__is_same(decltype(&S::a), void (S::*) ()));
+static_assert(__is_same(decltype(&S::b), void (S::*) () __restrict));
+static_assert(__is_same(decltype(&S::b), void (S::*) ()));
+static_assert(__is_same(decltype(&S::c), void (S::*) () __restrict));
+static_assert(__is_same(decltype(&S::c), void (S::*) ()));
+
+static_assert(is_same<decltype(&S::a), void (S::*) () __restrict>::value);
+static_assert(is_same<decltype(&S::a), void (S::*) ()>::value);
+static_assert(is_same<decltype(&S::b), void (S::*) () __restrict>::value);
+static_assert(is_same<decltype(&S::b), void (S::*) ()>::value);
+static_assert(is_same<decltype(&S::c), void (S::*) () __restrict>::value);
+static_assert(is_same<decltype(&S::c), void (S::*) ()>::value);
+
 // Non-member function types with '__restrict' are distinct types.
 using A = void () __restrict;
 using B = void ();
@@ -38,4 +64,4 @@ class foo {
 void foo::bar(int * a) __restrict {
   member[3] = *a;
 }
-}
\ No newline at end of file
+}
diff --git a/clang/test/SemaCXX/restrict-this.cpp b/clang/test/SemaCXX/restrict-this.cpp
index e78c8e0d56e2f8..f978458b620536 100644
--- a/clang/test/SemaCXX/restrict-this.cpp
+++ b/clang/test/SemaCXX/restrict-this.cpp
@@ -1,6 +1,13 @@
-// RUN: %clang_cc1 -verify -fsyntax-only %s
+// RUN: %clang_cc1 -verify -fsyntax-only -DMSVC=false %s
+// RUN: %clang_cc1 -verify -fsyntax-only -fms-compatibility -DMSVC=true %s
 // expected-no-diagnostics
 
+// Check that '__restrict' is applied to 'this' as appropriate; note that a
+// mismatch in '__restrict'-qualification is allowed between the declaration
+// and definition of a member function. In MSVC mode, 'this' is '__restrict'
+// if the *declaration* is '__restrict'; otherwise, 'this' is '__restrict' if
+// the *definition* is '__restrict'.
+
 struct C {
   void f() __restrict {
     static_assert(__is_same(decltype(this), C *__restrict));
@@ -14,6 +21,10 @@ struct C {
       (void) [*this]() mutable { static_assert(__is_same(decltype(this), C *)); };
     };
   }
+
+  void a() __restrict;
+  void b() __restrict;
+  void c();
 };
 
 template <typename T> struct TC {
@@ -29,10 +40,75 @@ template <typename T> struct TC {
       (void) [*this]() mutable { static_assert(__is_same(decltype(this), TC<int> *)); };
     };
   }
+
+  void a() __restrict;
+  void b() __restrict;
+  void c();
 };
 
+// =========
+
+void C::a() __restrict {
+  static_assert(__is_same(decltype(this), C *__restrict));
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), C *__restrict));
+    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict)); };
+  };
+}
+
+template <typename T>
+void TC<T>::a() __restrict {
+  static_assert(__is_same(decltype(this), TC<int> *__restrict));
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), TC<int> *__restrict));
+    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict)); };
+  };
+}
+
+// =========
+
+void C::b() {
+  static_assert(__is_same(decltype(this), C *__restrict) == MSVC);
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), C *__restrict) == MSVC);
+    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict) == MSVC); };
+  };
+}
+
+template <typename T>
+void TC<T>::b() {
+  static_assert(__is_same(decltype(this), TC<int> *__restrict) == MSVC);
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), TC<int> *__restrict)  == MSVC);
+    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict) == MSVC); };
+  };
+}
+
+// =========
+
+void C::c() __restrict {
+  static_assert(__is_same(decltype(this), C *__restrict) == !MSVC);
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), C *__restrict) == !MSVC);
+    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict) == !MSVC); };
+  };
+}
+
+
+template <typename T>
+void TC<T>::c() __restrict {
+  static_assert(__is_same(decltype(this), TC<int> *__restrict) == !MSVC);
+  (void) [this]() {
+    static_assert(__is_same(decltype(this), TC<int> *__restrict)  == !MSVC);
+    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict) == !MSVC); };
+  };
+}
+
 void f() {
   TC<int>{}.f();
+  TC<int>{}.a();
+  TC<int>{}.b();
+  TC<int>{}.c();
 }
 
 namespace gh18121 {

>From 215a53e59dc6e6384f6cb537974288d92fee67d5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 08:32:22 +0100
Subject: [PATCH 05/13] [Clang] Make restrict-this test more readable

---
 clang/test/SemaCXX/restrict-this.cpp | 68 ++++++++++++++++------------
 1 file changed, 39 insertions(+), 29 deletions(-)

diff --git a/clang/test/SemaCXX/restrict-this.cpp b/clang/test/SemaCXX/restrict-this.cpp
index f978458b620536..0df86726e75231 100644
--- a/clang/test/SemaCXX/restrict-this.cpp
+++ b/clang/test/SemaCXX/restrict-this.cpp
@@ -8,17 +8,28 @@
 // if the *declaration* is '__restrict'; otherwise, 'this' is '__restrict' if
 // the *definition* is '__restrict'.
 
+#define Restrict(C) static_assert(__is_same(decltype(this), C* __restrict))
+#define NotRestrict(C) static_assert(__is_same(decltype(this), C*) || __is_same(decltype(this), const C*))
+
+#if MSVC
+# define RestrictIfMSVC Restrict
+# define RestrictIfGCC NotRestrict
+#else
+# define RestrictIfMSVC NotRestrict
+# define RestrictIfGCC Restrict
+#endif
+
 struct C {
   void f() __restrict {
-    static_assert(__is_same(decltype(this), C *__restrict));
+    Restrict(C);
     (void) [this]() {
-      static_assert(__is_same(decltype(this), C *__restrict));
-      (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict)); };
+      Restrict(C);
+      (void) [this]() { Restrict(C); };
 
       // By-value capture means 'this' is now a different object; do not
       // make it __restrict.
-      (void) [*this]() { static_assert(__is_same(decltype(this), const C *)); };
-      (void) [*this]() mutable { static_assert(__is_same(decltype(this), C *)); };
+      (void) [*this]() { RestrictIfMSVC(C); };
+      (void) [*this]() mutable { RestrictIfMSVC(C); };
     };
   }
 
@@ -29,15 +40,15 @@ struct C {
 
 template <typename T> struct TC {
   void f() __restrict {
-    static_assert(__is_same(decltype(this), TC<int> *__restrict));
+    Restrict(TC);
     (void) [this]() {
-      static_assert(__is_same(decltype(this), TC<int> *__restrict));
-      (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict)); };
+      Restrict(TC);
+      (void) [this]() { Restrict(TC); };
 
       // By-value capture means 'this' is now a different object; do not
       // make it __restrict.
-      (void) [*this]() { static_assert(__is_same(decltype(this), const TC<int> *)); };
-      (void) [*this]() mutable { static_assert(__is_same(decltype(this), TC<int> *)); };
+      (void) [*this]() { RestrictIfMSVC(TC); };
+      (void) [*this]() mutable { RestrictIfMSVC(TC); };
     };
   }
 
@@ -49,58 +60,57 @@ template <typename T> struct TC {
 // =========
 
 void C::a() __restrict {
-  static_assert(__is_same(decltype(this), C *__restrict));
+  Restrict(C);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), C *__restrict));
-    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict)); };
+    Restrict(C);
+    (void) [*this]() { RestrictIfMSVC(C); };
   };
 }
 
 template <typename T>
 void TC<T>::a() __restrict {
-  static_assert(__is_same(decltype(this), TC<int> *__restrict));
+  Restrict(TC);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), TC<int> *__restrict));
-    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict)); };
+    Restrict(TC);
+    (void) [*this]() { RestrictIfMSVC(TC); };
   };
 }
 
 // =========
 
 void C::b() {
-  static_assert(__is_same(decltype(this), C *__restrict) == MSVC);
+  RestrictIfMSVC(C);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), C *__restrict) == MSVC);
-    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict) == MSVC); };
+    RestrictIfMSVC(C);
+    (void) [*this]() { RestrictIfMSVC(C); };
   };
 }
 
 template <typename T>
 void TC<T>::b() {
-  static_assert(__is_same(decltype(this), TC<int> *__restrict) == MSVC);
+  RestrictIfMSVC(TC);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), TC<int> *__restrict)  == MSVC);
-    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict) == MSVC); };
+    RestrictIfMSVC(TC);
+    (void) [*this]() { RestrictIfMSVC(TC); };
   };
 }
 
 // =========
 
 void C::c() __restrict {
-  static_assert(__is_same(decltype(this), C *__restrict) == !MSVC);
+  RestrictIfGCC(C);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), C *__restrict) == !MSVC);
-    (void) [this]() { static_assert(__is_same(decltype(this), C *__restrict) == !MSVC); };
+    RestrictIfGCC(C);
+    (void) [*this]() { NotRestrict(C); };
   };
 }
 
-
 template <typename T>
 void TC<T>::c() __restrict {
-  static_assert(__is_same(decltype(this), TC<int> *__restrict) == !MSVC);
+  RestrictIfGCC(TC);
   (void) [this]() {
-    static_assert(__is_same(decltype(this), TC<int> *__restrict)  == !MSVC);
-    (void) [this]() { static_assert(__is_same(decltype(this), TC<int> *__restrict) == !MSVC); };
+    RestrictIfGCC(TC);
+    (void) [*this]() { NotRestrict(TC); };
   };
 }
 

>From 5068271cde13d32e3bb80da852acbaa52f384527 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 11:13:29 +0100
Subject: [PATCH 06/13] [Clang] Proper handling of __restrict 'this' in
 MSVCCompat mode

---
 clang/include/clang/AST/DeclCXX.h    | 11 +++++-
 clang/lib/AST/DeclCXX.cpp            | 56 ++++++++++++++++++----------
 clang/lib/CodeGen/CGDebugInfo.cpp    |  6 +--
 clang/lib/Sema/SemaDeclCXX.cpp       | 20 ----------
 clang/lib/Sema/SemaExprCXX.cpp       | 14 +++++--
 clang/test/SemaCXX/restrict-this.cpp | 24 +++++++-----
 6 files changed, 73 insertions(+), 58 deletions(-)

diff --git a/clang/include/clang/AST/DeclCXX.h b/clang/include/clang/AST/DeclCXX.h
index 9cebaff63bb0db..11dac2a569ac4b 100644
--- a/clang/include/clang/AST/DeclCXX.h
+++ b/clang/include/clang/AST/DeclCXX.h
@@ -2208,13 +2208,20 @@ class CXXMethodDecl : public FunctionDecl {
     return getNumParams() - (isExplicitObjectMemberFunction() ? 1 : 0);
   }
 
-  static QualType getThisType(const FunctionProtoType *FPT,
-                              const CXXRecordDecl *Decl);
+  /// Get the type of the \c this pointer for the given method type to be used
+  /// in a MemberPointerType or similar.
+  static QualType getThisTypeForMemberPtr(const FunctionProtoType *FPT,
+                                          const CXXRecordDecl *Decl);
 
   Qualifiers getMethodQualifiers() const {
     return getType()->castAs<FunctionProtoType>()->getMethodQuals();
   }
 
+  /// Get whether this method should be considered '__restrict'-qualified,
+  /// irrespective of the presence or absence of '__restrict' on this
+  /// particular declaration.
+  bool isEffectivelyRestrict() const;
+
   /// Retrieve the ref-qualifier associated with this method.
   ///
   /// In the following example, \c f() has an lvalue ref-qualifier, \c g()
diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index b4f2327d9c560a..106844919057df 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2535,27 +2535,25 @@ CXXMethodDecl::overridden_methods() const {
 
 static QualType getThisObjectType(ASTContext &C, const FunctionProtoType *FPT,
                                   const CXXRecordDecl *Decl) {
+  // Unlike 'const' and 'volatile', a '__restrict' qualifier must be
+  // attached to the pointer type, not the pointee.
   QualType ClassTy = C.getTypeDeclType(Decl);
-  return C.getQualifiedType(ClassTy, FPT->getMethodQuals());
-}
-
-QualType CXXMethodDecl::getThisType(const FunctionProtoType *FPT,
-                                    const CXXRecordDecl *Decl) {
+  Qualifiers Qs = FPT->getMethodQuals();
+  Qs.removeRestrict();
+  return C.getQualifiedType(ClassTy, Qs);
+}
+
+// This may be called in cases where we simply don't have a CXXMethodDecl,
+// in which case we don't have enough information to reason whether the type
+// of 'this' should include '__restrict', however, since we would strip
+// '__restrict' in MemberPointerTypes anyway, this ends up not being much of
+// an issue.
+QualType CXXMethodDecl::getThisTypeForMemberPtr(const FunctionProtoType *FPT,
+                                                const CXXRecordDecl *Decl) {
   ASTContext &C = Decl->getASTContext();
   QualType ObjectTy = ::getThisObjectType(C, FPT, Decl);
-
-  // Unlike 'const' and 'volatile', a '__restrict' qualifier must be
-  // attached to the pointer type, not the pointee.
-  bool Restrict = FPT->getMethodQuals().hasRestrict();
-  if (Restrict)
-    ObjectTy.removeLocalRestrict();
-
-  ObjectTy = C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
-                                  : C.getPointerType(ObjectTy);
-
-  if (Restrict)
-    ObjectTy.addRestrict();
-  return ObjectTy;
+  return C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
+                              : C.getPointerType(ObjectTy);
 }
 
 QualType CXXMethodDecl::getThisType() const {
@@ -2565,8 +2563,26 @@ QualType CXXMethodDecl::getThisType() const {
   // volatile X*, and if the member function is declared const volatile,
   // the type of this is const volatile X*.
   assert(isInstance() && "No 'this' for static methods!");
-  return CXXMethodDecl::getThisType(getType()->castAs<FunctionProtoType>(),
-                                    getParent());
+  ASTContext &C = getASTContext();
+  auto FPT = getType()->castAs<FunctionProtoType>();
+
+  QualType ObjectTy = ::getThisObjectType(C, FPT, getParent());
+  ObjectTy = C.getLangOpts().HLSL ? C.getLValueReferenceType(ObjectTy)
+                                  : C.getPointerType(ObjectTy);
+
+  if (isEffectivelyRestrict())
+    ObjectTy.addRestrict();
+  return ObjectTy;
+}
+
+bool CXXMethodDecl::isEffectivelyRestrict() const {
+  // MSVC only cares about '__restrict' on the first declaration; GCC only
+  // cares about the definition.
+  Qualifiers Qs =
+      getASTContext().getLangOpts().MSVCCompat
+          ? cast<CXXMethodDecl>(getFirstDecl())->getMethodQualifiers()
+          : getMethodQualifiers();
+  return Qs.hasRestrict();
 }
 
 QualType CXXMethodDecl::getFunctionObjectParameterReferenceType() const {
diff --git a/clang/lib/CodeGen/CGDebugInfo.cpp b/clang/lib/CodeGen/CGDebugInfo.cpp
index c2c01439f2dc99..97cef4756c22e5 100644
--- a/clang/lib/CodeGen/CGDebugInfo.cpp
+++ b/clang/lib/CodeGen/CGDebugInfo.cpp
@@ -3322,9 +3322,9 @@ llvm::DIType *CGDebugInfo::CreateType(const MemberPointerType *Ty,
   const FunctionProtoType *FPT =
       Ty->getPointeeType()->castAs<FunctionProtoType>();
   return DBuilder.createMemberPointerType(
-      getOrCreateInstanceMethodType(
-          CXXMethodDecl::getThisType(FPT, Ty->getMostRecentCXXRecordDecl()),
-          FPT, U),
+      getOrCreateInstanceMethodType(CXXMethodDecl::getThisTypeForMemberPtr(
+                                        FPT, Ty->getMostRecentCXXRecordDecl()),
+                                    FPT, U),
       ClassType, Size, /*Align=*/0, Flags);
 }
 
diff --git a/clang/lib/Sema/SemaDeclCXX.cpp b/clang/lib/Sema/SemaDeclCXX.cpp
index 0dc4e133673abf..d4e1dc67cb50a1 100644
--- a/clang/lib/Sema/SemaDeclCXX.cpp
+++ b/clang/lib/Sema/SemaDeclCXX.cpp
@@ -725,26 +725,6 @@ bool Sema::MergeCXXFunctionDecl(FunctionDecl *New, FunctionDecl *Old,
       Old->isDefined(OldDefinition, true))
     CheckForFunctionRedefinition(New, OldDefinition);
 
-  // Both GCC and MSVC allow a mismatch in '__restrict'-qualification between
-  // the old and new declarations' cv-qualifier-seq's. Unfortunately, they
-  // also handle this mismatch differently: GCC considers '__restrict' on the
-  // definition to be authoritative (i.e. the type of 'this' is '__restrict'
-  // in the body of a definition, if that definition is '__restrict'-qualified),
-  // while MSVC does the opposite: it only looks at the first declaration).
-  //
-  // To support this behaviour, copy '__restrict' from the old declaration to
-  // the new one in MSVC mode only.
-  if (isa<CXXMethodDecl>(New) and getLangOpts().MSVCCompat) {
-    auto NewType = New->getType()->castAs<FunctionProtoType>();
-    auto Quals = Old->getType()->castAs<FunctionProtoType>()->getMethodQuals();
-    if (Quals.hasRestrict() && !NewType->getMethodQuals().hasRestrict()) {
-      FunctionProtoType::ExtProtoInfo EPI = NewType->getExtProtoInfo();
-      EPI.TypeQuals.addRestrict();
-      New->setType(Context.getFunctionType(NewType->getReturnType(),
-                                               NewType->getParamTypes(), EPI));
-    }
-  }
-
   return Invalid;
 }
 
diff --git a/clang/lib/Sema/SemaExprCXX.cpp b/clang/lib/Sema/SemaExprCXX.cpp
index c4750ce78fa9c1..5c3c1be604c1c3 100644
--- a/clang/lib/Sema/SemaExprCXX.cpp
+++ b/clang/lib/Sema/SemaExprCXX.cpp
@@ -1130,8 +1130,8 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
   // member function.  We then start with the innermost lambda and iterate
   // outward checking to see if any lambda performs a by-copy capture of '*this'
   // - and if so, any nested lambda must respect the 'constness' of that
-  // capturing lamdbda's call operator.
-  //
+  // capturing lamdbda's call operator. In MSVCCompat mode, we also consider
+  // 'this' to be '__restrict' even if it is captured by copy.
 
   // Since the FunctionScopeInfo stack is representative of the lexical
   // nesting of the lambda expressions during initial parsing (and is the best
@@ -1174,7 +1174,10 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
     if (C.isCopyCapture()) {
       if (CurLSI->lambdaCaptureShouldBeConst())
         ClassType.addConst();
-      return ASTCtx.getPointerType(ClassType);
+      auto Ptr = ASTCtx.getPointerType(ClassType);
+      if (ThisTy.isRestrictQualified() && ASTCtx.getLangOpts().MSVCCompat)
+        Ptr.addRestrict();
+      return Ptr;
     }
   }
 
@@ -1213,7 +1216,10 @@ static QualType adjustCVQualifiersForCXXThisWithinLambda(
       if (IsByCopyCapture) {
         if (IsConstCapture)
           ClassType.addConst();
-        return ASTCtx.getPointerType(ClassType);
+        auto Ptr = ASTCtx.getPointerType(ClassType);
+        if (ThisTy.isRestrictQualified() && ASTCtx.getLangOpts().MSVCCompat)
+          Ptr.addRestrict();
+        return Ptr;
       }
       Closure = isLambdaCallOperator(Closure->getParent())
                     ? cast<CXXRecordDecl>(Closure->getParent()->getParent())
diff --git a/clang/test/SemaCXX/restrict-this.cpp b/clang/test/SemaCXX/restrict-this.cpp
index 0df86726e75231..e4b1e31bbd87e6 100644
--- a/clang/test/SemaCXX/restrict-this.cpp
+++ b/clang/test/SemaCXX/restrict-this.cpp
@@ -9,7 +9,7 @@
 // the *definition* is '__restrict'.
 
 #define Restrict(C) static_assert(__is_same(decltype(this), C* __restrict))
-#define NotRestrict(C) static_assert(__is_same(decltype(this), C*) || __is_same(decltype(this), const C*))
+#define NotRestrict(C) static_assert(__is_same(decltype(this), C*))
 
 #if MSVC
 # define RestrictIfMSVC Restrict
@@ -28,7 +28,7 @@ struct C {
 
       // By-value capture means 'this' is now a different object; do not
       // make it __restrict.
-      (void) [*this]() { RestrictIfMSVC(C); };
+      (void) [*this]() { RestrictIfMSVC(const C); };
       (void) [*this]() mutable { RestrictIfMSVC(C); };
     };
   }
@@ -47,7 +47,7 @@ template <typename T> struct TC {
 
       // By-value capture means 'this' is now a different object; do not
       // make it __restrict.
-      (void) [*this]() { RestrictIfMSVC(TC); };
+      (void) [*this]() { RestrictIfMSVC(const TC); };
       (void) [*this]() mutable { RestrictIfMSVC(TC); };
     };
   }
@@ -63,7 +63,8 @@ void C::a() __restrict {
   Restrict(C);
   (void) [this]() {
     Restrict(C);
-    (void) [*this]() { RestrictIfMSVC(C); };
+    (void) [*this]() { RestrictIfMSVC(const C); };
+    (void) [*this]() mutable { RestrictIfMSVC(C); };
   };
 }
 
@@ -72,7 +73,8 @@ void TC<T>::a() __restrict {
   Restrict(TC);
   (void) [this]() {
     Restrict(TC);
-    (void) [*this]() { RestrictIfMSVC(TC); };
+    (void) [*this]() { RestrictIfMSVC(const TC); };
+    (void) [*this]() mutable { RestrictIfMSVC(TC); };
   };
 }
 
@@ -82,7 +84,8 @@ void C::b() {
   RestrictIfMSVC(C);
   (void) [this]() {
     RestrictIfMSVC(C);
-    (void) [*this]() { RestrictIfMSVC(C); };
+    (void) [*this]() { RestrictIfMSVC(const C); };
+    (void) [*this]() mutable { RestrictIfMSVC(C); };
   };
 }
 
@@ -91,7 +94,8 @@ void TC<T>::b() {
   RestrictIfMSVC(TC);
   (void) [this]() {
     RestrictIfMSVC(TC);
-    (void) [*this]() { RestrictIfMSVC(TC); };
+    (void) [*this]() { RestrictIfMSVC(const TC); };
+    (void) [*this]() mutable { RestrictIfMSVC(TC); };
   };
 }
 
@@ -101,7 +105,8 @@ void C::c() __restrict {
   RestrictIfGCC(C);
   (void) [this]() {
     RestrictIfGCC(C);
-    (void) [*this]() { NotRestrict(C); };
+    (void) [*this]() { NotRestrict(const C); };
+    (void) [*this]() mutable { NotRestrict(C); };
   };
 }
 
@@ -110,7 +115,8 @@ void TC<T>::c() __restrict {
   RestrictIfGCC(TC);
   (void) [this]() {
     RestrictIfGCC(TC);
-    (void) [*this]() { NotRestrict(TC); };
+    (void) [*this]() { NotRestrict(const TC); };
+    (void) [*this]() mutable { NotRestrict(TC); };
   };
 }
 

>From 7b5b6828f41853d3a3ebb9072c164a749f6e6012 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 13:39:52 +0100
Subject: [PATCH 07/13] [Clang] Properly propagate __restrict in member
 function templates

---
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 19 +++++++++++++++++++
 1 file changed, 19 insertions(+)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 9c696e072ba4a7..7e23610b6349e9 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5052,6 +5052,25 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   Function->setInnerLocStart(PatternDecl->getInnerLocStart());
   Function->setRangeEnd(PatternDecl->getEndLoc());
 
+  // In non-MSVCCompat mode, we only care about the presence of a
+  // '__restrict' qualifier in the cv-qualifier-seq of a member
+  // function, and not its declaration.
+  if (auto MD = dyn_cast<CXXMethodDecl>(Function);
+      MD && !getLangOpts().MSVCCompat) {
+    bool Restrict =
+        cast<CXXMethodDecl>(PatternDecl)->getMethodQualifiers().hasRestrict();
+    if (Restrict != MD->getMethodQualifiers().hasRestrict()) {
+      const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
+      FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
+      if (Restrict)
+        EPI.TypeQuals.addRestrict();
+      else
+        EPI.TypeQuals.removeRestrict();
+      MD->setType(Context.getFunctionType(FPT->getReturnType(),
+                                          FPT->getParamTypes(), EPI));
+    }
+  }
+
   EnterExpressionEvaluationContext EvalContext(
       *this, Sema::ExpressionEvaluationContext::PotentiallyEvaluated);
 

>From e9e0123fadc825f7a34a7fe111522d1b15d2e422 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 13:57:03 +0100
Subject: [PATCH 08/13] [Clang] More tests for __restrict member functions

---
 .../restrict-member-function-matching.cpp     | 49 +++++++++++++------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/clang/test/SemaCXX/restrict-member-function-matching.cpp b/clang/test/SemaCXX/restrict-member-function-matching.cpp
index 5df5180f3d8e48..a97613554aa163 100644
--- a/clang/test/SemaCXX/restrict-member-function-matching.cpp
+++ b/clang/test/SemaCXX/restrict-member-function-matching.cpp
@@ -1,5 +1,5 @@
-// RUN: %clang_cc1 -fsyntax-only -verify -DMSVC=false %s
-// RUN: %clang_cc1 -fsyntax-only -verify -triple=x86_64-pc-win32 -fms-compatibility -DMSVC=true %s
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// RUN: %clang_cc1 -fsyntax-only -verify -triple=x86_64-pc-win32 -fms-compatibility %s
 // expected-no-diagnostics
 
 // GCC and MSVC allows '__restrict' in the cv-qualifier-seq of member functions
@@ -27,13 +27,14 @@ void S::a() __restrict { }
 void S::b() { }
 void S::c() __restrict { }
 
-static_assert(__is_same(void (S::*) (), void (S::*) () __restrict));
-static_assert(__is_same(void (S::*) () &, void (S::*) () __restrict &));
-static_assert(__is_same(void (S::*) () &&, void (S::*) () __restrict &&));
+void (S::*p1)() __restrict = &S::a;
+void (S::*p2)()            = &S::a;
+void (S::*p3)() __restrict = &S::c;
+void (S::*p4)()            = &S::c;
 
-static_assert(is_same<void (S::*) (), void (S::*) () __restrict>::value);
-static_assert(is_same<void (S::*) () &, void (S::*) () __restrict &>::value);
-static_assert(is_same<void (S::*) () &&, void (S::*) () __restrict &&>::value);
+static_assert(__is_same(void (S::*) (), void (S::*) () __restrict));
+static_assert(__is_same(void (S::*) () const &, void (S::*) () __restrict const &));
+static_assert(__is_same(void (S::*) () volatile &&, void (S::*) () volatile __restrict &&));
 
 static_assert(__is_same(decltype(&S::a), void (S::*) () __restrict));
 static_assert(__is_same(decltype(&S::a), void (S::*) ()));
@@ -42,12 +43,32 @@ static_assert(__is_same(decltype(&S::b), void (S::*) ()));
 static_assert(__is_same(decltype(&S::c), void (S::*) () __restrict));
 static_assert(__is_same(decltype(&S::c), void (S::*) ()));
 
-static_assert(is_same<decltype(&S::a), void (S::*) () __restrict>::value);
-static_assert(is_same<decltype(&S::a), void (S::*) ()>::value);
-static_assert(is_same<decltype(&S::b), void (S::*) () __restrict>::value);
-static_assert(is_same<decltype(&S::b), void (S::*) ()>::value);
-static_assert(is_same<decltype(&S::c), void (S::*) () __restrict>::value);
-static_assert(is_same<decltype(&S::c), void (S::*) ()>::value);
+template <typename>
+struct TS {
+    void a() __restrict;
+    void b() __restrict;
+    void c();
+};
+
+template <typename T>
+void TS<T>::b() __restrict { }
+
+template <typename T>
+void TS<T>::c() { }
+
+void (TS<int>::*p5)() __restrict = &TS<int>::a;
+void (TS<int>::*p6)()            = &TS<int>::a;
+void (TS<int>::*p7)() __restrict = &TS<int>::c;
+void (TS<int>::*p8)()            = &TS<int>::c;
+
+void h() {
+    TS<int>().a();
+    TS<int>().b();
+    TS<int>().c();
+    TS<double>().a();
+    TS<double>().b();
+    TS<double>().c();
+}
 
 // Non-member function types with '__restrict' are distinct types.
 using A = void () __restrict;

>From 18726cf9f7273be82e570cbde6120d56d0f9473a Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 14:08:32 +0100
Subject: [PATCH 09/13] [Clang] More tests with function templates

---
 clang/test/SemaCXX/restrict-this.cpp | 83 ++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/clang/test/SemaCXX/restrict-this.cpp b/clang/test/SemaCXX/restrict-this.cpp
index e4b1e31bbd87e6..186035edc0eeaa 100644
--- a/clang/test/SemaCXX/restrict-this.cpp
+++ b/clang/test/SemaCXX/restrict-this.cpp
@@ -36,6 +36,15 @@ struct C {
   void a() __restrict;
   void b() __restrict;
   void c();
+
+  template <typename U>
+  void ta() __restrict;
+
+  template <typename U>
+  void tb() __restrict;
+
+  template <typename U>
+  void tc();
 };
 
 template <typename T> struct TC {
@@ -55,6 +64,15 @@ template <typename T> struct TC {
   void a() __restrict;
   void b() __restrict;
   void c();
+
+  template <typename U>
+  void ta() __restrict;
+
+  template <typename U>
+  void tb() __restrict;
+
+  template <typename U>
+  void tc();
 };
 
 // =========
@@ -78,6 +96,27 @@ void TC<T>::a() __restrict {
   };
 }
 
+template <typename T>
+void C::ta() __restrict {
+  Restrict(C);
+  (void) [this]() {
+    Restrict(C);
+    (void) [*this]() { RestrictIfMSVC(const C); };
+    (void) [*this]() mutable { RestrictIfMSVC(C); };
+  };
+}
+
+template <typename T>
+template <typename U>
+void TC<T>::ta() __restrict {
+  Restrict(TC);
+  (void) [this]() {
+    Restrict(TC);
+    (void) [*this]() { RestrictIfMSVC(const TC); };
+    (void) [*this]() mutable { RestrictIfMSVC(TC); };
+  };
+}
+
 // =========
 
 void C::b() {
@@ -99,6 +138,27 @@ void TC<T>::b() {
   };
 }
 
+template <typename T>
+void C::tb() {
+  RestrictIfMSVC(C);
+  (void) [this]() {
+    RestrictIfMSVC(C);
+    (void) [*this]() { RestrictIfMSVC(const C); };
+    (void) [*this]() mutable { RestrictIfMSVC(C); };
+  };
+}
+
+template <typename T>
+template <typename U>
+void TC<T>::tb() {
+  RestrictIfMSVC(TC);
+  (void) [this]() {
+    RestrictIfMSVC(TC);
+    (void) [*this]() { RestrictIfMSVC(const TC); };
+    (void) [*this]() mutable { RestrictIfMSVC(TC); };
+  };
+}
+
 // =========
 
 void C::c() __restrict {
@@ -120,6 +180,29 @@ void TC<T>::c() __restrict {
   };
 }
 
+template <typename T>
+void C::tc() __restrict {
+  RestrictIfGCC(C);
+  (void) [this]() {
+    RestrictIfGCC(C);
+    (void) [*this]() { NotRestrict(const C); };
+    (void) [*this]() mutable { NotRestrict(C); };
+  };
+}
+
+template <typename T>
+template <typename U>
+void TC<T>::tc() __restrict {
+  RestrictIfGCC(TC);
+  (void) [this]() {
+    RestrictIfGCC(TC);
+    (void) [*this]() { NotRestrict(const TC); };
+    (void) [*this]() mutable { NotRestrict(TC); };
+  };
+}
+
+// =========
+
 void f() {
   TC<int>{}.f();
   TC<int>{}.a();

>From a5739dd698cc64e45451e308456308e513ba558e Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 16:00:34 +0100
Subject: [PATCH 10/13] [Clang] Propagate __restrict properly in template
 instantiation

---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 10 +++-------
 clang/test/SemaCXX/restrict-this.cpp           |  6 ++++++
 2 files changed, 9 insertions(+), 7 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 7e23610b6349e9..7353faecdcdd8f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5052,13 +5052,9 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   Function->setInnerLocStart(PatternDecl->getInnerLocStart());
   Function->setRangeEnd(PatternDecl->getEndLoc());
 
-  // In non-MSVCCompat mode, we only care about the presence of a
-  // '__restrict' qualifier in the cv-qualifier-seq of a member
-  // function, and not its declaration.
-  if (auto MD = dyn_cast<CXXMethodDecl>(Function);
-      MD && !getLangOpts().MSVCCompat) {
-    bool Restrict =
-        cast<CXXMethodDecl>(PatternDecl)->getMethodQualifiers().hasRestrict();
+  // Propagate '__restrict' properly.
+  if (auto MD = dyn_cast<CXXMethodDecl>(Function)) {
+    bool Restrict = cast<CXXMethodDecl>(PatternDecl)->isEffectivelyRestrict();
     if (Restrict != MD->getMethodQualifiers().hasRestrict()) {
       const auto *FPT = MD->getType()->getAs<FunctionProtoType>();
       FunctionProtoType::ExtProtoInfo EPI = FPT->getExtProtoInfo();
diff --git a/clang/test/SemaCXX/restrict-this.cpp b/clang/test/SemaCXX/restrict-this.cpp
index 186035edc0eeaa..3575ea2a3f561f 100644
--- a/clang/test/SemaCXX/restrict-this.cpp
+++ b/clang/test/SemaCXX/restrict-this.cpp
@@ -204,10 +204,16 @@ void TC<T>::tc() __restrict {
 // =========
 
 void f() {
+  C{}.ta<int>();
+  C{}.tb<int>();
+  C{}.tc<int>();
   TC<int>{}.f();
   TC<int>{}.a();
   TC<int>{}.b();
   TC<int>{}.c();
+  TC<int>{}.ta<int>();
+  TC<int>{}.tb<int>();
+  TC<int>{}.tc<int>();
 }
 
 namespace gh18121 {

>From 03dba07fbead6ee010d4a7e02f6a713d5d0bbb1d Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 16:01:40 +0100
Subject: [PATCH 11/13] [NFC] clang-format

---
 clang/lib/AST/ASTContext.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/clang/lib/AST/ASTContext.cpp b/clang/lib/AST/ASTContext.cpp
index abc5e3a9840d85..a03fa353673858 100644
--- a/clang/lib/AST/ASTContext.cpp
+++ b/clang/lib/AST/ASTContext.cpp
@@ -3479,7 +3479,8 @@ QualType ASTContext::getRValueReferenceType(QualType T) const {
   return QualType(New, 0);
 }
 
-QualType ASTContext::getMemberPointerTypeInternal(QualType T, const Type *Cls) const {
+QualType ASTContext::getMemberPointerTypeInternal(QualType T,
+                                                  const Type *Cls) const {
   // Unique pointers, to guarantee there is only one pointer of a particular
   // structure.
   llvm::FoldingSetNodeID ID;
@@ -4436,8 +4437,7 @@ QualType ASTContext::getFunctionTypeInternal(
 
   // Determine whether the type being created is already canonical or not.
   bool isCanonical = !Unique && IsCanonicalExceptionSpec &&
-                     isCanonicalResultType(ResultTy) &&
-                     !EPI.HasTrailingReturn;
+                     isCanonicalResultType(ResultTy) && !EPI.HasTrailingReturn;
   for (unsigned i = 0; i != NumArgs && isCanonical; ++i)
     if (!ArgArray[i].isCanonicalAsParam())
       isCanonical = false;

>From b6dc450ff1dcf312b897d218136d08155dcda5d5 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Mon, 4 Mar 2024 16:12:21 +0100
Subject: [PATCH 12/13] [Clang] Actually check definition for __restrict

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

diff --git a/clang/lib/AST/DeclCXX.cpp b/clang/lib/AST/DeclCXX.cpp
index 106844919057df..34968fd1c1181c 100644
--- a/clang/lib/AST/DeclCXX.cpp
+++ b/clang/lib/AST/DeclCXX.cpp
@@ -2578,11 +2578,13 @@ QualType CXXMethodDecl::getThisType() const {
 bool CXXMethodDecl::isEffectivelyRestrict() const {
   // MSVC only cares about '__restrict' on the first declaration; GCC only
   // cares about the definition.
-  Qualifiers Qs =
-      getASTContext().getLangOpts().MSVCCompat
-          ? cast<CXXMethodDecl>(getFirstDecl())->getMethodQualifiers()
-          : getMethodQualifiers();
-  return Qs.hasRestrict();
+  if (getASTContext().getLangOpts().MSVCCompat) {
+    Qualifiers Qs = cast<CXXMethodDecl>(getFirstDecl())->getMethodQualifiers();
+    return Qs.hasRestrict();
+  }
+
+  const auto *D = getDefinition();
+  return D && cast<CXXMethodDecl>(D)->getMethodQualifiers().hasRestrict();
 }
 
 QualType CXXMethodDecl::getFunctionObjectParameterReferenceType() const {

>From 2ddf9003e2d2f922af8bea16a41f4315768c80c3 Mon Sep 17 00:00:00 2001
From: Sirraide <aeternalmail at gmail.com>
Date: Tue, 9 Apr 2024 14:35:20 +0200
Subject: [PATCH 13/13] [NFC] Expand comment a bit

---
 clang/lib/Sema/SemaTemplateInstantiateDecl.cpp | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 0e2ed20efe5741..4f1d12581d4a0f 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -5081,7 +5081,14 @@ void Sema::InstantiateFunctionDefinition(SourceLocation PointOfInstantiation,
   Function->setInnerLocStart(PatternDecl->getInnerLocStart());
   Function->setRangeEnd(PatternDecl->getEndLoc());
 
-  // Propagate '__restrict' properly.
+  // Propagate '__restrict' (or lack thereof) properly.
+  //
+  // Since '__restrict'-ness is allowed to differ between the declaration and
+  // definition of a function, we need to propagate the '__restrict'-ness of
+  // the template declaration to the instantiated declaration, and that of the
+  // template definition to the instantiated definition. The former happens
+  // automatically during template instantiation, so we only need to handle
+  // the latter here.
   if (auto MD = dyn_cast<CXXMethodDecl>(Function)) {
     bool Restrict = cast<CXXMethodDecl>(PatternDecl)->isEffectivelyRestrict();
     if (Restrict != MD->getMethodQualifiers().hasRestrict()) {



More information about the cfe-commits mailing list