[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.
This commit is contained in:
parent
1930089f25
commit
3669d2e4dc
@ -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
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@ -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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user