Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CWG2596 [temp.inst]/17 Instantiation of constrained non-template friends #47

Closed
dfrib opened this issue Jun 2, 2022 · 6 comments
Closed

Comments

@dfrib
Copy link

dfrib commented Jun 2, 2022

Full name of submitter: David Friberg

Reference (section label): [temp.inst]

Link to reflector thread (if any): -

Issue description:

As per US115 of P2103R0 hidden non-template friends may also use requires-clauses:

struct Base {};

template<int N>
struct S : public Base {
    friend int foo(Base&) requires (N == 1) { return 1; }
    friend int foo(Base&) requires (N == 2) { return 3; }
};

int main() {
    S<1> s1{};
    S<2> s2{};  // #1
    auto const foo1 = foo(s1);
    auto const foo2 = foo(s2); // #2 
}

The current wording doesn't seem to cover what should happen at #1 above: requires-clauses take effect during overload resolution, but what about instantiation of constrained (hidden) friends whose requires-clauses are not satisfied for the particular enclosing class template specialization being instantiated? Particularly [temp.inst]/17 doesn't cover friends as they are neither template specializations nor member functions:

The type-constraints and requires-clause of a template specialization or member function are not instantiated along with the specialization or function itself, even for a member function of a local class; substitution into the atomic constraints formed from them is instead performed as specified in [temp.constr.decl] and [temp.constr.atomic] when determining whether the constraints are satisfied or as specified in [temp.constr.decl] when comparing declarations.

Implementation variance:

  • GCC and MSVC both currently reject the program at #1 for ODR-violations
  • Clang fails at #2, and generates the same mangled name for two different definitions

For the latter, the Itanium C++ ABI currently does not allow unambiguously mangling these kind of friends. See itanium-cxx-abi/cxx-abi#24 (comment)

Suggested resolution:
To be discussed. Should be program above be well-formed?

@erichkeane
Copy link

I believe #1 is covered by https://eel.is/c++draft/temp.friend#9 . Those are in different 'scopes' since they have a constraint at all (as they are non-function templates). Had they been function templates, the constraint needs to refer to the enclosing template to get the 'different scope' protection of [temp.friend]p9.

FWIW, I'm implementing this in Clang right now (it doesn't do it correctly) as part of the deferred concept implementation, and have a patch to implement MOST of that Itanium ABI proposal, but its held up on the concept patch, as well as Itanium not having done anything with that proposal yet.

@xmh0511
Copy link

xmh0511 commented Jun 6, 2022

IMO, "in any other scope" in [temp.friend] p9 is not sufficiently clear.

Such a constrained friend function or function template declaration does not declare the same function or function template as a declaration in any other scope.

Does it mean

a declaration inhabits any other scope

or

a declaration whose target scope is any other scope

or both?

Furthermore, "any other scope" means different scopes or includes the same scope? Besides that, the example is regulated by [temp.friend] p9, I think. Moreover, "does not correspond to" is better than "does not declare the same" since [temp.friend] is introduced in [basic.scope.scope] p4 that is talking about whether two declarations correspond.

[temp.inst]/17 ought to cover templated function cases in order to make it be clear that the satisfaction of the associated constraints is only checked in overload resolution or other contexts we need.

Suggested resolution:

change [temp.inst] p17 to

The type-constraints and requires-clause of a template specialization or member templated function are not instantiated along with the specialization or function itself, even for a member function of a local class;

Not sure the complete intent of [temp.friend] p9, if we agree that it is applied to the first example, the same should be true for this example:

struct Base {};

template<int N>
struct S : public Base {
    friend int foo(Base&) requires (N == 1) { return 1; }
    friend int foo(Base&) requires (N == 1) { return 1; }
};

However, all three compilers agree the latter is a redeclaration of the former.

@jensmaurer
Copy link
Member

@erichkeane , "scope" per C++ is lexical (and yes, "other" means "not the same").
The temp.friend p9 clause just means that you can't (re)declare just a constrained friend outside of the class; any attempt at such redeclaration would declare a different entity.

@jensmaurer
Copy link
Member

CWG2596

@jensmaurer jensmaurer changed the title [temp.inst]/17 Instantiation of constrained non-template friends CWG2596 [temp.inst]/17 Instantiation of constrained non-template friends Jun 15, 2022
@xmh0511
Copy link

xmh0511 commented Jun 15, 2022

CWG2596

Shall we change "does not declare the same" in [temp.friend] p9 to "does not correspond to"? Since [basic.scope.scope] p4 cross-references [temp.friend], which is talking about whether two declarations correspond, we are looking forward to that [temp.friend] gives the case an answer that whether they correspond or not. Moreover, two declarations that do not correspond can never declare the same entity, so "does not correspond to" is better than "does not declare the same", I think.

A non-template friend declaration with a requires-clause shall be a definition. A friend function template with a constraint that depends on a template parameter from an enclosing template shall be a definition. Such a constrained friend function or function template declaration does not declare the same function or function template as correspond to any declaration in that inhabits any other scope.

@jensmaurer
Copy link
Member

This particular issue is about the instantiation question primarily.

In order to fix [temp.friend] properly, the rule should simply be moved to the definition of "corresponds" in [basic.scope.scope] p4, but that's not in scope for CWG2596 (which is talking about instantiations, not about declaration correspondence).

If the simple drive-by "inhabits" is not considered an improvement, it should simply be omitted.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants