C++ Logo

sg12

Advanced search

Re: [ub] Proposal: make self-initialized references ill-formed (C++17?)

From: Richard Smith <richardsmith_at_[hidden]>
Date: Fri, 26 Sep 2014 12:02:02 -0700
On 25 September 2014 20:45, David Krauss <david_work_at_[hidden]> wrote:

>
> On 2014–09–25, at 4:52 AM, Richard Smith <richardsmith_at_[hidden]> wrote:
>
> Then I'm strongly opposed. It does not seem acceptable to silently change
> existing valid and well-defined code into having undefined behavior. I'm
> sure I'm not the only one who'll feel this way.
>
>
> True, nobody gains anything from the possibility of non-diagnosis and a
> crash-on-run executable. But that’s not in practice going to happen except
> as a result, as you put it, of the compiler doing nothing special in
> particular. If the implementation notices the out-of-thin-air result at
> all, that suggests the existence of some exception handling which provides
> an opportunity for diagnosis.
>
> Perhaps there should be some specification like static UB diagnosis, where
> a particular expression is ill-formed/NDR but behavior is well-defined if
> it is neither diagnosed nor evaluated.
>

Yes, that would nicely address one half of my concer. Essentially we'd
introduce a new class of program that is well-formed, but which an
implementation is not required to translate (and can instead reject with a
diagnostic).

The other half of my concern is portability: it is hugely painful to some
audiences if a program is accepted by one compiler but rejected (or
interpreted differently) by another, and indeed, the very purpose of having
a standard is to minimize the occurrence of this problem. Making the
diagnosis optional exacerbates this.

With the tweak discussed above, I don't see that we gain much over the
status quo: either way, implementations can choose to detect this case, and
either way, they can choose to diagnose or not. The *only* difference is
that their "conforming" mode would be permitted to refuse to translate the
program (which I claim is actually harmful for some audiences).

The current wording of [dcl.init.ref] 8.5.3/1 actually comes pretty close:
>
> A variable declared to be a T& or T&&, that is, “reference to type T”
> (8.3.2), shall be initialized by an object, or function, of type T or by an
> object that can be converted into a T.
>
>
> This is a “shall be” requirement applied to a runtime occurrence.
> Although, it perhaps intends only to constrain the type of an initializer
> expression.
>

Perhaps; this is definitely imprecisely worded. Either there's a mixture of
a compile-time and a runtime constraint here, or this really means "lvalue
of object or function type T". I suspect the latter.

Does portability suffer from NDR? Sure. But the main suggestion here is to
> weed out nonsense with a new diagnosis. If some customer can’t live with a
> hard error, issuing a warning and producing an executable also satisfies
> NDR.
>
> Non-diagnosis and UB right at static initialization is the status quo for
> Clang and GCC, given this declaration sequence which portably specifies a
> defective product:
>
> extern int & a;
> int & b = a;
> int & a = b;
>
> The only other way I know to get circular reference initialization is in a
> constructor, between two member references, which Clang does diagnose by
> default as use of an uninitialized variable. GCC’s -Wuninitialized is also
> an area of active development.
>
> On the other hand, there are ways to use a reference in its own
> initializer which are not unreasonable:
>
> std::function< void( int ) > && f
> = [&] ( int cnt ) { if ( cnt ) f( -- cnt ); };
>
> Stronger analysis might be better when it comes to checking initialization.
>

Yes, whatever we do specify, this example should not be made ill-formed.

Received on 2014-09-26 21:02:05