Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Some kind of unwanted optimization going on here ??? And if so, how do I prevent it ???

145 views
Skip to first unread message

Bob Langelaan

unread,
Mar 25, 2020, 5:01:36 PM3/25/20
to
Below is some code provided by a text I am using.

I am using MS VS 2019 with default settings running in Release mode to build and run this code.

The code below as is works as expected. But if I move the statement
ending with the comment "SEE THIS STATEMENT" to before the previous statement,
it calculates time as 0.000000000 minutes. Equally strange, it outputs the
string "fibonacci( 47 ) = " and then there is a long pause while it appears to
be calculating the value. It should have returned from the function and therefore calculated the value before outputting that string ?!?!

Thanks in advance :)

// Fig. 24.10: fibonacci.cpp
// Fibonacci calculations performed sequentially
#include <iostream>
#include <iomanip>
#include <ctime>
using namespace std;

unsigned long long int fibonacci( unsigned int n ); // function prototype

// function main begins program execution
int main( void )
{
cout << fixed << setprecision( 6 );
cout << "Sequential calls to fibonacci(46) and fibonacci(47)" << endl;

// calculate fibonacci values for numbers 47 (line 17) and 46 (line 27)
cout << "Calculating fibonacci( 47 )" << endl;
time_t startTime1 = time( nullptr );
unsigned long long int result1 = fibonacci( 47 );


cout << "fibonacci( 47 ) = " << result1 << endl;
time_t endTime1 = time(nullptr); // SEE THIS STATEMENT
cout << "Calculation time = "
<< (endTime1 - startTime1 ) / 60.0
<< " minutes\n" << endl;
cout << endTime1 << " " << startTime1 << endl;
cout << "Calculating fibonacci( 46 )" << endl;
time_t startTime2 = time( nullptr );
unsigned long long int result2 = fibonacci( 46 );


cout << "fibonacci( 46 ) = " << result2 << endl;
time_t endTime2 = time(nullptr);
cout << "Calculation time = "
<< (endTime2 - startTime2) / 60.0
<< " minutes\n" << endl;

cout << "Total calculation time = "
<< (endTime2 - startTime1) / 60.0 << " minutes" << endl;
} // end main

// Recursively calculates fibonacci numbers
unsigned long long int fibonacci( unsigned int n )
{
// base case
if ( 0 == n || 1 == n )
{
return n;
} // end if
else // recursive step
{
return fibonacci( n - 1 ) + fibonacci( n - 2 );
} // end else
} // end function fibonacci


/**************************************************************************
* (C) Copyright 1992-2014 by Deitel & Associates, Inc. and *
* Pearson Education, Inc. All Rights Reserved. *
* *
* DISCLAIMER: The authors and publisher of this book have used their *
* best efforts in preparing the book. These efforts include the *
* development, research, and testing of the theories and programs *
* to determine their effectiveness. The authors and publisher make *
* no warranty of any kind, expressed or implied, with regard to these *
* programs or to the documentation contained in these books. The authors *
* and publisher shall not be liable in any event for incidental or *
* consequential damages in connection with, or arising out of, the *
* furnishing, performance, or use of these programs. *
**************************************************************************/

Öö Tiib

unread,
Mar 25, 2020, 5:51:58 PM3/25/20
to
On Wednesday, 25 March 2020 23:01:36 UTC+2, Bob Langelaan wrote:
> Below is some code provided by a text I am using.
>
> I am using MS VS 2019 with default settings running in Release mode to build and run this code.
>
> The code below as is works as expected. But if I move the statement
> ending with the comment "SEE THIS STATEMENT" to before the previous statement,
> it calculates time as 0.000000000 minutes.

The std::time() returns std::time_t that is usually integral number of
seconds since 00:00, Jan 1 1970 UTC, but that is not mandated by C++
standard. It is defined so about extremely similar POSIX time() function
by POSIX standard AFAIK.
Argument of std::time() can be pointer to a std::time_t object to
store the time as well, or a null pointer.

So apparently if you get 0 from it the clock of your system is stuck
at 00:00, Jan 1 1970 UTC or MS uses some rather unusual time_t there.


> Equally strange, it outputs the
> string "fibonacci( 47 ) = " and then there is a long pause while it appears to
> be calculating the value. It should have returned from the function and
> therefore calculated the value before outputting that string ?!?!

The algorithm seems hyper naive as it does recurse
2 in 47 times. That can take time indeed.

Öö Tiib

unread,
Mar 25, 2020, 6:40:46 PM3/25/20
to
Calculating the value after output seems strange. Perhaps compiler
somehow deduced that fibonacci() does not have side effects and delayed
its call until result1 was used (as optimization)?
Can't reproduce on online gcc 9.2.0:
<http://coliru.stacked-crooked.com/a/e34866820ac2d2a4>

Mike Terry

unread,
Mar 25, 2020, 6:58:00 PM3/25/20
to
Yes, in the Release build,the compiler has "optimised" the order of
function calls, and moved the line
time_t endTime1 = time(nullptr);
to before the call to fibonacci().

I have the same VStudio version, and can see this is what it is doing.

I don't know whether (or under what conditions) this is allowed by the
standards though.

Mike.

Öö Tiib

unread,
Mar 25, 2020, 7:13:39 PM3/25/20
to
Things like input-output and access to volatile are side-effects.
Things with side effects may not be reordered.
I can't imagine how Windows can figure time without access of
something volatile or doing input from somewhere so perhaps MS
optimizer has screwed up tracking of what of its API has side
effects.


Mike Terry

unread,
Mar 25, 2020, 7:20:49 PM3/25/20
to
I found this from about 5 years back:


<https://stackoverflow.com/questions/26190364/is-it-legal-for-a-c-optimizer-to-reorder-calls-to-clock>

There's a suggestion that it is legal if the compiler is aware of all
the code executed between the clock calls, and there are no side effects
preventing it. It suggests making the time and result variable volatile
should prevent reordering:

volatile time_t startTime1 = time( nullptr );
volatile unsigned long long int result1 = fibonacci( 47 );
volatile time_t endTime1 = time(nullptr);

which seems to work on my system.

Regards,
Mike.

Öö Tiib

unread,
Mar 25, 2020, 7:28:10 PM3/25/20
to
Yes but OP's problem sounded like reordering I/O and time() call:

unsigned long long int result1 = fibonacci( 47 );
cout << "fibonacci( 47 ) = " << result1 << endl; // <- I/O (1)
time_t endTime1 = time(nullptr); // <- time() call (2)

Mike Terry

unread,
Mar 25, 2020, 7:34:05 PM3/25/20
to
I'm just a regular C++ user, and don't understand the relevent standards
as well as others here!

It would seem to me that calling time() probably IS deemed as having
side effects by the VC compiler, but this in itself would only prevent
the re-ordering of the calls to time(). I guess the compiler sees that
fibonacci() has no side effects, and so it's free to re-order it with
respect to the time() calls. (?) As a test for OP, deliberately
introducing a side effect into fibonacci() might solve the problem.


Mike.

Öö Tiib

unread,
Mar 25, 2020, 7:49:36 PM3/25/20
to
Yes but in OP's code result of fibonzzi() was used in output before
calling time() and that must be as good as assigning to volatile.

Mike Terry

unread,
Mar 25, 2020, 8:00:37 PM3/25/20
to
Hmm, this would be consistent with the compiler recognising the time()
calls and cout<< calls as both having side effects (or involving I/O),
but not the fibonacci() call, thus allowing it (supposedly) to move the
fibonacci() call to after the time() call.

Here is the OP's code which gives the unexpected result: (I've prefixed
lines with side effects or I/O with # )

///// Actual code (unexpected result)
# cout << "Calculating fibonacci( 47 )" << endl;

# time_t startTime1 = time( nullptr );
unsigned long long int result1 = fibonacci( 47 );
# time_t endTime1 = time(nullptr);

# cout << "fibonacci( 47 ) = " << result1 << endl;

..and here is the "optimised" result: (obvious why zero seconds are
recorded)

///// Compiler reordere code
# cout << "Calculating fibonacci( 47 )" << endl;

# time_t startTime1 = time( nullptr );
# time_t endTime1 = time(nullptr);

unsigned long long int result1 = fibonacci( 47 );
# cout << "fibonacci( 47 ) = " << result1 << endl;


The OP's code which worked as expected is as like this:

///// Actual code (giving expected result)

# cout << "Calculating fibonacci( 47 )" << endl;

# time_t startTime1 = time( nullptr );
unsigned long long int result1 = fibonacci( 47 );

# cout << "fibonacci( 47 ) = " << result1 << endl;
# time_t endTime1 = time(nullptr);

Here, the second time() call can't be reordered before the fibonacci()
call because:
1) it must occur after the second cout<< call
2) the second cout<< call must occur after the fibonacci() call,
as it uses the result1 variable.

Well, we could say that still the compiler could have moved the
fibonacci() call to before the first time() call, rather than after the
second time() call. True - but who knows why it chooses which way to
optimise? (I mean, it's not deliberately trying to thwart the programmer!)


Mike.

Mike Terry

unread,
Mar 25, 2020, 8:09:13 PM3/25/20
to
..and this code worked as expected. I doubt that simply using a
variable for output /at a later point of execution/ is considered
equivalent to marking the variable as volatile /from its creation/.
(But now I'm guessing...)

Mike.


Chris Vine

unread,
Mar 25, 2020, 8:17:38 PM3/25/20
to
On Wed, 25 Mar 2020 16:49:24 -0700 (PDT)
Öö Tiib <oot...@hot.ee> wrote:
[snip]
> Yes but in OP's code result of fibonzzi() was used in output before
> calling time() and that must be as good as assigning to volatile.
>
> unsigned long long int result1 = fibonacci( 47 );
> cout << "fibonacci( 47 ) = " << result1 << endl;
> time_t endTime1 = time(nullptr);

§4.6/7 of C++17 says

The least requirements on a conforming implementation are:

* Accesses through volatile glvalues are evaluated strictly
according to the rules of the abstract machine.

* At program termination, all data written into files shall be
identical to one of the possible results that execution of the
program according to the abstract semantics would have produced.

* The input and output dynamics of interactive devices shall take
place in such a fashion that prompting output is actually
delivered before a program waits for input. What constitutes an
interactive device is implementation-defined. These collectively
are referred to as the observable behavior of the program.

I cannot say I fully understand the consequences, but it seems
reasonably clear that printing the correct value of the 47th (and 46th)
fibonacci to stdout has a computational consequence and must be
correct (within available resource limits), but printing the time at
and during which the program happens to be executed does not, in the
sense that no particular value of time is predicated by the program as
written.

So maybe the optimization is permitted. Dunno.

Öö Tiib

unread,
Mar 25, 2020, 8:29:30 PM3/25/20
to
Nope, for third time OP's code was verbatim:

cout << "fibonacci( 47 ) = " << result1 << endl;
time_t endTime1 = time(nullptr); // SEE THIS STATEMENT

You have yourself reordered.

Chris Vine

unread,
Mar 25, 2020, 8:43:23 PM3/25/20
to
And if so, as another poster has observed, making the time values
volatile should deal with the OP's issue.

Mike Terry

unread,
Mar 25, 2020, 9:24:29 PM3/25/20
to
You're not reading the OP correctly. Yes, I know what he wrote in the
"verbatim" program code.

OK, OP starts with the following paragraph explaining what the following
code is, and what the unexpected result is:

The code below as is works as expected.
But if I move the statement ending with the
comment "SEE THIS STATEMENT" to before the
previous statement,
it calculates time as 0.000000000 minutes.

Note the phrase "works as expected". This indicates that the "verbatim"
code is the code where things run IN THE EXPECTED ORDER.

Here is the verbatum code (GIVING EXPECTED RESULT):

cout << "Calculating fibonacci( 47 )" << endl;
time_t startTime1 = time( nullptr );
unsigned long long int result1 = fibonacci( 47 );


cout << "fibonacci( 47 ) = " << result1 << endl;
time_t endTime1 = time(nullptr); // SEE THIS STATEMENT
cout << "Calculation time = "
<< (endTime1 - startTime1 ) / 60.0
<< " minutes\n" << endl;

Now note the phrase "But if I move the statement.....[unexpected result]"

So, the code with the unexpected result has the time() call moved up AS
I SHOWED IT.

SUMMARY: Yes, I've re-ordered the verbatim code AS INSTRUCTED BY OP TO
REPRODUCE THE UNEXPECTED BEHAVIOUR.

When explaining the unexpected compiler behaviour there are two code
listings to consider:

a) the "actual" code given to the compiler
b) the "reordered" code as adjusted internally by the compiler

The code I quote above is the "actual" compiler input GIVING THE
UNEXPECTED RESULT. I follow this (below) by the "compiler reordered" code.

The "verbatim" code (which works as expected) I then listed (even
further below), along with an explanation for why it doesn't give
unexpected results.

No need to spend any more time on this, as I've already wasted more than
it warrents, and won't respond any further.

Alf P. Steinbach

unread,
Mar 25, 2020, 11:36:57 PM3/25/20
to
I've reproduced the issue with Visual C++ 2019, using optimization
option `/O2`. With the `time` call before the output it reports 0 time
even though the code clearly runs for several seconds.

I believe that a compiler is permitted to move the call to `time` to
before the call to `fibonacci`, since it can prove that the latter
doesn't change any state that the `time` function could depend on. The
flow of time is not regarded as part of the abstract machine state.

Adding `std::atomic_thread_fence(std::memory_order_acq_rel);` around
each `fibonacci` call did not prevent this. I don't know if it should.
But I felt it was worth trying.

Adding `volatile` to the result variables fixed it.

However, most uses of `volatile` will be deprecated in C++20; <url:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1152r4.html>.
And apparently that includes `volatile` return types. So one cannot,
apparently, quick-fix it by making `time` (and `clock`, etc.) produce
`volatile` values, if that would work, which I don't know.

This reminds me of the C++11 (or was it C++14) /carte blanche/ to
completely optimize away a provably infinite loop.

A very impractical effect for real programming, stemming from the
standard specifying things in terms of a too limited academic ivory
tower abstraction.

- Alf

Öö Tiib

unread,
Mar 26, 2020, 2:58:19 AM3/26/20
to
On Thursday, 26 March 2020 03:24:29 UTC+2, Mike Terry wrote:
>
> Now note the phrase "But if I move the statement.....[unexpected result]"
>
> So, the code with the unexpected result has the time() call moved up AS
> I SHOWED IT.
>
> SUMMARY: Yes, I've re-ordered the verbatim code AS INSTRUCTED BY OP TO
> REPRODUCE THE UNEXPECTED BEHAVIOUR.

OK. Seems that I misunderstood that part. On that case your solution
of using volatile is fine.
Unless they have done something bad (as they seem to plan) to volatile.

Öö Tiib

unread,
Mar 26, 2020, 4:30:36 AM3/26/20
to
Precisely! Idea of breaking volatile seems to come from same school as
that infinite loop mess. Making low level hardware drivers impossible
to write in C++ would result with developers being forced to use low
level hardware drivers of hardware vendors and making it impossible
for smaller hardware vendors to write their drivers in anything but
assembler (that is rare and expensive skill these days).

David Brown

unread,
Mar 26, 2020, 6:32:29 AM3/26/20
to
The compiler can't re-order things with side-effects. But calculating
fibonacci() does not have any side-effects, and the compiler can
re-arrange that with respect to any side-effect code. There is a common
misunderstanding that code observable behaviour (or potentially having
observable behaviour) also forces an order on other code - it does not.

David Brown

unread,
Mar 26, 2020, 6:40:39 AM3/26/20
to
When the code is:

unsigned long long int result1 = fibonacci( 47 );
cout << "fibonacci( 47 ) = " << result1 << endl;
time_t endTime1 = time(nullptr);

the compiler needs to write the output before measuring the time. And
in order to write the output, it must calculate the result of the fibonacci.


When the code is:

unsigned long long int result1 = fibonacci( 47 );
time_t endTime1 = time(nullptr);
cout << "fibonacci( 47 ) = " << result1 << endl;

the compiler must measure the time before writing out the result. And
in order to write the output, it must calculate the result of the fibonacci.

But it does /not/ need the result of the fibonacci in order to measure
the time. The compiler can see that calling fibonacci(47) cannot
possibly have side-effects that are relevant to the call to time().
Therefore it is fine to delay the calculation until the results are needed.

Thus this optimisation is perfectly valid.

The simple solution here is to use volatile:

volatile unsigned long long int result1 = fibonacci( 47 );
time_t endTime1 = time(nullptr);
cout << "fibonacci( 47 ) = " << result1 << endl;

This forces the compiler to calculate the results and write result1
before getting the time.

I'd also recommend the "47" be replaced by a volatile:

volatile const unsigned int N = 47;
volatile unsigned long long int result1 = fibonacci(N);
time_t endTime1 = time(nullptr);
cout << "fibonacci( 47 ) = " << result1 << endl;

A smart enough compiler could see that fibonacci(47) can be
pre-calculated at compile time, and eliminate the run-time calculation
entirely. A less smart one might eliminate some of it, but not all.

David Brown

unread,
Mar 26, 2020, 6:58:53 AM3/26/20
to
No, you are both misunderstanding this quite significantly.

The re-ordering of the calculation in this code is allowed because the
calculation of fibonacci(47) has no observable behaviour. Adding a
memory fence around it will not force any order, because "result1" is
not required to be in memory - though a compiler is certainly free to
interpret the fence more generally and use it for ordering result1.

Making result1 volatile /will/ force the desired order.

Making the return type of functions like "time" volatile is just a silly
idea. It makes no sense at all. Think about what volatile means, and
it should be obvious.

No version of C or C++ has a /carte blanche/ to remove infinite loops.
Quoting from C11 (because I have it open at the moment):

"""
An iteration statement whose controlling expression is not a constant
expression, 156) that performs no input/output operations, does not
access volatile objects, and performs no synchronization or atomic
operations in its body, controlling expression, or (in the case of a for
statement) its expression-3, may be assumed by the implementation to
terminate. 157)
"""

In C++, this is part of the "forward progress guarantee", and amounts to
the same thing. An infinite loop that does nothing is undefined
behaviour - an infinite loop that does something observable is fine.


And as for the suggested changes to "volatile", the recommendation is to
disallow complicated cases and only allow simple ones. That is /fine/ -
the complicated cases should never be used in the first place.

So if you have:

volatile int a, b, c;

then
a = 1;
b = c;

and the like are still allowed. You can have pointers-to-volatile, you
can read and write volatile data, you can use pointer casts to make a
"use a volatile access here" expressions (which every compiler has
allowed, but was not defined behaviour until C18).

What you can't do are things like:

a = b++ + c++;

That's fine. There is no standard for saying how these things should
work, and compilers interpret then in very different ways.

If your low-level drivers are affected by the new restrictions in
volatile, then they /should/ be re-written - even if you are using older
C++ standards.


Öö Tiib

unread,
Mar 26, 2020, 11:11:07 AM3/26/20
to
I misunderstood that VS somehow reorders call to time() (that has to
involve input or read of volatile) and cout output.

Öö Tiib

unread,
Mar 26, 2020, 11:13:29 AM3/26/20
to
That was already covered elsewhere in thread.
>
> Making result1 volatile /will/ force the desired order.

Unless they *deprecate* and *break* the volatile as they seemingly
plan to.

Ned Latham

unread,
Mar 26, 2020, 1:27:32 PM3/26/20
to
David Brown wrote:
> Öö Tiib wrote:

----snip----

> > Things like input-output and access to volatile are side-effects.
> > Things with side effects may not be reordered.
> > I can't imagine how Windows can figure time without access of
> > something volatile or doing input from somewhere so perhaps MS
> > optimizer has screwed up tracking of what of its API has side
> > effects.
>
> The compiler can't re-order things with side-effects. But calculating
> fibonacci() does not have any side-effects, and the compiler can
> re-arrange that with respect to any side-effect code.

Frankly, I can't see any reason whatever for reordering the programmer's
code. If an optimiser sees an opportunity, it can suggest something, but
it should be a suggestion only.

> There is a common
> misunderstanding that code observable behaviour (or potentially having
> observable behaviour) also forces an order on other code - it does not.

Code order should always be the programmer's province.

Chris Vine

unread,
Mar 26, 2020, 4:27:04 PM3/26/20
to
That makes sense. Thanks.

I am not sure that that much freedom is a good idea though.

David Brown

unread,
Mar 26, 2020, 5:42:35 PM3/26/20
to
Personally, I have no doubt that it is an excellent idea. It would be
very difficult to specify rules that ban this case but allow other cases
of re-arranging code and evaluations - and without the possibility of
re-arrangement, you would cripple performance in many cases. (/You/ may
have a super fast modern x86 cpu that does all sorts of hardware
re-ordering of instructions, but not everyone does.)

C and C++ have always had the "as if" rule and the concept of observable
behaviour - and time is not observable. Any re-arrangements that don't
affect the observable behaviour are allowed.

David Brown

unread,
Mar 26, 2020, 5:46:54 PM3/26/20
to
Only in assembly.

C and C++ are high level languages. You say what you want, in terms of
the language whose observable behaviour is defined for an abstract
machine. The compiler can do whatever it wants to get object code that
matches this behaviour. Intermediary steps between observable events
(like passing output from the program, or calling an external function
such as "time()") are irrelevant in the language, and the compiler can
do whatever it wants there. A good compiler will usually do what it can
to minimise the time this takes, but there may be other considerations
(typically controlled by compiler flags) such as ease of debugging.

The order of observable behaviour /is/ in the programmer's province.
The process of calculating a pure function is not observable.

David Brown

unread,
Mar 26, 2020, 5:57:59 PM3/26/20
to
On 26/03/2020 16:13, Öö Tiib wrote:
> On Thursday, 26 March 2020 12:58:53 UTC+2, David Brown wrote:
>> On 26/03/2020 09:30, Öö Tiib wrote:
>>> On Thursday, 26 March 2020 05:36:57 UTC+2, Alf P. Steinbach wrote:
<snip>
Did you read the paper linked (P1152r4) ?

"""
The proposed deprecation preserves the useful parts of volatile, and
removes the dubious / already broken ones. This paper aims at breaking
at compile-time code which is today subtly broken at runtime or through
a compiler update. The paper might also break another type of code: that
which doesn’t exist. This removes a significant foot-gun and removes
unintuitive corner cases from the languages.
"""

Did you look at the votes? The solid majority of the working groups
were in favour. Do you think they /all/ forgot that people use C++ in
embedded programming?

Can you give an example of a use of volatile that you think is good,
solid programming practice in pre-C++20 but is not deprecated? At
worst, you have to write "x = x + 1;" instead of "x++;".

Chris M. Thomasson

unread,
Mar 26, 2020, 9:02:33 PM3/26/20
to
[...]

Right. The memory fence can be optimized away because it has no other
atomic operations to synchronize with. There needs to be a
std::atomic<T> around somewhere to sync with.

James Kuyper

unread,
Mar 27, 2020, 12:21:54 AM3/27/20
to
On 3/26/20 5:42 PM, David Brown wrote:
...
> C and C++ have always had the "as if" rule and the concept of observable
> behaviour - and time is not observable. Any re-arrangements that don't
> affect the observable behaviour are allowed.

C has had the "least requirements" clause ever since it was first
standardized - but the term "observable behavior" wasn't used in
connection with those requirements until C2011. The actual requirements
imposed by C were essentially the same as those imposed by C++, but C++
defined a convenient term for discussing those requirements, while C
didn't. The C++ standard remains much clearer than the C standard with
respect to the role that observable behavior plays in judging the
conformance of an implementation.

I've lost my copies of the oldest versions of the C++ standard, but as I
remember it, it has defined the term "observable behavior" ever since it
was first standardized - but early versions of the standard had a
different definition of that term than current ones.

The oldest draft of the standard that I have is n2723.pdf (2008?). It
says "The observable behavior of the abstract machine is its sequence of
reads and writes to volatile data and calls to library I/O functions."
The "least requirements" clause was not connected to the term
"observable behavior" in that version.
The next oldest draft version I have is n3035.pdf (2010?). In that
version, the connection between the "least requirements" and "observable
behavior" is essentially the same as in the current standard.

Öö Tiib

unread,
Mar 27, 2020, 4:03:19 AM3/27/20
to
Yes, like I did read the paper where they deprecated constexpr detection
compile time. It is typical programmer does not know what programmer is
doing lie following with taking away the essential tool.

>
> """
> The proposed deprecation preserves the useful parts of volatile, and
> removes the dubious / already broken ones. This paper aims at breaking
> at compile-time code which is today subtly broken at runtime or through
> a compiler update. The paper might also break another type of code: that
> which doesn’t exist. This removes a significant foot-gun and removes
> unintuitive corner cases from the languages.
> """

Basically it is saying that C++17 library was implemented
volatile-incorrect in implementations and so lets remove volatile
as fix. Lets take away the possibility that someone may have something
implemented volatile-correctly at all.


> Did you look at the votes? The solid majority of the working groups
> were in favour. Do you think they /all/ forgot that people use C++ in
> embedded programming?

Popular votes in this world have left me always indifferent. And those
are votes of wormtongues counted. They certainly *know* that now
embedded programming in C++ is double tedious if not impossible.

> Can you give an example of a use of volatile that you think is good,
> solid programming practice in pre-C++20 but is not deprecated? At
> worst, you have to write "x = x + 1;" instead of "x++;".

Basically I read it that instead of member functions of volatile unions
I now have to use inline assembler again since it will be more readable
than C++, even if it is somehow possible to write in C++ at all (should
invest tens of man-days into research). And with next standard they most
likely break even that "somehow".

Alf P. Steinbach

unread,
Mar 27, 2020, 4:47:20 AM3/27/20
to
On 27.03.2020 05:21, James Kuyper wrote:
> On 3/26/20 5:42 PM, David Brown wrote:
> ...
>> C and C++ have always had the "as if" rule and the concept of observable
>> behaviour - and time is not observable. Any re-arrangements that don't
>> affect the observable behaviour are allowed.
>
> C has had the "least requirements" clause ever since it was first
> standardized - but the term "observable behavior" wasn't used in
> connection with those requirements until C2011. The actual requirements
> imposed by C were essentially the same as those imposed by C++, but C++
> defined a convenient term for discussing those requirements, while C
> didn't. The C++ standard remains much clearer than the C standard with
> respect to the role that observable behavior plays in judging the
> conformance of an implementation.
>
> I've lost my copies of the oldest versions of the C++ standard, but as I
> remember it, it has defined the term "observable behavior" ever since it
> was first standardized - but early versions of the standard had a
> different definition of that term than current ones.

C++98 §1.9/6:

“The observable behavior of the abstract machine is its sequence of
reads and writes to volatile data and calls to library I/O functions”

C++17 §4.6/7 (via final draft N4659):

“The least requirements on a conforming implementation are:
— Accesses through volatile glvalues are evaluated strictly according to
the rules of the abstract machine.
— At program termination, all data written into files shall be identical
to one of the possible results that execution of the program according
to the abstract semantics would have produced.
— The input and output dynamics of interactive devices shall take place
in such a fashion that prompting output is actually delivered before a
program waits for input. What constitutes an interactive device is
implementation-defined.
These collectively are referred to as the /observable behavior/ of the
program”


> The oldest draft of the standard that I have is n2723.pdf (2008?). It
> says "The observable behavior of the abstract machine is its sequence of
> reads and writes to volatile data and calls to library I/O functions."
> The "least requirements" clause was not connected to the term
> "observable behavior" in that version.
> The next oldest draft version I have is n3035.pdf (2010?). In that
> version, the connection between the "least requirements" and "observable
> behavior" is essentially the same as in the current standard.

- Alf

David Brown

unread,
Mar 27, 2020, 7:22:19 AM3/27/20
to
That must be a different paper. I could not find a reference to
anything relevant in a quick search.

> It is typical programmer does not know what programmer is
> doing lie following with taking away the essential tool.

As I couldn't find a paper on "deprecated constexpr detection", I can't
comment on it. But AFAICS the "deprecate volatile" paper stands on its
own - and should be judged on its own. It makes no sense to me to say
it must be a bad thing to deprecate complex volatile uses just because
you didn't like a paper on deprecating constexpr detection.

>
>>
>> """
>> The proposed deprecation preserves the useful parts of volatile, and
>> removes the dubious / already broken ones. This paper aims at breaking
>> at compile-time code which is today subtly broken at runtime or through
>> a compiler update. The paper might also break another type of code: that
>> which doesn’t exist. This removes a significant foot-gun and removes
>> unintuitive corner cases from the languages.
>> """
>
> Basically it is saying that C++17 library was implemented
> volatile-incorrect in implementations and so lets remove volatile
> as fix. Lets take away the possibility that someone may have something
> implemented volatile-correctly at all.
>

No one is removing or deprecating volatile.

Simple volatile usage is almost invariably compiled correctly, so that
the object code does exactly what the programmer expected. Complex
cases are riddled with compiler bugs, programmer confusion and
misunderstanding, underspecification in the standards, and alternative
implementations that are each arguably correct. The change in C++20 has
nothing to do with the C++17 library - it is nothing more nor less than
an admission that complex volatile usage is ripe with errors,
unnecessary in practice, and thus should be removed from the language.

>
>> Did you look at the votes? The solid majority of the working groups
>> were in favour. Do you think they /all/ forgot that people use C++ in
>> embedded programming?
>
> Popular votes in this world have left me always indifferent. And those
> are votes of wormtongues counted. They certainly *know* that now
> embedded programming in C++ is double tedious if not impossible.
>

You really think the C++ standards groups are /deliberately/ and
/knowingly/ sabotaging the language for its use in low-level
programming? That is real tin-foil hat stuff.

>> Can you give an example of a use of volatile that you think is good,
>> solid programming practice in pre-C++20 but is not deprecated? At
>> worst, you have to write "x = x + 1;" instead of "x++;".
>
> Basically I read it that instead of member functions of volatile unions
> I now have to use inline assembler again since it will be more readable
> than C++, even if it is somehow possible to write in C++ at all (should
> invest tens of man-days into research). And with next standard they most
> likely break even that "somehow".
>

All you have to do is make sure your volatile accesses are simple and
explicit.

If you think your current code is deprecated by the new standard, please
show a small example. If I am wrong here, I would like to know about it!

Öö Tiib

unread,
Mar 27, 2020, 10:58:56 AM3/27/20
to
Paper is that removal of deprecated legacy exception specifications.
<http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0003r5.html>
Basically that:
<https://timsong-cpp.github.io/cppwp/n4140/expr.unary.noexcept>
notice "unless the call is a constant expression"
Was changed to that:
<https://eel.is/c++draft/expr.unary.noexcept>
notice link to [except.spec] that does not anymore say
that constant expressions can not be "potentially throwing".
Typical work of wormtongues.

>
> >
> >>
> >> """
> >> The proposed deprecation preserves the useful parts of volatile, and
> >> removes the dubious / already broken ones. This paper aims at breaking
> >> at compile-time code which is today subtly broken at runtime or through
> >> a compiler update. The paper might also break another type of code: that
> >> which doesn’t exist. This removes a significant foot-gun and removes
> >> unintuitive corner cases from the languages.
> >> """
> >
> > Basically it is saying that C++17 library was implemented
> > volatile-incorrect in implementations and so lets remove volatile
> > as fix. Lets take away the possibility that someone may have something
> > implemented volatile-correctly at all.
> >
>
> No one is removing or deprecating volatile.
>
> Simple volatile usage is almost invariably compiled correctly, so that
> the object code does exactly what the programmer expected. Complex
> cases are riddled with compiler bugs, programmer confusion and
> misunderstanding, underspecification in the standards, and alternative
> implementations that are each arguably correct. The change in C++20 has
> nothing to do with the C++17 library - it is nothing more nor less than
> an admission that complex volatile usage is ripe with errors,
> unnecessary in practice, and thus should be removed from the language.

Yes, and so we should use assembler to write device drivers.

> >> Did you look at the votes? The solid majority of the working groups
> >> were in favour. Do you think they /all/ forgot that people use C++ in
> >> embedded programming?
> >
> > Popular votes in this world have left me always indifferent. And those
> > are votes of wormtongues counted. They certainly *know* that now
> > embedded programming in C++ is double tedious if not impossible.
> >
>
> You really think the C++ standards groups are /deliberately/ and
> /knowingly/ sabotaging the language for its use in low-level
> programming? That is real tin-foil hat stuff.

What else explanation there is? Useless tool is useless tool.
Who made it useless weren't morons, so they made it specially.


> >> Can you give an example of a use of volatile that you think is good,
> >> solid programming practice in pre-C++20 but is not deprecated? At
> >> worst, you have to write "x = x + 1;" instead of "x++;".
> >
> > Basically I read it that instead of member functions of volatile unions
> > I now have to use inline assembler again since it will be more readable
> > than C++, even if it is somehow possible to write in C++ at all (should
> > invest tens of man-days into research). And with next standard they most
> > likely break even that "somehow".
> >
>
> All you have to do is make sure your volatile accesses are simple and
> explicit.
>
> If you think your current code is deprecated by the new standard, please
> show a small example. If I am wrong here, I would like to know about it!

Basically I can not imagine how to write any device driver in C++ without
volatile member functions nor operators. Say VGA compatible text mode
driver?

Basically we have to use old stinky C macros that reinterpret cast
constant uint values to volatile pointer values on every step and
then do arithmetic with those in large blob functions. Happen to have
whole array of such peripherals? Be explicit and copy paste, no way
to have volatile objects. And perhaps that reinterpret_cast is
undefined behavior as well.
Strong pressure seemed to be to screw up std::atomic as well.
Meanwhile not all implementations have <stdatomic.h> of C yet as it
is C11.

Macro assembler or perhaps C (if they do not trash that as well)
remain the viable options.

David Brown

unread,
Mar 27, 2020, 11:56:48 AM3/27/20
to
This is getting a little out of my depth here, so I hope someone will
chime in if I'm wrong. But how could a constant expression be
potentially throwing? I can't see anything here that has changed except
that the terms have been made a little more general.


>>
>>>
>>>>
>>>> """
>>>> The proposed deprecation preserves the useful parts of volatile, and
>>>> removes the dubious / already broken ones. This paper aims at breaking
>>>> at compile-time code which is today subtly broken at runtime or through
>>>> a compiler update. The paper might also break another type of code: that
>>>> which doesn’t exist. This removes a significant foot-gun and removes
>>>> unintuitive corner cases from the languages.
>>>> """
>>>
>>> Basically it is saying that C++17 library was implemented
>>> volatile-incorrect in implementations and so lets remove volatile
>>> as fix. Lets take away the possibility that someone may have something
>>> implemented volatile-correctly at all.
>>>
>>
>> No one is removing or deprecating volatile.
>>
>> Simple volatile usage is almost invariably compiled correctly, so that
>> the object code does exactly what the programmer expected. Complex
>> cases are riddled with compiler bugs, programmer confusion and
>> misunderstanding, underspecification in the standards, and alternative
>> implementations that are each arguably correct. The change in C++20 has
>> nothing to do with the C++17 library - it is nothing more nor less than
>> an admission that complex volatile usage is ripe with errors,
>> unnecessary in practice, and thus should be removed from the language.
>
> Yes, and so we should use assembler to write device drivers.

Do you think that complex, unreliable, under-specified volatile
expressions were needed to write device drivers? I certainly don't.
Low-level coding is my main job (mainly in C, but that makes no
difference here). I use "volatile" regularly. There is nothing in the
changes to volatile in C++20 that I dislike or that would cause any
hardship in my coding.
I am seeing a great deal of anger and fear here, and very little
rational thought or explanation. That is not like you. I am still
waiting for concrete examples - not another claim about how this will
make low-level programming impossible, but a simple real example.

James Kuyper

unread,
Mar 27, 2020, 12:53:47 PM3/27/20
to
On Friday, March 27, 2020 at 10:58:56 AM UTC-4, Öö Tiib wrote:
> On Friday, 27 March 2020 13:22:19 UTC+2, David Brown wrote:
> > On 27/03/2020 09:03, Öö Tiib wrote:
...
> > > Yes, like I did read the paper where they deprecated constexpr detection
> > > compile time.
> >
> > That must be a different paper. I could not find a reference to
> > anything relevant in a quick search.
> >
> > > It is typical programmer does not know what programmer is
> > > doing lie following with taking away the essential tool.
> >
> > As I couldn't find a paper on "deprecated constexpr detection", I can't
> > comment on it. But AFAICS the "deprecate volatile" paper stands on its
> > own - and should be judged on its own. It makes no sense to me to say
> > it must be a bad thing to deprecate complex volatile uses just because
> > you didn't like a paper on deprecating constexpr detection.
>
> Paper is that removal of deprecated legacy exception specifications.
> <http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0003r5.html>

What does that have to do with "deprecating constexpr detection [at]
compile time"? There are seven occurrences of "constexpr" in that paper
- none of them involve making it deprecated. Also, what connection do
you see between this paper and "volatile". There's only one occurrence
of "volatile" in that paper, and it doesn't seem to have anything to do
with what you're complaining about.

> Basically that:
> <https://timsong-cpp.github.io/cppwp/n4140/expr.unary.noexcept>
> notice "unless the call is a constant expression"
> Was changed to that:
> <https://eel.is/c++draft/expr.unary.noexcept>
> notice link to [except.spec] that does not anymore say
> that constant expressions can not be "potentially throwing".

As david said, how could something that meets the requirements to be
described as a "constant expression" be also "potentially throwing"?

> Typical work of wormtongues.

Removing redundant specification is the "work of wormtongues"? In that
case, maybe we need more of them on the committee.

...
> > Simple volatile usage is almost invariably compiled correctly, so that
> > the object code does exactly what the programmer expected. Complex
> > cases are riddled with compiler bugs, programmer confusion and
> > misunderstanding, underspecification in the standards, and alternative
> > implementations that are each arguably correct. The change in C++20 has
> > nothing to do with the C++17 library - it is nothing more nor less than
> > an admission that complex volatile usage is ripe with errors,
> > unnecessary in practice, and thus should be removed from the language.
>
> Yes, and so we should use assembler to write device drivers.

Can you give a specific example of code that used to be permitted, which
will now be deprecated,as a result of which we'll be forced to use
assembler instead?

...
> > > Popular votes in this world have left me always indifferent. And those

Keep in mind that this isn't a simple popularity contest. These are
votes of experts serving on the C++ committee. These are people who
either implement C++ or use it (roughly equal numbers of each); either
way, they are sufficiently interested in C++ to volunteer to spend
their free time (unpaid) to work to make it better. Why would such want
to destroy C++? They might destroy it unintentionally, but why would
they do it deliberately, as you claim?

...
> > You really think the C++ standards groups are /deliberately/ and
> > /knowingly/ sabotaging the language for its use in low-level
> > programming? That is real tin-foil hat stuff.
>
> What else explanation there is? Useless tool is useless tool.
> Who made it useless weren't morons, so they made it specially.

One alternative explanation is that you do as bad a job of reading
English as you do of writing it (which seems quite plausible), and as a
result you've misunderstood something about the paper you're complaining
about, that made you incorrectly think a useful tool was actually
useless. If you could provide a simple example of code that you think
would become deprecated if that paper is approved, that would go a long
way toward figuring out what you've misunderstood (if you're wrong), or
what we've misunderstood (if you're right).

...
> > All you have to do is make sure your volatile accesses are simple and
> > explicit.
> >
> > If you think your current code is deprecated by the new standard, please
> > show a small example. If I am wrong here, I would like to know about it!
>
> Basically I can not imagine how to write any device driver in C++ without
> volatile member functions nor operators. Say VGA compatible text mode

Please give a specific example of a volatile member function or operator
that you think is deprecated by that paper.

...
> Basically we have to use old stinky C macros that reinterpret cast
> constant uint values to volatile pointer values on every step and
> then do arithmetic with those in large blob functions.

Please provide an example of currently permitted C++ code that would
have to be replaced by such C macros if that paper is approved.

Note: if you do provide an example, I expect the immediate response will
be "Why in the world do you think such code will be deprecated by that
paper?". Therefore, it would save everyone some time if you would
accompany each example with a citation of the specific words from that
paper which left you with the impression that it would deprecate that
example.

Öö Tiib

unread,
Mar 27, 2020, 12:59:28 PM3/27/20
to
Standard does not say anymore that it cannot be potentially throwing so
it can. Look that:

#include <iostream>

constexpr int foo(int x) {return x;}

int main()
{
std::cout << (noexcept(foo(42)) ? "constant expressions can not throw\n"
: "constant expressions may throw\n");
}

Demo <http://coliru.stacked-crooked.com/a/7503ebfcc2474e7b>

The program does not even say when compiled in -std=c++14 mode anymore
that "constant expressions can not throw" yet it did few versions
of gcc ago.
So these are some rather strong forces behind requirement that constant
expressions *should* be throwing.
But it is understandable if you do not write device drivers in C++.
You do not consider how C++ features could be used to make those
handier so you perhaps see no difference.
I can't give code since I am not sole owner of it. There are compilers
on what it works like was specified by standards. The ongoing events
however remove point of considering open sourcing such code. So why
should I be happy?

Öö Tiib

unread,
Mar 27, 2020, 1:17:09 PM3/27/20
to
On Friday, 27 March 2020 18:53:47 UTC+2, James Kuyper wrote:
>
> Removing redundant specification is the "work of wormtongues"? In that
> case, maybe we need more of them on the committee.

I replied to David Brown. It is not about debate here who debates better,
call that is a constant expression is noexcept(false) since C++17.
Both gcc and clang have made it so and rejected to repair:
https://bugs.llvm.org/show_bug.cgi?id=15481
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=87603

David Brown

unread,
Mar 27, 2020, 1:59:58 PM3/27/20
to
This is either a mistake in the way it is defined (these things happen -
the C++ standards folks are not infallible), or intentional. If it is
intentional, there will be a reason behind it - perhaps it is because
constexpr functions /can/ throw (when evaluated at run-time), and people
prefer that noexcept() returns consistent values even if that is
pessimistic. Maybe there are other reasons. But I severely doubt that
there is someone sitting in a chair, stroking his cat and saying, "How
shall we cause trouble for people? Especially those that dare to use
C++ for low-level programming? I know, we'll make changes to noexcept()
- that will get them!". (Forgetting, of course, that for a lot of
low-level and embedded work exceptions are disabled.)
So show me!
I don't want a section of a commercial program. I want a simple, short
example case.

Öö Tiib

unread,
Mar 27, 2020, 2:52:49 PM3/27/20
to
So the constant expression foo(42) can throw since it is somehow
evaluated run-time? May be ... I don't know how. Reasons why
exceptions are often disabled in embedded world are unrelated and
even the situation of these being disabled is actually non-conforming.
If you can write with straight face that compile time evaluated expression
may throw is normal then also you will write with same straight face that
my code is garbage. So lets better drop that.
Message has been deleted

James Kuyper

unread,
Mar 27, 2020, 5:08:32 PM3/27/20
to
On 3/27/20 2:52 PM, Öö Tiib wrote:
> On Friday, 27 March 2020 19:59:58 UTC+2, David Brown wrote:
>> On 27/03/2020 17:59, Öö Tiib wrote:
>>> On Friday, 27 March 2020 17:56:48 UTC+2, David Brown wrote:
...
...
>> So show me!
>
> If you can write with straight face that compile time evaluated expression
> may throw is normal then also you will write with same straight face that
> my code is garbage. So lets better drop that.


"For a constexpr function or constexpr constructor that is neither
defaulted nor a template, if no argument values exist such that an
invocation of the function or constructor could be an evaluated
subexpression of a core constant expression (8.20), or, for a
constructor, a constant initializer for some object (6.6.2), the program
is ill-formed, no diagnostic required. [ Example:

constexpr int f(bool b)
{ return b ? throw 0 : 0; } // OK
constexpr int f() { return f(true); } // ill-formed, no diagnostic required"

(n4659.pdf 10.1.5p5)

Given the above declarations, what would you prefer for
noexcept(f(true)) to return?

Öö Tiib

unread,
Mar 27, 2020, 9:20:40 PM3/27/20
to
false

David Brown

unread,
Mar 28, 2020, 11:45:09 AM3/28/20
to
On 27/03/2020 19:52, Öö Tiib wrote:
> On Friday, 27 March 2020 19:59:58 UTC+2, David Brown wrote:

>> This is either a mistake in the way it is defined (these things happen -
>> the C++ standards folks are not infallible), or intentional. If it is
>> intentional, there will be a reason behind it - perhaps it is because
>> constexpr functions /can/ throw (when evaluated at run-time), and people
>> prefer that noexcept() returns consistent values even if that is
>> pessimistic. Maybe there are other reasons. But I severely doubt that
>> there is someone sitting in a chair, stroking his cat and saying, "How
>> shall we cause trouble for people? Especially those that dare to use
>> C++ for low-level programming? I know, we'll make changes to noexcept()
>> - that will get them!". (Forgetting, of course, that for a lot of
>> low-level and embedded work exceptions are disabled.)
>
> So the constant expression foo(42) can throw since it is somehow
> evaluated run-time? May be ... I don't know how.

That's not what I said. I said that "foo()" might be evaluated at
run-time. Perhaps foo(43) would throw - and then "foo(43)" is not a
constant expression. My understanding is that "noexcept(..)" should not
evaluate its operand (like sizeof() and other such operators). It
merely requires the type of the parameters, and their values don't
matter. So if "foo(43)" might throw, then as far as noexcept() is
concerned, "foo(x)" could throw regardless of which "x" you use.

Having said that, it surprises me a little that the compiler can't use
its knowledge of foo (whose definition must be visible) to see that
"foo(x)" never throws for any x, and thus consider it as though it were
marked "noexcept" if it knows there are no exceptions ever thrown.

My guess for this is one of complexity and consistency. In general, it
would be impossible for a compiler to be see that a non-throwing
function really cannot throw - it would be solving the halting problem.
In practice, it would be easy to see for some functions and hard for
other functions. This would mean that some compilers would be able to
see that a function is non-throwing, while others would not do enough
analysis and thus treat it as "might throw". This would be a very
unpleasant inconsistency.

The only clear, consistent and understandable rule would be to say that
constexpr functions are like other functions - they must be considered
as potentially throwing unless explicitly marked nonexcept by the
programmer.

(This is based on my understanding and reasoning, rather than the
standards - frankly, I haven't managed to get my head around the
standards here as yet.)

> Reasons why
> exceptions are often disabled in embedded world are unrelated and
> even the situation of these being disabled is actually non-conforming.
>

That is correct. I mentioned it merely to show the silliness of your
idea that the committee members are deliberately sabotaging embedded and
low-level programming in C++.


>>>>
>>>> Do you think that complex, unreliable, under-specified volatile
>>>> expressions were needed to write device drivers? I certainly don't.
>>>> Low-level coding is my main job (mainly in C, but that makes no
>>>> difference here). I use "volatile" regularly. There is nothing in the
>>>> changes to volatile in C++20 that I dislike or that would cause any
>>>> hardship in my coding.
>>>
>>> But it is understandable if you do not write device drivers in C++.
>>> You do not consider how C++ features could be used to make those
>>> handier so you perhaps see no difference.
>>
>> So show me!
>
> If you can write with straight face that compile time evaluated expression
> may throw is normal then also you will write with same straight face that
> my code is garbage. So lets better drop that.
>

Currently, I don't think I would consider your code (that requires
volatile usage that will be deprecated in C++20) to be garbage. I
consider it imaginary. (I believe that this is due to your
misunderstandings about what the changes to volatile in C++20 mean, not
because I think you are being deliberately awkward.)

James Kuyper

unread,
Mar 28, 2020, 1:23:09 PM3/28/20
to
On 3/28/20 11:44 AM, David Brown wrote:
> On 27/03/2020 19:52, Öö Tiib wrote:
...
> The only clear, consistent and understandable rule would be to say that
> constexpr functions are like other functions - they must be considered
> as potentially throwing unless explicitly marked nonexcept by the
> programmer.
>
> (This is based on my understanding and reasoning, rather than the
> standards - frankly, I haven't managed to get my head around the
> standards here as yet.)

"The predicate indicating whether a function cannot exit via an
exception is called the exception specification of the function. If the
predicate is false, the function has a potentially-throwing exception
specification, otherwise it has a non-throwing exception specification.
The exception specification is either defined implicitly, or defined
explicitly by using a noexcept-specifier as a suffix of a function
declarator (11.3.5).
noexcept-specifier:
noexcept ( constant-expression )
noexcept
throw ( )"
(18.4p1).

Note that the phrases "exception specification", "potentially-throwing
exception specification" and "non-throwing exception specification" are
all italicized, an ISO convention indicating that this clause
constitutes the official definition of those terms.

Therefore, the value of a noexcept() expression is determined entirely
by the exception specification of the function. The body of the function
plays no role in determining that value, not even if it's declared
inline in the same translation unit.
...
>> If you can write with straight face that compile time evaluated expression
>> may throw is normal then also you will write with same straight face that
>> my code is garbage. So lets better drop that.
>>
>
> Currently, I don't think I would consider your code (that requires
> volatile usage that will be deprecated in C++20) to be garbage. I
> consider it imaginary. (I believe that this is due to your
> misunderstandings about what the changes to volatile in C++20 mean, not
> because I think you are being deliberately awkward.)

That's why we both keep asking him to provide examples - so we can say
to him "such code will not be deprecated in C++2020".

Öö Tiib

unread,
Mar 28, 2020, 1:42:35 PM3/28/20
to
I already did cite relevant parts of C++14 where it was required and
C++17 where it was removed. These were 8 words "unless the call is a
constant expression". So if foo(43) is not constant expression but
foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
true. I do not see what about it was inconsistent. I see how it was
great. I could compile time decide between calling non-constexpr
algorithms (library functions, compiler intrinsics, inline assembler
or some function that did cache something) and some potentially less
efficient runtime but constexpr algorithms in C++14.
And I am 100% sure that all compilers do that constant expression
analysis just telling the results to programmer were suddenly
forbidden by C++17.

> The only clear, consistent and understandable rule would be to say that
> constexpr functions are like other functions - they must be considered
> as potentially throwing unless explicitly marked nonexcept by the
> programmer.

Your immediate reaction was different: "But how could a constant
expression be potentially throwing?". Now you are advocating that
it is most consistent for it to be potentially throwing.

>
> (This is based on my understanding and reasoning, rather than the
> standards - frankly, I haven't managed to get my head around the
> standards here as yet.)
>
> > Reasons why
> > exceptions are often disabled in embedded world are unrelated and
> > even the situation of these being disabled is actually non-conforming.
> >
>
> That is correct. I mentioned it merely to show the silliness of your
> idea that the committee members are deliberately sabotaging embedded and
> low-level programming in C++.

Not only embedded programming on case of constexpr. Whole tool of detecting
constant expressions in whatever context was taken away. Programmer has
to make that analysis manually. I did show that it was made even in
C++11 and C++14 (that had the requirement) modes of compilers.
So fine, I am just stupid and silly when I complain that our tools were
clearly and deliberately damaged. Damaged with unprecedented retroactive
effect in C++11 and C++14 modes as well.

>
> >>>>
> >>>> Do you think that complex, unreliable, under-specified volatile
> >>>> expressions were needed to write device drivers? I certainly don't.
> >>>> Low-level coding is my main job (mainly in C, but that makes no
> >>>> difference here). I use "volatile" regularly. There is nothing in the
> >>>> changes to volatile in C++20 that I dislike or that would cause any
> >>>> hardship in my coding.
> >>>
> >>> But it is understandable if you do not write device drivers in C++.
> >>> You do not consider how C++ features could be used to make those
> >>> handier so you perhaps see no difference.
> >>
> >> So show me!
> >
> > If you can write with straight face that compile time evaluated expression
> > may throw is normal then also you will write with same straight face that
> > my code is garbage. So lets better drop that.
> >
>
> Currently, I don't think I would consider your code (that requires
> volatile usage that will be deprecated in C++20) to be garbage. I
> consider it imaginary. (I believe that this is due to your
> misunderstandings about what the changes to volatile in C++20 mean, not
> because I think you are being deliberately awkward.)

The change deprecates volatile qualified member functions and so
possibility to detect volatile this with overload resolution.
You have technically already rejected usefulness of it with your
praise of changes and don't see value in it.

I am convinced in it because you do not see value in tool of detecting
constexpr with explicit code but instead call it inconsistent. Yes,
constant expressions weren't made detectable with some
std::is_constexpr(foo(42)) but it wasn't needed as there was possible
to write it using noexcept.

On any case both possibilities that I am just stupid and silly
in wanting my code to work or that there is sabotage of my tools
make me equally unhappy.

David Brown

unread,
Mar 28, 2020, 3:35:23 PM3/28/20
to
On 28/03/2020 18:42, Öö Tiib wrote:
> On Saturday, 28 March 2020 17:45:09 UTC+2, David Brown wrote:
>> On 27/03/2020 19:52, Öö Tiib wrote:
>>> On Friday, 27 March 2020 19:59:58 UTC+2, David Brown wrote:

> Not only embedded programming on case of constexpr. Whole tool of detecting
> constant expressions in whatever context was taken away. Programmer has
> to make that analysis manually. I did show that it was made even in
> C++11 and C++14 (that had the requirement) modes of compilers.
> So fine, I am just stupid and silly when I complain that our tools were
> clearly and deliberately damaged. Damaged with unprecedented retroactive
> effect in C++11 and C++14 modes as well.
>

noexcept() has never been about "detecting constant expressions"!

Until C++20, there was no way of detecting constant expressions. Now
there is "is_constant_evaluated", constinit and consteval. To my mind,
C++20 has significantly improved the scope for constant expressions and
compile-time programming.

>>
>>>>>>
>>>>>> Do you think that complex, unreliable, under-specified volatile
>>>>>> expressions were needed to write device drivers? I certainly don't.
>>>>>> Low-level coding is my main job (mainly in C, but that makes no
>>>>>> difference here). I use "volatile" regularly. There is nothing in the
>>>>>> changes to volatile in C++20 that I dislike or that would cause any
>>>>>> hardship in my coding.
>>>>>
>>>>> But it is understandable if you do not write device drivers in C++.
>>>>> You do not consider how C++ features could be used to make those
>>>>> handier so you perhaps see no difference.
>>>>
>>>> So show me!
>>>
>>> If you can write with straight face that compile time evaluated expression
>>> may throw is normal then also you will write with same straight face that
>>> my code is garbage. So lets better drop that.
>>>
>>
>> Currently, I don't think I would consider your code (that requires
>> volatile usage that will be deprecated in C++20) to be garbage. I
>> consider it imaginary. (I believe that this is due to your
>> misunderstandings about what the changes to volatile in C++20 mean, not
>> because I think you are being deliberately awkward.)
>
> The change deprecates volatile qualified member functions and so
> possibility to detect volatile this with overload resolution.
> You have technically already rejected usefulness of it with your
> praise of changes and don't see value in it.

I have asked you repeatedly to show an example that /you/ think is good
code design, and which relies on a feature of volatile that is (or which
you think is) deprecated in C++20. I have not asked you to show code
you think /I/ will like - that would be entirely unreasonable.

>
> I am convinced in it because you do not see value in tool of detecting
> constexpr with explicit code but instead call it inconsistent. Yes,
> constant expressions weren't made detectable with some
> std::is_constexpr(foo(42)) but it wasn't needed as there was possible
> to write it using noexcept.

You are conflating three completely separate concepts here. noexcept()
has nothing to do with detecting constant expressions. And neither of
these is remotely connected with volatile or the changes to volatile in
C++20.

I am still unsure as to whether you have misunderstood something in
C++20 (or perhaps in an earlier version), or if you have a point which
you are failing to demonstrate. Until we get an example, no one can tell.

James Kuyper

unread,
Mar 28, 2020, 3:39:09 PM3/28/20
to
On 3/28/20 1:42 PM, Öö Tiib wrote:
> On Saturday, 28 March 2020 17:45:09 UTC+2, David Brown wrote:
...
>> Currently, I don't think I would consider your code (that requires
>> volatile usage that will be deprecated in C++20) to be garbage. I
>> consider it imaginary. (I believe that this is due to your
>> misunderstandings about what the changes to volatile in C++20 mean, not
>> because I think you are being deliberately awkward.)
>
> The change deprecates volatile qualified member functions and so

P1152R0 mentions deprecating volatile-qualified member functions. The
notes concerning the changes between r0 and r1 list the results of votes
on several different issues involving volatile-qualified member
functions, all but one of which was approved. However, in r4, I can find
no other mention of deprecating them. Can you point me to the clause (in
P1152R4) where that occurs?

Öö Tiib

unread,
Mar 28, 2020, 3:42:42 PM3/28/20
to
On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
>
> noexcept() has never been about "detecting constant expressions"!

James Kuyper

unread,
Mar 28, 2020, 3:54:54 PM3/28/20
to
A call to a constexpr function whose definition has been provided, has
defined behavior, and which meets the requirements for a constexpr
function (10.1.5) doesn't match any of the items in the list under
8.20p2 - as such, it can never fail to qualify as a constant expression,
regardless of which value is passed to the function.

Öö Tiib

unread,
Mar 28, 2020, 4:17:10 PM3/28/20
to
On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
>
> Until C++20, there was no way of detecting constant expressions. Now
> there is "is_constant_evaluated", constinit and consteval. To my mind,
> C++20 has significantly improved the scope for constant expressions and
> compile-time programming.

It does not work and so you are under some kind of illusion from
worm-tongue-wording of standard.

#include <iostream>
#include <type_traits>

constexpr int foo(int x)
{
if (std::is_constant_evaluated())
return x;
return 666;
}

int main()
{
std::cout << (foo(42) == 42 ? "constant expressions can be detected\n"
: "constant expressions can't be "
"detected\n");
}

Demo: <http://coliru.stacked-crooked.com/a/61f3d3e62656525e>

Öö Tiib

unread,
Mar 28, 2020, 5:13:08 PM3/28/20
to
Is it removed that when conditional expression evaluates into throw
expression then it is not constant expression?

James Kuyper

unread,
Mar 28, 2020, 6:25:27 PM3/28/20
to
In n3337.pdf dated 2012-01-16, section 5.19p2 includes "an invocation of
a constexpr function with arguments that, when substituted by function
invocation substitution (7.1.5), do not produce a constant expression;"
as one of the things that fails to qualify as a constant expression.

n3797.pdf, dated 2013-10-13, drops that item from the list.

So it would appear that the change you're complaining about took place
about 7-8 years ago.

Keith Thompson

unread,
Mar 28, 2020, 7:11:49 PM3/28/20
to
Drafts of the C++ standard are available in this git repo:

https://github.com/cplusplus/draft

The commit that removed that wording is:

commit 7cb8947bec0eaf7788e75e3b36e23f4714e157a8
Author: Richard Smith <ric...@metafoo.co.uk>
Date: 2012-05-05 19:59:36 -0700

N3652 Relaxing constraints on constexpr functions

Conflicts resolved as indicated by N3652.

N3652 is here: https://isocpp.org/files/papers/N3652.html

Relaxing constraints on constexpr functions

constexpr member functions and implicit const

This paper describes the subset of N3597 selected for inclusion in
C++14, relaxing a number of restrictions on constexpr
functions. These changes all received overwhelmingly strong or
unopposed support under review of the Evolution Working Group. It
also incorporates Option 2 of N3598.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips Healthcare
void Void(void) { Void(); } /* The recursive call of the void */

Öö Tiib

unread,
Mar 28, 2020, 8:29:53 PM3/28/20
to
I am really not complaining about that since my constexpr functions
don't throw ... it is just another inconvenience. Are these now
ill-formed constant expressions instead of being non-constant
expressions?

I am complaining that constant expression is not detectable with code.
So I must keep track if something evaluates to constant expression or
not as monkey-coder; I can't make code that does it automatically.

The requirement that was useful that noexcept was required to return
true to calls that were constant expressions was removed.
Things that I do not see much use for in their current form:
std::is_constant_evaluated, constinit and consteval were added.

David Brown

unread,
Mar 29, 2020, 4:24:47 AM3/29/20
to
As I said - noexcept() has never been about detecting constant expressions.

I understand that you think it's odd that a constant expression can
return "false" for noexcept. But that is beside the point.

You claimed the change to noexcept() broke the tool for detecting
constant expressions. I was hoping for a justification for that, rather
than another side-track.

David Brown

unread,
Mar 29, 2020, 4:31:29 AM3/29/20
to
On 28/03/2020 21:16, Öö Tiib wrote:
> On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
>>
>> Until C++20, there was no way of detecting constant expressions. Now
>> there is "is_constant_evaluated", constinit and consteval. To my mind,
>> C++20 has significantly improved the scope for constant expressions and
>> compile-time programming.
>
> It does not work and so you are under some kind of illusion from
> worm-tongue-wording of standard.

I haven't read the standard here yet.

But I must admit I assumed that std::is_constant_evaluated() was
basically a standardisation of the useful gcc extension
__builtin_constant_p(...). Unfortunately, it is not.

Öö Tiib

unread,
Mar 29, 2020, 4:47:15 AM3/29/20
to
On Sunday, 29 March 2020 11:24:47 UTC+3, David Brown wrote:
> On 28/03/2020 20:42, Öö Tiib wrote:
> > On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
> >>
> >> noexcept() has never been about "detecting constant expressions"!
> >
> > I already did cite relevant parts of C++14 where it was required and
> > C++17 where it was removed. These were 8 words "unless the call is a
> > constant expression". So if foo(43) is not constant expression but
> > foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
> > true.
> >
>
> As I said - noexcept() has never been about detecting constant expressions.

But C++ has never been what you said but it is always been what standard
said. And C++14 said that noexcept operator should detect constant
expressions:

The result of the noexcept operator is false if in a
potentially-evaluated context the expression would contain

- a potentially-evaluated call83 to a function, member function,
function pointer, or member function pointer that does not have
a non-throwing exception-specification ([except.spec]),
unless the call is a constant expression ([expr.const]),

> I understand that you think it's odd that a constant expression can
> return "false" for noexcept. But that is beside the point.

You misrepresent. I do think it's odd that such change was made
and hastily implemented in all compilers with retroactive effect in
C++11 and C++14 modes too. Despite in C++11 and C++14 the noexcept
operator was required to detect constant expressions.

> You claimed the change to noexcept() broke the tool for detecting
> constant expressions. I was hoping for a justification for that, rather
> than another side-track.

I do not even understand what you expect. Some kind of philosophical
debate? Facts are there. I do not know justifications to those facts.



Öö Tiib

unread,
Mar 29, 2020, 5:08:29 AM3/29/20
to
On Sunday, 29 March 2020 11:31:29 UTC+3, David Brown wrote:
> On 28/03/2020 21:16, Öö Tiib wrote:
> > On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
> >>
> >> Until C++20, there was no way of detecting constant expressions. Now
> >> there is "is_constant_evaluated", constinit and consteval. To my mind,
> >> C++20 has significantly improved the scope for constant expressions and
> >> compile-time programming.
> >
> > It does not work and so you are under some kind of illusion from
> > worm-tongue-wording of standard.
>
> I haven't read the standard here yet.
>
> But I must admit I assumed that std::is_constant_evaluated() was
> basically a standardisation of the useful gcc extension
> __builtin_constant_p(...). Unfortunately, it is not.

Also __builtin_constant_p does not work. Only noexcept did but stopped.
Linus Torvalds trying to figure fixes to __builtin_constant_p :
https://lkml.org/lkml/2018/3/17/184

David Brown

unread,
Mar 29, 2020, 5:39:01 AM3/29/20
to
On 29/03/2020 10:47, Öö Tiib wrote:
> On Sunday, 29 March 2020 11:24:47 UTC+3, David Brown wrote:
>> On 28/03/2020 20:42, Öö Tiib wrote:
>>> On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
>>>>
>>>> noexcept() has never been about "detecting constant expressions"!
>>>
>>> I already did cite relevant parts of C++14 where it was required and
>>> C++17 where it was removed. These were 8 words "unless the call is a
>>> constant expression". So if foo(43) is not constant expression but
>>> foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
>>> true.
>>>
>>
>> As I said - noexcept() has never been about detecting constant expressions.
>
> But C++ has never been what you said but it is always been what standard
> said. And C++14 said that noexcept operator should detect constant
> expressions:

No!

C++14 and earlier said constant expressions were /part/ of the logic in
determining the result of the noexcept operator. That does not mean, as
you have been claiming, that noexcept is a tool for determining if
something is a constant expression.

>
> The result of the noexcept operator is false if in a
> potentially-evaluated context the expression would contain
>
> - a potentially-evaluated call83 to a function, member function,
> function pointer, or member function pointer that does not have
> a non-throwing exception-specification ([except.spec]),
> unless the call is a constant expression ([expr.const]),
>
>> I understand that you think it's odd that a constant expression can
>> return "false" for noexcept. But that is beside the point.
>
> You misrepresent. I do think it's odd that such change was made
> and hastily implemented in all compilers with retroactive effect in
> C++11 and C++14 modes too. Despite in C++11 and C++14 the noexcept
> operator was required to detect constant expressions.
>

I guess the prevailing opinion (of those who know about these things,
unlike me) is that the earlier behaviour was a mistake.

>> You claimed the change to noexcept() broke the tool for detecting
>> constant expressions. I was hoping for a justification for that, rather
>> than another side-track.
>
> I do not even understand what you expect. Some kind of philosophical
> debate? Facts are there. I do not know justifications to those facts.
>

Neither the facts, the standards (earlier or later versions), nor what
you have now written support the idea that noexcept() was a way to
identify constant expressions, or that the changes broke such methods,
or that the changes were made maliciously, callously, or without careful
consideration.


David Brown

unread,
Mar 29, 2020, 5:48:51 AM3/29/20
to
What do you mean, "__builtin_constant_p does not work" ? It works
perfectly well, and I have used it - as have many people. It might not
do what you think in all cases, such as the one Torvalds is describing -
but see the follow-ups for an explanation of that.

Perhaps what you mean is "It does not work for my particular case, while
an abuse of noexcept() happened to work. But I won't tell anyone the
details."


Öö Tiib

unread,
Mar 29, 2020, 6:27:47 AM3/29/20
to
On Sunday, 29 March 2020 12:39:01 UTC+3, David Brown wrote:
> On 29/03/2020 10:47, Öö Tiib wrote:
> > On Sunday, 29 March 2020 11:24:47 UTC+3, David Brown wrote:
> >> On 28/03/2020 20:42, Öö Tiib wrote:
> >>> On Saturday, 28 March 2020 21:35:23 UTC+2, David Brown wrote:
> >>>>
> >>>> noexcept() has never been about "detecting constant expressions"!
> >>>
> >>> I already did cite relevant parts of C++14 where it was required and
> >>> C++17 where it was removed. These were 8 words "unless the call is a
> >>> constant expression". So if foo(43) is not constant expression but
> >>> foo(42) is then noexcept(foo(43)) should be false and noexcept(foo(42))
> >>> true.
> >>>
> >>
> >> As I said - noexcept() has never been about detecting constant expressions.
> >
> > But C++ has never been what you said but it is always been what standard
> > said. And C++14 said that noexcept operator should detect constant
> > expressions:
>
> No!
>
> C++14 and earlier said constant expressions were /part/ of the logic in
> determining the result of the noexcept operator. That does not mean, as
> you have been claiming, that noexcept is a tool for determining if
> something is a constant expression.

It was the only thing in standard that had requirement of detecting
if something is constant expression, makeing nothing ill-formed doing
so and returning constant expression itself. Nothing else (including
non-standard __builtin_constant_p) had those properties.
So it was useful as such tool but now it isn't.

> > The result of the noexcept operator is false if in a
> > potentially-evaluated context the expression would contain
> >
> > - a potentially-evaluated call83 to a function, member function,
> > function pointer, or member function pointer that does not have
> > a non-throwing exception-specification ([except.spec]),
> > unless the call is a constant expression ([expr.const]),
> >
> >> I understand that you think it's odd that a constant expression can
> >> return "false" for noexcept. But that is beside the point.
> >
> > You misrepresent. I do think it's odd that such change was made
> > and hastily implemented in all compilers with retroactive effect in
> > C++11 and C++14 modes too. Despite in C++11 and C++14 the noexcept
> > operator was required to detect constant expressions.
> >
>
> I guess the prevailing opinion (of those who know about these things,
> unlike me) is that the earlier behaviour was a mistake.

It is void argument to me. I have just my work to do and I
want to do it well. You post zero code how I should do it now,
just popular votes and alleged opinions of your "authorities".

>
> >> You claimed the change to noexcept() broke the tool for detecting
> >> constant expressions. I was hoping for a justification for that, rather
> >> than another side-track.
> >
> > I do not even understand what you expect. Some kind of philosophical
> > debate? Facts are there. I do not know justifications to those facts.
> >
>
> Neither the facts, the standards (earlier or later versions), nor what
> you have now written support the idea that noexcept() was a way to
> identify constant expressions, or that the changes broke such methods,
> or that the changes were made maliciously, callously, or without careful
> consideration.

I would probably be "fixed" too by now if I had direct evidence about
malice. Forces that can enforce retroactive "fixes" in all compilers
have enough resources to "fix" people too. But mine is just opinion
based on facts there are.

Öö Tiib

unread,
Mar 29, 2020, 6:34:00 AM3/29/20
to
You unlike me are posting zero code, quotes and/or cites. So I feel your
posture that I am withholding details insulting.

David Brown

unread,
Mar 29, 2020, 7:56:31 AM3/29/20
to
I am not posting code, because I don't have a problem with any code.
This whole discussion started with /your/ claim that C++20 breaks
volatile and makes it impossible to write low-level code that uses it.
What would you like me to post - code that has a volatile variable and
writes to it?

#define output_register *((volatile uint32_t *) 0x1234)

void set_output(uint32_t x) {
output_register = x;
}

That works fine in every C and C++ standard.

I don't have any sample code with noexcept, because I disable exceptions
in my embedded C++ code. (Yes, I know that is a non-conforming
extension in my compiler.)


/You/ are the one with complaints about the standards and how changes
break your code. /You/ are the one that said C++20 changes volatile and
breaks your code - the onus is on /you/ to post code that shows this
effect. /You/ are the one that said C++17 changes noexcept and breaks
your code (though I still haven't a clue how that relates to your
complain about "volatile").

I can't give any code snippets or references to problems with volatile
or noexcept, because I don't have any problems. I can't give any code
snippets or references regarding /your/ problems with volatile and
noexcept, because you won't say what they are or show examples. You
have given some references, but no indication as to why they are relevant.


If you want to warn people about standards changes that break real,
useful code, then post some examples so that we can learn.

If you want to get help making your code robust in the face of changes,
post some samples so that we can help. (I am not guaranteeing help, and
I am sure others would be more helpful than I could be - but at least
people can try.)

If you want to complain about the changes causing you trouble, post some
examples so that we can see the problem and sympathise, and possibly
make useful guesses as to the reason behind the changes. We all know
the C++ committee and compiler writers are mere humans and make mistakes.

If you want to convince people that you have misunderstood the changes,
or that you have been writing weird code that merely happens to work by
luck, then continue posting red-herrings, mixing up topics, claiming
malice and incompetence on the part of the committees and compiler
writers, and failing to answer direct questions or show samples. If
that's the way you want to play it, I am done here - I can't see any way
to make progress.

Öö Tiib

unread,
Mar 29, 2020, 8:30:56 AM3/29/20
to
So when I am posting code that stopped working for me then you reply
that it was weird trash anyway that merely worked by luck while it
was clearly mandated by C++14 standard to work. You do not post where
I supposedly misinterpreted standard nor advice how should I write
the code instead nor any explanations why it was retroactively
"fixed" in all compilers. Just insults. What "help" is that?

0 new messages