Files
clang-p2996/clang/test/SemaTemplate/concepts-recovery-expr.cpp
Younan Zhang 141de74959 [clang][Sema] Populate function template depth at AddTemplateOverloadCandidate (#80395)
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).
2024-02-03 16:14:48 +08:00

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