[clang] [Clang] Fix SFINAE access check for protected in a dependent context (PR #190595)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Apr 6 02:25:36 PDT 2026
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 <agarwala.kartik at gmail.com>
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
More information about the cfe-commits
mailing list