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

getPatternForClassTemplateSpecialization() eventually calls 
CheckConstructorAccess() for a partial spec which adds a DelayedDiagnostic() to 
the partial. However, this was not getting handled. Furthermore, the 
DelayedDiagnostic() is lost, when later in 
getPatternForClassTemplateSpecialization() `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.

Fixes #103895 and #62053.

>From 903afdebdcb14047f959f701ecdc26a1d2835b86 Mon Sep 17 00:00:00 2001
From: hax0kartik <[email protected]>
Date: Wed, 1 Apr 2026 13:11:50 +0530
Subject: [PATCH] [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 | 12 ++++
 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, 113 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..df66e912e7fbc 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,14 @@ 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 +120,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..76697f05e32ab
--- /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:
+    // SNIFAE Pattern 1 - Checks whether constructor is acessible
+    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 acessible
+    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

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

Reply via email to