Topic: Compile-time check for noexcept: a thought of


Author: David Krauss <potswa@gmail.com>
Date: Sat, 04 Jan 2014 13:24:22 +0800
Raw View
On 1/4/14 12:35 PM, Kazutoshi Satoda wrote:
>    "static_assert(noexcept(expr))" is a way and may help. But it is hard
>    to maintain in sync with the real code including variable
>    initialization and destruction. Compilers can know precisely which
>    expressions (function calls) should be tested.

For expression-statements, you can use a macro:

#define do_noexcept( EXPR ) \
     do { \
         static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
         EXPR; \
     while(0)

     void some_class::strong_op()
     {
       some_class transitive(*this);
       transitive.op1_may_throw();
       transitive.op2_may_throw();
       do_noexcept( *this = std::move(transitive) );
     }


If we wanted a similar construct to work on an entire function body, I
would suggest syntax like a noexcept(auto) exception-specification to
analyze all the potentially-evaluated expressions. Implicitly declared
destructors already do this by default, although the analysis is
inherently limited. The construct could be applied to a sequence of
statements using a lambda expression.

       auto move_finish = [&]() noexcept(auto) {
         *this = std::move(transitive);
       };
       static_assert( noexcept( move_finish() ), "Transitive move may throw." );
       move_finish();
     }


However, this cannot assert that initialization of an object may not
throw but its use may throw. I'm not sure if that's a useful case though.

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

.


Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Tue, 07 Jan 2014 11:33:27 +0900
Raw View
On 2014/01/04 14:24 +0900, David Krauss wrote:
> On 1/4/14 12:35 PM, Kazutoshi Satoda wrote:
>>    "static_assert(noexcept(expr))" is a way and may help. But it is hard
>>    to maintain in sync with the real code including variable
>>    initialization and destruction. Compilers can know precisely which
>>    expressions (function calls) should be tested.
>=20
> For expression-statements, you can use a macro:
>=20
> #define do_noexcept( EXPR ) \
>      do { \
>          static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
>          EXPR; \
>      while(0)
....

That's an option for the current standard. But, as you know, it can't be
applied straight to variable initialization and destruction.

> If we wanted a similar construct to work on an entire function body, I=20
> would suggest syntax like a noexcept(auto) exception-specification to=20
> analyze all the potentially-evaluated expressions. Implicitly declared=20
> destructors already do this by default, although the analysis is=20
> inherently limited. The construct could be applied to a sequence of=20
> statements using a lambda expression.
>=20
>        auto move_finish =3D [&]() noexcept(auto) {
>          *this =3D std::move(transitive);
>        };
>        static_assert( noexcept( move_finish() ), "Transitive move may thr=
ow." );
>        move_finish();
>      }
>
> However, this cannot assert that initialization of an object may not=20
> throw but its use may throw. I'm not sure if that's a useful case though.

It has the same problem that "noexcept block" in original noexcept
proposal had, with more syntactic overhead.
http://akrzemi1.wordpress.com/2011/06/10/using-noexcept/
(a quote from the list of reasons to drop static check of noexcept from
 the original proposal which had "noexcept block" to disable the check
 for a region of code in a function)
> 2. The noexcept block for locally disabling the static check was not
>    good enough for all situations, for instance it was not suitable
>    for disabling the check in constructors=92 initialization lists.
> 3. Requiring an additional block in function code was too
>    inconvenient. Additional scope could require rethinking the scopes
>    of local variables and unnecessarily make the functions longer. ...

Such a requirement of additional block (another scope) is the cause of
limitation about variable initialization and destruction, and is what
the label approach is trying to bypass.

--=20
k_satoda

--=20

---=20
You received this message because you are subscribed to the Google Groups "=
ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an e=
mail to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposa=
ls/.

.


Author: Matthew Woehlke <mw_triad@users.sourceforge.net>
Date: Tue, 07 Jan 2014 10:20:32 -0500
Raw View
On 2014-01-06 21:33, Kazutoshi Satoda wrote:
> On 2014/01/04 14:24 +0900, David Krauss wrote:
>> For expression-statements, you can use a macro:
>>
>> #define do_noexcept( EXPR ) \
>>       do { \
>>           static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
>>           EXPR; \
>>       while(0)
> ...
>
> That's an option for the current standard. But, as you know, it can't be
> applied straight to variable initialization and destruction.

What about [[noexcept]]?

// initializer list may not throw
foo::foo() : [[noexcept]] ...
{
   ...
   [[noexcept]] {
     // No statement in this scope may throw
     ...
   }
   ...
   a = [[noexcept]] b; // assignment may not throw
}


Not sure if that's what was meant about assignment, or if that would be
the best syntax. A possible advantage though is that other variations of
the above theme can be added as deemed necessary.


>> 3. Requiring an additional block in function code was too
>>     inconvenient. Additional scope could require rethinking the scopes
>>     of local variables and unnecessarily make the functions longer. ...
>
> Such a requirement of additional block (another scope) is the cause of
> limitation about variable initialization and destruction, and is what
> the label approach is trying to bypass.


I don't see that the label approach is any different. Since you
specified that the effect ends at the end of scope, how is this:

{
   ...
   noexcept:
   ...
}

....any different from this?

{
   ...
   [[noexcept]] {
     ...
   }
}


If one really needs to 'undo' noexcept, i.e. use declarations from
within the noexcept scope in the next scope, what about allowing
'inline' to be used on a scope to extend the lifetime of objects in the
scope to the parent scope (a bit like e.g. Python)?

IOW:

int bar() {
   inline [[noexcept]] {
     double a = 42.0;
   }
   // 'a' is still in scope here
}

Obviously, this would not allow extending from e.g. a function scope to
global scope (and 'inline' means something different there anyway).

--
Matthew

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

.


Author: Kazutoshi Satoda <k_satoda@f2.dion.ne.jp>
Date: Mon, 13 Jan 2014 13:15:23 +0900
Raw View
On 2014/01/08 0:20 +0900, Matthew Woehlke wrote:
> On 2014-01-06 21:33, Kazutoshi Satoda wrote:
>> On 2014/01/04 14:24 +0900, David Krauss wrote:
>>> For expression-statements, you can use a macro:
>>>
>>> #define do_noexcept( EXPR ) \
>>>       do { \
>>>           static_assert( noexcept( EXPR ), "do_noexcept violation." ); \
>>>           EXPR; \
>>>       while(0)
>> ...
>>
>> That's an option for the current standard. But, as you know, it can't be
>> applied straight to variable initialization and destruction.
>
> What about [[noexcept]]?

I think attribute can also be used in place of label to achieve similar
effects with less changes in syntax.

    void some_class::strong_op()
    {
      some_class transitive(*this);
      transitive.op1_may_throw();
      transitive.op2_may_throw();
      [[noexcept]]; // attribute-declaration in 7 [dcl.dcl]
      *this = std::move(transitive);
    }

I choose label as it looks more intuitive to represent "the critical
line", which divide a function (or a block) into separated parts.

> // initializer list may not throw
> foo::foo() : [[noexcept]] ...
> {
>    ...
>    [[noexcept]] {
>      // No statement in this scope may throw
>      ...
>    }
>    ...
>    a = [[noexcept]] b; // assignment may not throw
> }
>
> Not sure if that's what was meant about assignment, or if that would be
> the best syntax. A possible advantage though is that other variations of
> the above theme can be added as deemed necessary.

For mem-initializer-list, it may look appertaining to a specific base or
member. That's why I put comma between "static noexcept" and "Base{}" in
the first post, knowing it looks somewhat odd.

For statements, the use of additional block looks unnecessary and I
don't want that, as discussed below.

For assignment, "[[noexcept]] a = b;" looks more intuitive for me and
also matches the current syntax.

>> Such a requirement of additional block (another scope) is the cause of
>> limitation about variable initialization and destruction, and is what
>> the label approach is trying to bypass.
>
> I don't see that the label approach is any different. Since you
> specified that the effect ends at the end of scope, how is this:
>
> {
>    ...
>    noexcept:
>    ...
> }
>
> ...any different from this?
>
> {
>    ...
>    [[noexcept]] {
>      ...
>    }
> }

If there are some local variables above the noexcept line, destructors
of such variables should be checked to be noexcept.

With the requirement of block, you can't mark initialization of a local
variable which is used later in different noexcept requirement.

  void foo() noexcept
  {
  static noexcept:
    ...
  inline noexcept: // temporarily disable static checking
    X x{...}; // known to not throw, but not declared with noexcept.
  static noexcept: // enable static checking again
    ... // can use x here
  }

And as I basically add an indent for each additional block, I don't
want to make and see accompanying change of indent to just move the
noexcept line up or down.

> If one really needs to 'undo' noexcept, i.e. use declarations from
> within the noexcept scope in the next scope, what about allowing
> 'inline' to be used on a scope to extend the lifetime of objects in the
> scope to the parent scope (a bit like e.g. Python)?
>
> IOW:
>
> int bar() {
>    inline [[noexcept]] {
>      double a = 42.0;
>    }
>    // 'a' is still in scope here
> }

It will incur misreading about object lifetime, and probably doesn't
match well with the current syntax and rule about blocks.

--
k_satoda

--

---
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposals+unsubscribe@isocpp.org.
To post to this group, send email to std-proposals@isocpp.org.
Visit this group at http://groups.google.com/a/isocpp.org/group/std-proposals/.

.