Author: I Date: 2026-03-11T11:52:31Z New Revision: 3669d2e4dc2bf21b646a638c6209dea86f94e3d8
URL: https://github.com/llvm/llvm-project/commit/3669d2e4dc2bf21b646a638c6209dea86f94e3d8 DIFF: https://github.com/llvm/llvm-project/commit/3669d2e4dc2bf21b646a638c6209dea86f94e3d8.diff LOG: [Clang] Fix ICE in constraint normalization when substituting concept template parameters (#184406) 23341c3d139b889e8c46867f8d704ab3c22b51f8 introduced `SubstituteConceptsInConstraintExpression` to substitute non-dependent concept template arguments into a concept's constraint expression during normalization, as part of the P2841R7 implementation ([temp.constr.normal]/1.4). The `ConstraintExprTransformer` added in that commit overrides `TransformTemplateArgument` to only transform concept-related arguments and preserve all others. However, `TransformUnresolvedLookupExpr` called `Sema::SubstExpr`, which creates a separate `TemplateInstantiator` that performs full substitution bypassing the selective override entirely. This caused all template parameters in the constraint expression to be substituted using the concept's MLTAL. For example, given: ```cpp template <class A, template <typename...> concept C> concept maybe_cvref = C<std::remove_cvref_t<A>>; template <maybe_cvref<std::integral> T, typename U> auto f0(T&& t, U&& u); // fortunately it passes on Clang 22 due to // maybe_cvref's first template parameter (A) has // same kind (type) of f0's first one (T) template <typename T, maybe_cvref<std::integral> U> auto f1(T&& t, U&& u); // it causes ICE on Clang 22 ``` `C<std::remove_cvref_t<A>>` was fully substituted into `std::integral<std::remove_cvref_t<U>>`, where `U` refers to `f1`'s parameter at depth 0, index 1. When `SubstituteParameterMappings` later applied the concept's MLTAL, it resolved `U` (`clang::TemplateArgument::Type`) at (0,1) to `std::integral` (`clang::TemplateArgument::Template`) instead of the intended type parameter, causing an ICE due to argument kind mismatch. Fix this by resolving the concept declaration directly from the MLTAL and routing template arguments through `ConstraintExprTransformer`'s own `TransformTemplateArguments`, preserving non-concept arguments correctly. Added: Modified: clang/docs/ReleaseNotes.rst clang/lib/Sema/SemaTemplateInstantiate.cpp clang/test/SemaCXX/cxx2c-template-template-param.cpp Removed: ################################################################################ diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 715d153257191..3617786f09595 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -321,6 +321,7 @@ Bug Fixes in This Version - Fixed an assertion failure caused by error recovery while extending a nested name specifier with results from ordinary lookup. (#GH181470) - Fixed a crash when parsing ``#pragma clang attribute`` arguments for attributes that forbid arguments. (#GH182122) - Fixed a bug with multiple-include optimization (MIOpt) state not being preserved in some cases during lexing, which could suppress header-guard mismatch diagnostics and interfere with include-guard optimization. (#GH180155) +- Fixed a crash when normalizing constraints involving concept template parameters whose index coincided with non-concept template parameters in the same parameter mapping. Bug Fixes to Compiler Builtins ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/clang/lib/Sema/SemaTemplateInstantiate.cpp b/clang/lib/Sema/SemaTemplateInstantiate.cpp index a60d11d8eb36f..34ed5dffa11b4 100644 --- a/clang/lib/Sema/SemaTemplateInstantiate.cpp +++ b/clang/lib/Sema/SemaTemplateInstantiate.cpp @@ -4529,11 +4529,40 @@ ExprResult Sema::SubstConceptTemplateArguments( ExprResult TransformUnresolvedLookupExpr(UnresolvedLookupExpr *E, bool IsAddressOfOperand = false) { - if (E->isConceptReference()) { - ExprResult Res = SemaRef.SubstExpr(E, MLTAL); - return Res; - } - return E; + if (!E->isConceptReference()) + return E; + + assert(E->getNumDecls() == 1 && + "ConceptReference must have single declaration"); + NamedDecl *D = *E->decls_begin(); + ConceptDecl *ResolvedConcept = nullptr; + + if (auto *TTP = dyn_cast<TemplateTemplateParmDecl>(D)) { + unsigned Depth = TTP->getDepth(); + unsigned Pos = TTP->getPosition(); + if (Depth < MLTAL.getNumLevels() && + MLTAL.hasTemplateArgument(Depth, Pos)) { + TemplateArgument Arg = MLTAL(Depth, Pos); + assert(Arg.getKind() == TemplateArgument::Template); + ResolvedConcept = + dyn_cast<ConceptDecl>(Arg.getAsTemplate().getAsTemplateDecl()); + } + if (ResolvedConcept == nullptr) + return E; + } else + ResolvedConcept = cast<ConceptDecl>(D); + + TemplateArgumentListInfo TransArgs(E->getLAngleLoc(), E->getRAngleLoc()); + if (TransformTemplateArguments(E->getTemplateArgs(), + E->getNumTemplateArgs(), TransArgs)) + return ExprError(); + + CXXScopeSpec SS; + DeclarationNameInfo NameInfo(ResolvedConcept->getDeclName(), + E->getNameLoc()); + return SemaRef.CheckConceptTemplateId(SS, SourceLocation(), NameInfo, + ResolvedConcept, ResolvedConcept, + &TransArgs, false); } }; diff --git a/clang/test/SemaCXX/cxx2c-template-template-param.cpp b/clang/test/SemaCXX/cxx2c-template-template-param.cpp index 704df3112277f..6214e96905409 100644 --- a/clang/test/SemaCXX/cxx2c-template-template-param.cpp +++ b/clang/test/SemaCXX/cxx2c-template-template-param.cpp @@ -432,5 +432,41 @@ template <typename T, template <typename...> concept C1> concept TestBinary = T::a || C1<T>; static_assert(TestBinary<int, A>); +} + +namespace concept_template_parameter_as_second_parameter { + +template <typename> +concept Any = true; + +template <typename T, template <typename...> concept C> +concept Wrap = C<T>; + +template <typename T, Wrap<Any> U> +void f(T&&, U&&) {} + +void test() { + f(0, 1); +} + +} + +namespace multi_level_concept_template_parameter { + +template <typename> +concept Any = true; + +template <typename T, template <typename...> concept C0, template <typename...> concept C1> +concept Both = C0<T> && C1<T>; + +template <typename T, template <typename...> concept C> +concept Wrap = Both<T, Any, C>; + +template <typename T, Wrap<Any> U> +void f(T&&, U&&) {} + +void test() { + f(0, 1); +} } _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
