llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Kartik (hax0kartik) <details> <summary>Changes</summary> 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. --- Full diff: https://github.com/llvm/llvm-project/pull/190595.diff 5 Files Affected: - (modified) clang/include/clang/AST/DependentDiagnostic.h (+12) - (modified) clang/include/clang/Sema/Sema.h (+2-1) - (modified) clang/lib/Sema/SemaTemplateInstantiate.cpp (+20-3) - (modified) clang/lib/Sema/SemaTemplateInstantiateDecl.cpp (+8-5) - (added) clang/test/SemaCXX/dependent-context-SFINAE.cpp (+71) ``````````diff 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 `````````` </details> https://github.com/llvm/llvm-project/pull/190595 _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
