www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Can anyone explain this?

reply Shachar Shemesh <shachar weka.io> writes:
I set up to find out what happens if the assert string throws. I have to 
admit I did not expect the result:

$ cat test.d
import std.stdio;
import core.exception;

void main() {
     scope(failure) {
         writeln("Not gonna happen");
     }

     try {
         static string throwingFunc() {
             throw new Exception("An exception");
         }
         assert(0==1, throwingFunc());
     } catch(Exception ex) {
         writeln("Exception");
     } catch(AssertError ex) {
         writeln("Assert");
     }
}

$ ldc2 --version
LDC - the LLVM D compiler (1.8.0):
   based on DMD v2.078.3 and LLVM 5.0.1
...

$ ./test
Not gonna happen
object.Exception test.d(11): An exception
----------------
??:? [0x3728941e]
??:? [0x372903aa]
??:? [0x3727b15c]
??:? [0x3724991d]
??:? [0x372496c9]
??:? [0x3727aecf]
??:? [0x3727addb]
??:? [0x3724a124]
??:? __libc_start_main [0xed8b01c0]
??:? [0x372495c9]

$ dmd --version
DMD64 D Compiler v2.080.0
Copyright (C) 1999-2018 by The D Language Foundation, All Rights 
Reserved written by Walter Bright

$ ./test
Not gonna happen
object.Exception test.d(11): An exception
----------------
??:? pure  safe immutable(char)[] test.main().throwingFunc() [0xe9b1c2b3]
??:? _Dmain [0xe9b1c1ad]
Jun 05 2018
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 5 June 2018 at 08:12:54 UTC, Shachar Shemesh wrote:
 I set up to find out what happens if the assert string throws. 
 I have to admit I did not expect the result:

 $ cat test.d
 import std.stdio;
 import core.exception;

 void main() {
     scope(failure) {
         writeln("Not gonna happen");
     }

     try {
         static string throwingFunc() {
             throw new Exception("An exception");
         }
         assert(0==1, throwingFunc());
     } catch(Exception ex) {
         writeln("Exception");
     } catch(AssertError ex) {
         writeln("Assert");
     }
 }

 $ ldc2 --version
 LDC - the LLVM D compiler (1.8.0):
   based on DMD v2.078.3 and LLVM 5.0.1
 ...

 $ ./test
 Not gonna happen
 object.Exception test.d(11): An exception
applying a bit of lowering int Dmain (int arc, const char** argv) { try return main(); catch (...) dumpThrowable(); // 3 dumps exception } void main() { try { try { static string throwingFunc() { throw new Exception("An exception"); } __assert_fail(throwingFunc(),...); // 1 throws before assert throws } catch(Exception ex) { writeln("Exception"); // Why is this not caught? I've no idea } catch(AssertError ex) { writeln("Assert"); // this is not caught because the thrown throwable is not an exception } } catch(...) { writeln("Not gonna happen"); // 2 scope failure is run throw; } }
Jun 05 2018
next sibling parent Shachar Shemesh <shachar weka.io> writes:
On 05/06/18 11:26, Nicholas Wilson wrote:
                 writeln("Exception"); // Why is this not caught?
I've no 
 idea
That's the part I was referring to.
Jun 05 2018
prev sibling parent reply Simen =?UTF-8?B?S2rDpnLDpXM=?= <simen.kjaras gmail.com> writes:
On Tuesday, 5 June 2018 at 08:26:22 UTC, Nicholas Wilson wrote:
 writeln("Assert"); // this is not caught because the thrown 
 throwable is not an exception
Now that's plain false - If you replace the call to throwingFunc() with a string literal, you'll see it's properly caught. Also, the catch clause explicitly specifies AssertError, which as the name implies, is an Error, not an Exception. This might just be a case of undefined behavior. According to https://dlang.org/spec/contracts.html: "As a contract, an assert represents a guarantee that the code must uphold. Any failure of this expression represents a logic error in the code that must be fixed in the source code. A program for which the assert contract is false is, by definition, invalid, and therefore has undefined behaviour." In short, what you're doing is UB, and the nasal demons are printing 'not gonna happen' to your console. That also makes some sense with Andrea Fontana's comments:
 - Do not have side effects in either AssignExpression that 
 subsequent code depends on.
 - Do not attempt to resume normal execution after an Assert 
 Failure.
However, if we add catch (Throwable ex) to the list of catch statements, that *does* catch a regular object.Exception. Moving the catch (Exception) statement down the list does nothing. Adding another layer of try-catch also catches it (even with just catch (Exception ex)): import std.stdio; import core.exception; unittest { scope(failure) { writeln("Not gonna happen"); } try { try { static string throwingFunc() { throw new Exception("An exception"); } assert(0==1, throwingFunc()); } catch(AssertError ex) { writeln("Assert"); } catch(Exception ex) { writeln("Exception A"); } } catch(Exception ex) { writeln("Exception B"); } } So I'm stumped. It seems the exception handling is simply not setup correctly. This could be because of UB I guess, but I'd hope the AssertError would be thrown first in such a case. -- Simen
Jun 05 2018
parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 5 June 2018 at 09:03:03 UTC, Simen Kjærås wrote:
 On Tuesday, 5 June 2018 at 08:26:22 UTC, Nicholas Wilson wrote:
 writeln("Assert"); // this is not caught because the thrown 
 throwable is not an exception
Now that's plain false - If you replace the call to throwingFunc() with a string literal, you'll see it's properly caught. Also, the catch clause explicitly specifies AssertError, which as the name implies, is an Error, not an Exception.
Whoops I meant _is_.
 However, if we add catch (Throwable ex) to the list of catch 
 statements, that *does* catch a regular object.Exception. 
 Moving the catch (Exception) statement down the list does 
 nothing. Adding another layer of try-catch also catches it 
 (even with just catch (Exception ex)):
That does seem odd but is consistent with the implicit "try" surrounding Dmain inserted by the runtime.
 import std.stdio;
 import core.exception;

 unittest {
     scope(failure) {
         writeln("Not gonna happen");
     }

     try {
         try {
             static string throwingFunc() {
                 throw new Exception("An exception");
             }
             assert(0==1, throwingFunc());
         } catch(AssertError ex) {
             writeln("Assert");
         } catch(Exception ex) {
             writeln("Exception A");
         }
     } catch(Exception ex) {
         writeln("Exception B");
     }
 }

 So I'm stumped. It seems the exception handling is simply not 
 setup correctly. This could be because of UB I guess, but I'd 
 hope the AssertError would be thrown first in such a case.
The assert error will never be thrown because one of the parameters to the __assert_fail throws. Its like as if you had written. if (0==1) { string tmp = throwingFunc(); // throws __assert_fail(tmp,__LINE__,...); // dead code. } Actually it may be the compiler doing something funky with assert(0, msg); being special (assert(0,...) since the spec says for assert any compile time expression == 0 has the effect of assert(0). i.e. assert(0==1); is the same as assert(0);). Proof import std.stdio; import core.exception; bool foo() { return false;} // mess with the compiler's reasoning about the truthiness of the assert void main() { scope(failure) { writeln("Not gonna happen"); } try { static string throwingFunc() { throw new Exception("An exception"); } assert(foo(), throwingFunc()); } catch(Exception ex) { writeln("Exception"); } catch(AssertError ex) { writeln("Assert"); } } prints Exception
Jun 05 2018
parent Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Tuesday, 5 June 2018 at 09:58:43 UTC, Nicholas Wilson wrote:
 prints

 Exception
https://issues.dlang.org/show_bug.cgi?id=18946
Jun 05 2018
prev sibling parent Andrea Fontana <nospam example.com> writes:
On Tuesday, 5 June 2018 at 08:12:54 UTC, Shachar Shemesh wrote:
 I set up to find out what happens if the assert string throws.
From here: https://dlang.org/spec/expression.html#assert_expressions [...] 8. Undefined Behavior: Once in an Invalid State the behavior of the continuing execution of the program is undefined. [...] [...] Best Practices: - Do not have side effects in either AssignExpression that subsequent code depends on. - AssertExpressions are intended to detect bugs in the program, do not use for detecting input or environmental errors. - Do not attempt to resume normal execution after an Assert Failure. [...] Anyway you can try to catch "Throwable" to understand what is going to happen (but it's not a good idea at all to keep it in a program) Andrea
Jun 05 2018