1

According to intro.abstract#1:

... conforming implementations are required to emulate (only) the observable behavior of the abstract machine as explained below.

This explanation is the as-if rule, which contains the following example:

... an actual implementation need not evaluate part of an expression if it can deduce that its value is not used and that no side effects affecting the observable behavior of the program are produced.

The definition of a side-effect is in intro.execution#7:

Reading an object designated by a volatile glvalue ([basic.lval]), modifying an object, calling a library I/O function, or calling a function that does any of those operations are all side effects, which are changes in the state of the execution environment. ...

It seems to me that in the following program:

int main() 
{
  throw 42;
}

The value of the expression throw 42; is not used, and it doesn't satisfy any of the criteria of being a side-effect.

Does that mean the implementation is allowed to not evaluate this expression? Is the above program equivalent to:

int main() {}

as far as the abstract machine is considered? I can't find any text that says the abstract machine knows or cares about exceptions.

cigien
  • 50,328
  • 7
  • 37
  • 78
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/222100/discussion-on-question-by-cigien-does-a-throw-exception-have-to-be-evaluated). – Bhargav Rao Sep 26 '20 at 04:39

2 Answers2

4

The (edited) question boils down to whether the compiler is allowed to "optimize" int main() { throw 42; } to just int main() { }. I believe the answer is negative in this case, because the thrown exception causes a change in the observable behavior of the program, which the compiler is required to emulate.

Since there is no explicit handler defined, throw 42; is supposed to invoke std::terminate() which, in turn, ends up calling std::abort(), where "an implementation defined status is returned to the host environment that indicates unsuccessful execution". In other words, the exit code of the program would be different with vs. without the exception being thrown, and therefore the throw cannot be ellided.

(This leaves out the case of freestanding implementations where there is no "host" to return to, but I don't think the compiler is allowed to make assumptions about the environment where it will run.)

In the more general case when there is an explicit matching exception handler defined in the user code, it becomes fuzzier. Throwing the exception transfers control to that handler, but if the compiler can establish that the handler causes no changes in the observable behavior or other side-effects, then the whole throw may be optimized out. That could happen in cases where the handler does not access any volatile data, does not modify any object, does not call any library I/O functions, and performs a program exit equivalent to the normal one.

dxiv
  • 15,185
  • 2
  • 21
  • 41
  • "*This leaves out the case of freestanding implementations ... but I don't think the compiler is allowed to make assumptions about the environment where it will run.*" If an implementation could legally not evaluate code, that sounds to me like the abstract machine doesn't say what will happen. – cigien Sep 24 '20 at 19:26
  • @cigien Between [the standard](http://eel.is/c++draft/basic.start.main) and [this comment](https://stackoverflow.com/questions/204476/what-should-main-return-in-c-and-c#comment55278220_31263079) it sounds like if the entry point is `main` then it must return an `int` and the compiler cannot change the return value. However, a freestanding implementation could define an entirely different entry point e.g. `void alt_main();` where there is no return value at all, thus nothing to observe. That said, it's a fine point for the real language lawyers to weigh on. – dxiv Sep 24 '20 at 19:49
  • Yes, it is a fine point. I suspect this might even be under-specified :p Thanks for making a clear answer, that takes a stand. +1 – cigien Sep 24 '20 at 19:52
  • "_therefore the throw cannot be ellided_" so it can't be "elided" (replaced w/ NOP) but it can be replaced w/ `abort` – curiousguy Sep 24 '20 at 20:21
  • @curiousguy It could, as long as the compiler determines that they are equivalent in the given case. – dxiv Sep 24 '20 at 21:11
  • @cigien Thanks, and I'll leave here one more relevant link for [Is exit status observable behavior?](https://stackoverflow.com/questions/53815997/is-exit-status-observable-behavior), which I sort of assumed all along, but then realized that even that is not as clear cut as I thought it must be. Of course, one would not expect the compiler to be allowed to rewrite `int main() { return 0; }` as `return 101;`, but the reasons why not seem to be buried quite deeply in the standardeze. – dxiv Sep 24 '20 at 22:44
  • Nice A re: uncaught exceptions, but no discussion of "as-if rule" specifically. Mine covers "as-if" but not exception. I still don't know whether the Q is about one or the other. – curiousguy Sep 30 '20 at 00:42
  • 3
    @curiousguy In my reading, the question *is* about "*the expression `throw 42;`*" with no handler defined i.e. an uncaught exception. And observable behavior is just another way to say as-if. – dxiv Sep 30 '20 at 01:10
  • @dxiv I think the Q is making up an issue that doesn't exist at all, because of focus on "as-if" instead of "what is the effect of throwing w/o an handler in the call stack". – curiousguy Sep 30 '20 at 02:12
-4

Once again, I'm proven correct about the "as if rule": there is no clause more widely misunderstood in either C or C++. It isn't even one "rule". It's a reminder of the essence of the description of the programming language standard.

You can only apply the so called "rule" to a program; there is no as if rule for statements or expressions. Since you don't have a program that is well formed and can thus be run, there is no so-called rule to apply in your question.

If you have a program that can run, the "rule" just says that conformance is defined by measuring the program I/O (including volatile access, which is a kind of I/O just like stdio, and returning from main/calling exit).

Summary: your question as such is meaningless and you should forget there is something called "as if rule", because it can't be made into a distinct, removable rule.

(That answer applies to C, C++, Java and pretty much anything that is "high level".)

NOTE

The value of the expression throw 42; is not used, and it doesn't satisfy any of the criteria of being a side-effect.

Because a throw expression throws an exception, it doesn't result in a "value" like the 42 part. It has an effect: its throws the exception, to stop normal control flow and jump to an exception handler or exit the program completely if none is present.

In term of "abstract machine", throwing certainly does something: it stops the usual execution in the abstract machine. In the abstract machine, you can see the effect of a throw.

Asking about "abstract machine" seems to contradict your whole point as any computation is visible in the abstract machine, but the abstract machine is not visible.

Anyway, all these dissertations only prove my point that "abstract machine", "as if rule"... are noxious concepts that don't help anyone.

Just consider program semantics, which is always defined in term of I/O in all high level languages. Forget the rest!

curiousguy
  • 7,344
  • 2
  • 37
  • 52
  • 1
    Thanks for the answer. The comments about the *as-if* rule are helpful. It doesn't really address my question, so I wrapped the expression into a program. Apologies for changing (slightly) the question after you answered. – cigien Sep 24 '20 at 17:09
  • How is my answer not applicable to your original question? What was unclear or not complete? – curiousguy Sep 24 '20 at 17:45
  • 4
    Ok, I don't think your answer is unclear. But your original answer boils down to "*"abstract machine", "as if rule"... are noxious concepts that don't help anyone.*", which you made explicit in the edit. Apart from the fact that I disagree, I don't think this answer is useful for a language-lawyer question asking about the precise definitions and meaning of those terms, and how they apply to a particular program. – cigien Sep 24 '20 at 18:04
  • @cigien These terms have no "precise definition" because they have no use what so ever in language semantics. They accomplish nothing except as an explanation device that actually confuses ppl (like you). The abstract machine is how the effect of computations are described. The whole std describe the abstract machine. But it's abstract and using `ptrace` on a real program may not always show the same computations as in the abstract machine. **All it says is that tracing through a debugger may not work as expected.** "_the fact that I disagree_" If you do, can you explain how they are useful? – curiousguy Sep 24 '20 at 18:10