Added support for constraint satisfaction checking and partial ordering of constraints in constrained partial specialization and function template overloads. Depends on D41569.
- Adjusted constraint normalization to piecewise constraint substitution
- Normalized constraints are now represented as described in the standard (a non-substituted expression along with a list of "template arguments").
ClassTemplate is in the "to" context, i.e. it is already imported.
ExpectedExpr ConstraintsOrErr = import(D->getAssociatedConstraints()); if (!ConstraintsOrErr) return ConstraintsOrErr.takeError(); // ... ClassTemplate->findPartialSpecialization(TemplateArgs, *ConstraintsOrErr, InsertPos);
(The same below.)
This looks correct: PartSpec is the new instance (but this is not the best name for it, maybe PartialSpec2?).
(Didn't finish the review, but I have to run now.)
Hmm, is this the right set of things to be profiling? The relevant rule should be that the template-heads are equivalent and the simple-template-ids are equivalent, and checking the associated constraints isn't sufficient to check the former. Should we be profiling the template parameter list and template arguments instead?
Please don't join sentences with periods like this; it looks strange given that we generally render diagnostics in lowercase, and not as full sentences). I'm also not sure what the second sentence here is telling the user. (What do you mean by "any"? "some", or "all", or substitutions the program requires, or something else?)
We generally use "while" not "when" for these "here's what I was doing" notes.
The specification says we substitute into the parameter mapping bottom-up (starting with atomic constraints and then substituting as they get used in enclosing constraints), but you're substituting top-down. That's not equivalent: specifically, the rules say that we should not perform substitution for parameters that are not used in the expansion of a concept ([temp.constr.atomic]p1: "[...] a mapping from the template parameters that appear within E to template arguments involving the [...]"). Example:
template<typename T> concept True = true; template<typename T> concept Foo = True<T*>; template<typename T> concept Bar = Foo<T&>; template<typename T> void f() requires Bar<T>;
Here, the true atomic constraint has no parameter mappings, so we should never perform the substitution that would form T&*, and so shouldn't reject this for forming a pointer to a reference.
I think it's sufficient to first determine which parameters are used in a concept (perhaps when the concept is defined), and then form a parameter mapping top-down skipping the unused arguments, but you need to be careful to do that recursively (Bar doesn't use its T because Foo doesn't use its T).
There are a bunch of ways we can optimize this, but let's get the straightforward approach landed first :)
&& and || on prior line, please.