https://github.com/hax0kartik updated 
https://github.com/llvm/llvm-project/pull/190595

>From f9e7274154debea939835a7a0f51400ec3a5a408 Mon Sep 17 00:00:00 2001
From: hax0kartik <[email protected]>
Date: Wed, 1 Apr 2026 13:11:50 +0530
Subject: [PATCH 1/2] [Clang] Fix SFINAE access check for protected in a
 dependent context

getPatternForClassTemplateSpecialization() eventually calls
CheckConstructorAccess() for a partial spec which adds a
DelayedDiagnostic() to the partial. However, this was not
getting handled. Further, the DelayedDiagnostic() is lost,
when `PartialSpec = PartialSpec->getInstantiatedFromMember();`
is called.

Fix this by modifying getPatternForClassTemplateSpecialization() to
explicitly handle a delayed diagnostic (delayed access check)
which might be attached to a partial spec.
---
 clang/include/clang/AST/DependentDiagnostic.h |  8 +++
 clang/include/clang/Sema/Sema.h               |  3 +-
 clang/lib/Sema/SemaTemplateInstantiate.cpp    | 23 +++++-
 .../lib/Sema/SemaTemplateInstantiateDecl.cpp  | 13 ++--
 .../test/SemaCXX/dependent-context-SFINAE.cpp | 71 +++++++++++++++++++
 5 files changed, 109 insertions(+), 9 deletions(-)
 create mode 100644 clang/test/SemaCXX/dependent-context-SFINAE.cpp

diff --git a/clang/include/clang/AST/DependentDiagnostic.h 
b/clang/include/clang/AST/DependentDiagnostic.h
index cadf970620041..68500bddced7b 100644
--- a/clang/include/clang/AST/DependentDiagnostic.h
+++ b/clang/include/clang/AST/DependentDiagnostic.h
@@ -48,6 +48,7 @@ class DependentDiagnostic {
                                      QualType BaseObjectType,
                                      const PartialDiagnostic &PDiag) {
     DependentDiagnostic *DD = Create(Context, Parent, PDiag);
+    DD->IsActive = true;
     DD->AccessData.Loc = Loc;
     DD->AccessData.IsMember = IsMemberAccess;
     DD->AccessData.Access = AS;
@@ -66,6 +67,10 @@ class DependentDiagnostic {
     return AccessData.IsMember;
   }
 
+  bool isActive() const { return IsActive; }
+
+  void setActive(bool active) { IsActive = active; }
+
   AccessSpecifier getAccess() const {
     assert(getKind() == Access);
     return AccessSpecifier(AccessData.Access);
@@ -111,6 +116,9 @@ class DependentDiagnostic {
 
   PartialDiagnostic Diag;
 
+  LLVM_PREFERRED_TYPE(bool)
+  unsigned IsActive : 1;
+
   struct {
     SourceLocation Loc;
     LLVM_PREFERRED_TYPE(AccessSpecifier)
diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h
index 1b8a9803be472..09fc01865b7e6 100644
--- a/clang/include/clang/Sema/Sema.h
+++ b/clang/include/clang/Sema/Sema.h
@@ -14415,7 +14415,8 @@ class Sema final : public SemaBase {
 
   void PerformDependentDiagnostics(
       const DeclContext *Pattern,
-      const MultiLevelTemplateArgumentList &TemplateArgs);
+      const MultiLevelTemplateArgumentList &TemplateArgs,
+      bool MarkInactive = false);
 
 private:
   /// Introduce the instantiated local variables into the local
diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 34ed5dffa11b4..8f27f936f5b47 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -16,6 +16,7 @@
 #include "clang/AST/ASTMutationListener.h"
 #include "clang/AST/DeclBase.h"
 #include "clang/AST/DeclTemplate.h"
+#include "clang/AST/DependentDiagnostic.h"
 #include "clang/AST/DynamicRecursiveASTVisitor.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprConcepts.h"
@@ -3946,9 +3947,24 @@ static ActionResult<CXXRecordDecl *> 
getPatternForClassTemplateSpecialization(
         continue;
 
       TemplateDeductionInfo Info(FailedCandidates.getLocation());
-      if (TemplateDeductionResult Result = S.DeduceTemplateArguments(
-              Partial, ClassTemplateSpec->getTemplateArgs().asArray(), Info);
-          Result != TemplateDeductionResult::Success) {
+
+      TemplateDeductionResult Result = S.DeduceTemplateArguments(
+          Partial, ClassTemplateSpec->getTemplateArgs().asArray(), Info);
+
+      if (Result == TemplateDeductionResult::Success) {
+        // Handle any dependent diags that might have been created for access
+        // checks Reject Candidate if it fails access checks.
+        if (Partial->isDependentContext()) {
+          Sema::SFINAETrap Trap(S, Info);
+          S.PerformDependentDiagnostics(
+              Partial, S.getTemplateInstantiationArgs(ClassTemplateSpec), 
true);
+          if (Trap.hasErrorOccurred()) {
+            Result = TemplateDeductionResult::SubstitutionFailure;
+          }
+        }
+      }
+
+      if (Result != TemplateDeductionResult::Success) {
         // Store the failed-deduction information for use in diagnostics, 
later.
         // TODO: Actually use the failed-deduction info?
         FailedCandidates.addCandidate().set(
@@ -3960,6 +3976,7 @@ static ActionResult<CXXRecordDecl *> 
getPatternForClassTemplateSpecialization(
         List.push_back(MatchResult{Partial, Info.takeCanonical()});
       }
     }
+
     if (Matched.empty() && PrimaryStrictPackMatch)
       Matched = std::move(ExtraMatched);
 
diff --git a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp 
b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
index 61878cba32235..dd95cb3524520 100644
--- a/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiateDecl.cpp
@@ -7292,13 +7292,16 @@ void Sema::PerformPendingInstantiations(bool LocalOnly, 
bool AtEndOfTU) {
     PendingInstantiations.swap(DelayedImplicitInstantiations);
 }
 
-void Sema::PerformDependentDiagnostics(const DeclContext *Pattern,
-                       const MultiLevelTemplateArgumentList &TemplateArgs) {
+void Sema::PerformDependentDiagnostics(
+    const DeclContext *Pattern,
+    const MultiLevelTemplateArgumentList &TemplateArgs, bool MarkInactive) {
   for (auto *DD : Pattern->ddiags()) {
-    switch (DD->getKind()) {
-    case DependentDiagnostic::Access:
+    if (DD->getKind() == DependentDiagnostic::Access && DD->isActive()) {
       HandleDependentAccessCheck(*DD, TemplateArgs);
-      break;
+
+      if (MarkInactive) {
+        DD->setActive(false);
+      }
     }
   }
 }
diff --git a/clang/test/SemaCXX/dependent-context-SFINAE.cpp 
b/clang/test/SemaCXX/dependent-context-SFINAE.cpp
new file mode 100644
index 0000000000000..fd81d4ca37093
--- /dev/null
+++ b/clang/test/SemaCXX/dependent-context-SFINAE.cpp
@@ -0,0 +1,71 @@
+// RUN: %clang_cc1 -fsyntax-only -verify %s
+// expected-no-diagnostics
+
+class PublicBase {
+    public:
+    PublicBase(int i);
+    using type = int;
+};
+
+class ProtectedBase {
+    protected:
+    ProtectedBase(int i);
+    using type = int;
+};
+
+class FriendProtectedBase {
+    template <typename U> friend class Derived;
+
+    protected:
+    FriendProtectedBase(int i);
+    using type = int;
+};
+
+class PrivateBase {
+    private:
+    PrivateBase(int i);
+    using type = int;
+};
+
+template <typename... Ts>
+using void_t = void;
+
+template <typename U>
+class Derived {
+    public:
+    // SFINAE Pattern 1 - Checks whether constructor is accessible
+    template <typename T, typename = void>
+    struct ConstructsBase {
+        static constexpr int value = 0;
+    };
+
+    template <typename T>
+    struct ConstructsBase<
+        T, void_t<decltype(T(0))>> {
+            static constexpr int value = 1;
+        };
+
+    // SFINAE Pattern 2 - Checks whether type alias is accessible
+    template <typename T, typename = void>
+    struct HasAccessibleType {
+        static constexpr int value = 0;
+    };
+
+    template <typename T>
+    struct HasAccessibleType<
+        T,
+        void_t<typename T::type>> 
+    {
+        static constexpr int value = 1;
+    };
+};
+
+static_assert(Derived<int>::ConstructsBase<PublicBase>::value);
+static_assert(!Derived<int>::ConstructsBase<ProtectedBase>::value);
+static_assert(Derived<int>::ConstructsBase<FriendProtectedBase>::value);
+static_assert(!Derived<int>::ConstructsBase<PrivateBase>::value);
+
+static_assert(Derived<int>::HasAccessibleType<PublicBase>::value);
+static_assert(!Derived<int>::HasAccessibleType<ProtectedBase>::value);
+static_assert(Derived<int>::HasAccessibleType<FriendProtectedBase>::value);
+static_assert(!Derived<int>::HasAccessibleType<PrivateBase>::value);
\ No newline at end of file

>From 4719360f5f499cffd9d15f9e40698f338b63b892 Mon Sep 17 00:00:00 2001
From: hax0kartik <[email protected]>
Date: Sun, 12 Apr 2026 10:47:24 +0530
Subject: [PATCH 2/2] [Clang] Use correct context in SFINAE access check

In the change before this, wrong Context was being used while
calling PerformDependentDiagnostic. Fix this by making the
context point to the partial spec.
---
 clang/lib/Sema/SemaTemplateInstantiate.cpp | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp 
b/clang/lib/Sema/SemaTemplateInstantiate.cpp
index 8f27f936f5b47..3e11f7c22d547 100644
--- a/clang/lib/Sema/SemaTemplateInstantiate.cpp
+++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp
@@ -3899,6 +3899,12 @@ bool Sema::usesPartialOrExplicitSpecialization(
   return false;
 }
 
+static DeclContext *getAsDeclContextOrEnclosing(Decl *D) {
+  if (auto *DC = dyn_cast<DeclContext>(D))
+    return DC;
+  return D->getDeclContext();
+}
+
 /// Get the instantiation pattern to use to instantiate the definition of a
 /// given ClassTemplateSpecializationDecl (either the pattern of the primary
 /// template or of a partial specialization).
@@ -3953,11 +3959,15 @@ static ActionResult<CXXRecordDecl *> 
getPatternForClassTemplateSpecialization(
 
       if (Result == TemplateDeductionResult::Success) {
         // Handle any dependent diags that might have been created for access
-        // checks Reject Candidate if it fails access checks.
+        // checks. Reject Candidate if it fails access checks.
         if (Partial->isDependentContext()) {
+          auto *Decl = Partial->hasDefinition()
+                           ? Partial
+                           : Partial->getInstantiatedFromMember();
+          Sema::ContextRAII SavedContext(S, getAsDeclContextOrEnclosing(Decl));
           Sema::SFINAETrap Trap(S, Info);
           S.PerformDependentDiagnostics(
-              Partial, S.getTemplateInstantiationArgs(ClassTemplateSpec), 
true);
+              Partial, S.getTemplateInstantiationArgs(Partial), true);
           if (Trap.hasErrorOccurred()) {
             Result = TemplateDeductionResult::SubstitutionFailure;
           }

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to