I'm going to rewrite this patch tomorrow morning. The semantics aren't quite right --- they should be simpler.
>> Previously, if constraints were not >> satisfied, we would not record the template as a candidate. However, >> this causes errors in class template instantiation if there are >> constrained friend declarations whose constraints are not satisfied >> ("no matching template declaration"). > > Is that wrong? We normally give errors about friend declarations that don't > match any template. Why don't we want this error when the friend > declaration is looking for a constrained template? It is wrong, but not for the reasons I gave. This only happens when you try to constrain a friend function that declares a specialization, which happens to be completely separate from the previously declared template. I'm going to disallow the ability to declare constrained friend specializations. They don't really make sense. >> + if (is_non_template_member_fn (fn) || is_non_template_friend (fn)) > > > Let's have one predicate instead of two; the condition here is a temploid > that is not a specialization of a primary template. Agreed. > >> + if (!check_template_constraints (tmpl, args) && (complain & >> tf_error)) >> { >> reason = template_constraint_failure (tmpl, args); >> viable = false; > > > Why add the complain check? A constraint failure should make a candidate > non-viable even in SFINAE context. What have I done? That's awful... >> + // If we're not instantiating a friend function, then we need to >> + // ensure the specialization of the best template satisfies its >> + // constraints. > > > Surely we need to check constraints in the earlier loop, so that we don't > treat as a candidate a template that doesn't satisfy the constraints; > otherwise if we have two templates > > template <class T> T f(T) requires Thing; > template <class T> T* f(T*); > > and our specialization requires Thing, we would select the second (because > it is otherwise more specialized) and then give an error about constraint > mismatch; I would think we want to select the first. I believe that's what the previous version did, and we'll go back to that. This change was part of the semantics that I didn't get right. >> + // If there is an overload with the same type and >> + // constraints, then this is a good declaration. >> + if (same_type_p (TREE_TYPE (fn), TREE_TYPE (f))) >> + if (equivalent_constraints (constr, get_constraints (f))) >> + return; > > > It seems that this will only allow friend declarations that match the > template exactly, not friend declarations that are more specialized than the > matching template. It looks like you're trying to implement a subset of > determine_specialization here, which I think is a mistake. I agree. It's a mistake. This is also related to the semantics that I got wrong. Effectively, the only changes needed for constrained friends are that: a) you can't constrain non-dependent friends b) you can't constraint non-template friend functions that declare a specialization and c) that we check non-template friends the same way as non-template member fns There should be no changes to any of the rules for determining specializations. Andrew