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

Reply via email to