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

The definition of "reference-compatible" is not accurate #5239

Open
xmh0511 opened this issue Jan 26, 2022 · 2 comments
Open

The definition of "reference-compatible" is not accurate #5239

xmh0511 opened this issue Jan 26, 2022 · 2 comments

Comments

@xmh0511
Copy link
Contributor

xmh0511 commented Jan 26, 2022

“cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence ([conv]).

Does "can be converted to ... via a standard conversion sequence" comprise the case: the standard conversion sequence does exist, however, due to some other requirement, the standard conversion so necessitates is ill-formed? Specifically, if we assume the definition of reference-compatible means that: if the standard conversion sequence is ill-formed, then “pointer to cv2 T2” cannot be converted to the type “pointer to cv1 T1”; consider this example

struct A{
   A() = default;
   A(A const&){}  // #2
};
struct B: private A{};
B b;
const A& rf = b; // #1

A is reference-related to B, thus each bullet that requires "T1 is not reference-related to T2" is skiped. With the above assumption, A is not reference-compatible with B, thus each bullet that requires “cv1 T1” is reference-compatible with “cv2 T2” is skiped. Eventually, the case falls into [dcl.init.ref] 5.4.2

Otherwise, the initializer expression is implicitly converted to a prvalue of type “T1”. The temporary materialization conversion is applied, considering the type of the prvalue to be “cv1 T1”, and the reference is bound to the result.

The implicit conversion is the call A::A(A const&) with the argument b, where the parameter binding to the argument is identical to the reference binding at #1. This will cause the recursive inspection of the relevant bullets. Obviously, it's not the intent, the reference binding at #1 should be terminated at [dcl.init.ref] p4. That is, A is reference-compatible with B even if the standard conversion sequence is ill-formed due to inaccessibility. Maybe, we can improve the definition to be that

“cv1 T1” is reference-compatible with “cv2 T2” if ignore the base class is inaccessible(if any), there exists a standard conversion sequence that can convert a prvalue of type “pointer to cv2 T2” to the type “pointer to cv1 T1”([conv]). In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.

The former is used to admit that A and B are reference-compatible while the latter is used to negative the well-formed associated with the binding since the base class A is inaccessible.

@zygoloid
Copy link
Member

The rules in [conv] explicitly define what "can be converted" means.

[conv.qual]/3:

A prvalue of type T1 can be converted to type T2 if the qualification-combined type of T1 and T2 is T2.

[conv.ptr]/3:

A prvalue of type “pointer to cv D”, where D is a complete class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (11.7) of D.

[conv.fctptr]/1:

A prvalue of type “pointer to noexcept function” can be converted to a prvalue of type “pointer to function”.

The rules also say that in some cases the conversion is ill-formed, and what result you get, but the definition of "reference-compatible" doesn't talk about that. Given that both the definition and the use of the term use the same words "can be converted", I think there's no ambiguity in the intent here.

However, we are requiring a reader to assume that "can be converted" for a standard conversion sequence is defined in terms of "can be converted" for its constituent conversions, which I don't think we actually say. And in particular the similar phrase "can be implicitly converted" is defined in [conv]/3 in a different way, that excludes the cases where a constituent conversion is ill-formed.

Perhaps we could add a note that pointers can be converted even if the conversion would be ill-formed due to access or ambiguity? A cross-reference to the three different kinds of conversion that can be applied might be nice too:

[Note: Such a standard conversion sequence may include a qualification conversion ([conv.qual]), a pointer conversion ([conv.ptr]), or a function pointer conversion ([conv.fctptr]). It is possible for types to be reference-compatible even though the standard conversion sequence would be ill-formed due to access or an ambiguous base class.]

@xmh0511
Copy link
Contributor Author

xmh0511 commented Jan 27, 2022

@zygoloid I would expect that we can have formal rules that can eliminate these ambiguities. Since the ambiguities are not only in "reference-compatible" but also they exist in [over].

[over.best.ics.general] p1

An implicit conversion sequence is a sequence of conversions used to convert an argument in a function call to the type of the corresponding parameter of the function being called. The sequence of conversions is an implicit conversion as defined in [conv]...

Thus, whether the implicit conversion sequence can be formed is determined by [conv]/3, that is T t=E;, the well-formed of which is impacted by such as "accessibility", "ambiguous", "deleted function", and so on. Although we intend to clarify that by having a note

[Note 1: Other properties, such as the lifetime, storage class, alignment, accessibility of the argument, whether the argument is a bit-field, and whether a function is deleted, are ignored. So, although an implicit conversion sequence can be defined for a given argument-parameter pair, the conversion from the argument to the parameter might still be ill-formed in the final analysis. — end note]

However, I think it is not sufficient because the contradictories are produced by these two formal rules. Specifically, the contradictory was discussed in https://stackoverflow.com/questions/69326627/the-requirements-of-forming-an-implicit-conversion-sequence-are-contradictory

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

2 participants