This is yet another one-line patch to fix crashes on constraint
substitution.
```cpp
template <class, class> struct formatter;
template <class, class> struct basic_format_context {};
template <typename CharType>
concept has_format_function = format(basic_format_context<CharType, CharType>());
template <typename ValueType, typename CharType>
requires has_format_function<CharType>
struct formatter<ValueType, CharType> {
template <typename OutputIt>
CharType format(basic_format_context<OutputIt, CharType>);
};
```
In this case, we would build up a `RecoveryExpr` for a call within a
constraint expression due to the absence of viable functions. The
heuristic algorithm attempted to find such a function inside of a
ClassTemplatePartialSpecialization, from which we started to substitute
its requires-expression, and it succeeded with a FunctionTemplate such
that
1) It has only one parameter, which is dependent.
2) The only one parameter depends on two template parameters. They are,
in canonical form, `<template-parameter-1-0>` and
`<template-parameter-0-1>` respectively.
Before we emit an error, we still want to recover the most viable
functions. This goes downhill to deducing template parameters against
its arguments, where we would collect the argument type with the same
depth as the parameter type into a Deduced set. The size of the set is
presumed to be that of function template parameters, which is 1 in this
case. However, since we haven't yet properly set the template depth
before the dance, we'll end up putting the type for
`<template-parameter-0-1>` to the second position of Deduced set, which
is unfortunately an access violation!
The bug seems to appear since clang 12.0.
This fixes [the
case](https://github.com/llvm/llvm-project/issues/58548#issuecomment-1287935336).
210 lines
9.5 KiB
C++
210 lines
9.5 KiB
C++
// RUN: %clang_cc1 -std=c++20 -verify %s
|
|
|
|
// expected-error@+1{{use of undeclared identifier 'b'}}
|
|
constexpr bool CausesRecoveryExpr = b;
|
|
|
|
template<typename T>
|
|
concept ReferencesCRE = CausesRecoveryExpr;
|
|
|
|
template<typename T> requires CausesRecoveryExpr // #NVC1REQ
|
|
void NoViableCands1(){} // #NVC1
|
|
|
|
template<typename T> requires ReferencesCRE<T> // #NVC2REQ
|
|
void NoViableCands2(){} // #NVC2
|
|
|
|
template<ReferencesCRE T> // #NVC3REQ
|
|
void NoViableCands3(){} // #NVC3
|
|
|
|
void NVCUse() {
|
|
NoViableCands1<int>();
|
|
// expected-error@-1 {{no matching function for call to 'NoViableCands1'}}
|
|
// expected-note@#NVC1{{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#NVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
|
|
NoViableCands2<int>();
|
|
// expected-error@-1 {{no matching function for call to 'NoViableCands2'}}
|
|
// expected-note@#NVC2{{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#NVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
NoViableCands3<int>();
|
|
// expected-error@-1 {{no matching function for call to 'NoViableCands3'}}
|
|
// expected-note@#NVC3{{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#NVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
}
|
|
|
|
template<typename T> requires CausesRecoveryExpr // #OVC1REQ
|
|
void OtherViableCands1(){} // #OVC1
|
|
|
|
template<typename T>
|
|
void OtherViableCands1(){} // #OVC1_ALT
|
|
|
|
template<typename T> requires ReferencesCRE<T> // #OVC2REQ
|
|
void OtherViableCands2(){} // #OVC2
|
|
|
|
template<typename T>
|
|
void OtherViableCands2(){} // #OVC2_ALT
|
|
|
|
template<ReferencesCRE T> // #OVC3REQ
|
|
void OtherViableCands3(){} // #OVC3
|
|
template<typename T>
|
|
void OtherViableCands3(){} // #OVC3_ALT
|
|
|
|
void OVCUse() {
|
|
OtherViableCands1<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands1'}}
|
|
// expected-note@#OVC1_ALT {{candidate function}}
|
|
// expected-note@#OVC1 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
OtherViableCands2<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands2'}}
|
|
// expected-note@#OVC2_ALT {{candidate function}}
|
|
// expected-note@#OVC2 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
OtherViableCands3<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands3'}}
|
|
// expected-note@#OVC3_ALT {{candidate function}}
|
|
// expected-note@#OVC3 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
}
|
|
|
|
template<typename T> requires CausesRecoveryExpr // #OBNVC1REQ
|
|
void OtherBadNoViableCands1(){} // #OBNVC1
|
|
|
|
template<typename T> requires false // #OBNVC1REQ_ALT
|
|
void OtherBadNoViableCands1(){} // #OBNVC1_ALT
|
|
|
|
template<typename T> requires ReferencesCRE<T> // #OBNVC2REQ
|
|
void OtherBadNoViableCands2(){} // #OBNVC2
|
|
|
|
template<typename T> requires false// #OBNVC2REQ_ALT
|
|
void OtherBadNoViableCands2(){} // #OBNVC2_ALT
|
|
|
|
template<ReferencesCRE T> // #OBNVC3REQ
|
|
void OtherBadNoViableCands3(){} // #OBNVC3
|
|
template<typename T> requires false // #OBNVC3REQ_ALT
|
|
void OtherBadNoViableCands3(){} // #OBNVC3_ALT
|
|
|
|
void OBNVCUse() {
|
|
OtherBadNoViableCands1<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherBadNoViableCands1'}}
|
|
// expected-note@#OBNVC1_ALT {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC1REQ_ALT {{because 'false' evaluated to false}}
|
|
// expected-note@#OBNVC1 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
OtherBadNoViableCands2<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherBadNoViableCands2'}}
|
|
// expected-note@#OBNVC2_ALT {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC2REQ_ALT {{because 'false' evaluated to false}}
|
|
// expected-note@#OBNVC2 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
OtherBadNoViableCands3<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherBadNoViableCands3'}}
|
|
// expected-note@#OBNVC3_ALT {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC3REQ_ALT {{because 'false' evaluated to false}}
|
|
// expected-note@#OBNVC3 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#OBNVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
}
|
|
|
|
|
|
// Same tests with member functions.
|
|
struct OVC {
|
|
template<typename T> requires CausesRecoveryExpr // #MEMOVC1REQ
|
|
void OtherViableCands1(){} // #MEMOVC1
|
|
|
|
template<typename T>
|
|
void OtherViableCands1(){} // #MEMOVC1_ALT
|
|
|
|
template<typename T> requires ReferencesCRE<T> // #MEMOVC2REQ
|
|
void OtherViableCands2(){} // #MEMOVC2
|
|
|
|
template<typename T>
|
|
void OtherViableCands2(){} // #MEMOVC2_ALT
|
|
|
|
template<ReferencesCRE T> // #MEMOVC3REQ
|
|
void OtherViableCands3(){} // #MEMOVC3
|
|
template<typename T>
|
|
void OtherViableCands3(){} // #MEMOVC3_ALT
|
|
};
|
|
|
|
void MemOVCUse() {
|
|
OVC S;
|
|
S.OtherViableCands1<int>();
|
|
// expected-error@-1 {{no matching member function for call to 'OtherViableCands1'}}
|
|
// expected-note@#MEMOVC1_ALT {{candidate function}}
|
|
// expected-note@#MEMOVC1 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#MEMOVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
S.OtherViableCands2<int>();
|
|
// expected-error@-1 {{no matching member function for call to 'OtherViableCands2'}}
|
|
// expected-note@#MEMOVC2_ALT {{candidate function}}
|
|
// expected-note@#MEMOVC2 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#MEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
S.OtherViableCands3<int>();
|
|
// expected-error@-1 {{no matching member function for call to 'OtherViableCands3'}}
|
|
// expected-note@#MEMOVC3_ALT {{candidate function}}
|
|
// expected-note@#MEMOVC3 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#MEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
}
|
|
|
|
struct StaticOVC {
|
|
template<typename T> requires CausesRecoveryExpr // #SMEMOVC1REQ
|
|
static void OtherViableCands1(){} // #SMEMOVC1
|
|
|
|
template<typename T>
|
|
static void OtherViableCands1(){} // #SMEMOVC1_ALT
|
|
|
|
template<typename T> requires ReferencesCRE<T> // #SMEMOVC2REQ
|
|
static void OtherViableCands2(){} // #SMEMOVC2
|
|
|
|
template<typename T>
|
|
static void OtherViableCands2(){} // #SMEMOVC2_ALT
|
|
|
|
template<ReferencesCRE T> // #SMEMOVC3REQ
|
|
static void OtherViableCands3(){} // #SMEMOVC3
|
|
template<typename T>
|
|
static void OtherViableCands3(){} // #SMEMOVC3_ALT
|
|
};
|
|
|
|
void StaticMemOVCUse() {
|
|
StaticOVC::OtherViableCands1<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands1'}}
|
|
// expected-note@#SMEMOVC1_ALT {{candidate function}}
|
|
// expected-note@#SMEMOVC1 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#SMEMOVC1REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
StaticOVC::OtherViableCands2<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands2'}}
|
|
// expected-note@#SMEMOVC2_ALT {{candidate function}}
|
|
// expected-note@#SMEMOVC2 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#SMEMOVC2REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
StaticOVC::OtherViableCands3<int>();
|
|
// expected-error@-1 {{no matching function for call to 'OtherViableCands3'}}
|
|
// expected-note@#SMEMOVC3_ALT {{candidate function}}
|
|
// expected-note@#SMEMOVC3 {{candidate template ignored: constraints not satisfied}}
|
|
// expected-note@#SMEMOVC3REQ{{because substituted constraint expression is ill-formed: constraint depends on a previously diagnosed expression}}
|
|
}
|
|
|
|
namespace GH58548 {
|
|
|
|
template <class, class> struct formatter; // #primary-template
|
|
template <class, class> struct basic_format_context {};
|
|
|
|
template <typename CharType>
|
|
concept has_format_function =
|
|
format(basic_format_context<CharType, CharType>());
|
|
|
|
template <typename ValueType, typename CharType>
|
|
requires has_format_function<CharType>
|
|
struct formatter<ValueType, CharType> {
|
|
template <typename OutputIt>
|
|
CharType format(basic_format_context<OutputIt, CharType>);
|
|
};
|
|
|
|
template <class Ctx> int handle_replacement_field(Ctx arg) {
|
|
formatter<decltype(arg), int> ctx; // expected-error {{implicit instantiation of undefined template}}
|
|
return 0;
|
|
}
|
|
|
|
int x = handle_replacement_field(0);
|
|
// expected-note@-1 {{template specialization 'GH58548::handle_replacement_field<int>' requested here}}
|
|
// expected-note@#primary-template {{is declared here}}
|
|
|
|
} // GH58548
|