On Thu, 12 Jan 2023 at 16:52, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
---------- Forwarded message ---------
From: Jason McKesson <jmckesson@gmail.com>
Date: Thu, Jan 12, 2023 at 11:51 AM
Subject: Re: [std-proposals] Catch exception thrown from constructor
of global object
To: Edward Catmur <ecatmur@googlemail.com>


On Thu, Jan 12, 2023 at 11:40 AM Edward Catmur <ecatmur@googlemail.com> wrote:
>
> On Thu, 12 Jan 2023 at 16:17, Jason McKesson via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
>>
>> On Thu, Jan 12, 2023 at 10:34 AM Federico Kircheis via Std-Proposals
>> <std-proposals@lists.isocpp.org> wrote:
>> > On 2023-01-12 at 11:23, Frederick Virchanza Gotham via Std-Proposals
>> > wrote:
>> >
>> > >
>> > >Now if you were to ask me . . . . I would almost go so far as to
>> > >propose that C++26 should deprecate defining a global object that
>> > >doesn't satisfy 'is_trivial' so that all such objects would be wrapped
>> > >in an std::optional<T> and emplaced within the body of 'main', but of
>> > >course I know that I won't get any support on that.
>> >
>> >
>> > It would also not cover most use-cases, for example
>> >
>> >
>> > ----
>> > /* const */ int i = function_that_throws();
>> >
>> > int main(){
>> > }
>> > ----
>> >
>> >
>> > Also std::optional as global variable is problematic if inside a
>> > library, depending on the visibility of the symbols, the destructor
>> > might be executed more than once (and thus be UB)
>>
>> It seems to me that, to the extent that this is a significant problem,
>> static analysis is likely the most effective solution. Static analysis
>> tools should be able to detect if the initializer for a global is
>> `noexcept` and there can be options to have such tools give a warning
>> if it isn't. Better tools might be able to follow inlined functions to
>> see if anything could actually throw given the value of parameters.
>
>
> As Lénárd says above, this already exists: `constinit`.

`constinit` means that the initializer must be a constant expression.
But it's not hard to write an initializer that isn't a constant
expression yet does not throw exceptions. Constant expressions cannot
throw exceptions (yet), but there are many expressions that also don't
throw exceptions.

Sure, but maybe those should be constant expressions. Other than allocation, what is there to do in a constructor that can't already be evaluated in constant expressions?

> struct X {
>     constexpr X(int i) {
>         if (i == 99)
>             throw i;
>     }
> };
> constinit X x1 = 1; // OK
> constinit X x99 = 99; // ill-formed, X::X(99) throws
>
> It's perhaps not quite as useful yet as it might be, but once we get non-transient constexpr allocation (TM) there will be little that you can't do in a constant-initializer.

Has there been any movement on non-transient allocation? Because I got
the impression that this was deemed too difficult.

P2670R0 was in the last-but-one mailing. I don't think it's been reviewed yet; cf. https://github.com/cplusplus/papers/issues/1336

Also, that doesn't change the fact that you can't do things like open
files and the like, things which don't throw exceptions but also
cannot be constant expressions.

Those can fail, so they probably should throw exceptions when done from a constructor. Otherwise, you're just caching failure to report later, so you may as well just cache the constructor arguments and lazy-open the file on first use.