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] [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 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
