www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - scope(success) lowered to try-catch ?

reply Cauterite <cauterite gmail.com> writes:
Hello,
I'm not sure whether I'm missing something obvious here, but is 
there a reason for scope(success) being lowered to a try-catch 
statement?
I would have expected only scope(exit) and scope(failure) to 
actually interact with exception handling, while scope(success) 
simply places code on the path of normal control flow.

Example (windows x32):

---

// main.d
void main() {
	scope(success) {}
}

 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC --- Regardless of whether -betterC is used, you can see in the disassembly that having a scope(success) anywhere in the function causes the SEH prologue to be emitted in the code. Is there a reason scope(success) needs to set up for exception handling? Or is this a bug / potential enhancement ?
Jun 17 2018
next sibling parent reply Nicholas Wilson <iamthewilsonator hotmail.com> writes:
On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:
 Hello,
 I'm not sure whether I'm missing something obvious here, but is 
 there a reason for scope(success) being lowered to a try-catch 
 statement?
 I would have expected only scope(exit) and scope(failure) to 
 actually interact with exception handling, while scope(success) 
 simply places code on the path of normal control flow.

 Example (windows x32):

 ---

 // main.d
 void main() {
 	scope(success) {}
 }

 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC --- Regardless of whether -betterC is used, you can see in the disassembly that having a scope(success) anywhere in the function causes the SEH prologue to be emitted in the code. Is there a reason scope(success) needs to set up for exception handling? Or is this a bug / potential enhancement ?
I suspect scope(success) is lowered because scope(exit) and scope(failure) are, and that would result in a simpler (compiler) implementation of it. does adding nothrow to main fix it? For dcompute I specifically allow scope(exit|success) because there will never be any exceptions _at all_. If not, please do submit an issue. Also a better error message should be given.
Jun 17 2018
parent Cauterite <cauterite gmail.com> writes:
On Sunday, 17 June 2018 at 12:10:33 UTC, Nicholas Wilson wrote:
 I suspect scope(success) is lowered because scope(exit) and 
 scope(failure)
 are, and that would result in a simpler (compiler) 
 implementation of it.

 does adding nothrow to main fix it? For dcompute I specifically 
 allow scope(exit|success) because there will never be any 
 exceptions _at all_.

 If not, please do submit an issue. Also a better error message 
 should be given.
nothrow unfortunately doesn't help; i guess I'll file an issue. thanks for your input
Jun 17 2018
prev sibling next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/17/18 6:58 AM, Cauterite wrote:
 Hello,
 I'm not sure whether I'm missing something obvious here, but is there a 
 reason for scope(success) being lowered to a try-catch statement?
 I would have expected only scope(exit) and scope(failure) to actually 
 interact with exception handling, while scope(success) simply places 
 code on the path of normal control flow.
 
 Example (windows x32):
 
 ---
 
 // main.d
 void main() {
      scope(success) {}
 }
 
 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC --- Regardless of whether -betterC is used, you can see in the disassembly that having a scope(success) anywhere in the function causes the SEH prologue to be emitted in the code. Is there a reason scope(success) needs to set up for exception handling? Or is this a bug / potential enhancement ?
I think you are right, adding scope(success) should just add the statements to the end of the scope. Here's what I think happens: Because scope(anything) needs to put things like this: try { normal code scope(success) code } catch(Exception e) { scope(failure) code throw e; } finally { scope(exit) code } so any time you use a scope statement, it has to set up this framework so it can have the correct place to put things (there may be scope(failure) or scope(exit) code later). But I think we can fix this. -Steve
Jun 17 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:
 Hello,
 I'm not sure whether I'm missing something obvious here, but is 
 there a reason for scope(success) being lowered to a try-catch 
 statement?
 I would have expected only scope(exit) and scope(failure) to 
 actually interact with exception handling, while scope(success) 
 simply places code on the path of normal control flow.

 Example (windows x32):

 ---

 // main.d
 void main() {
 	scope(success) {}
 }

 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC --- Regardless of whether -betterC is used, you can see in the disassembly that having a scope(success) anywhere in the function causes the SEH prologue to be emitted in the code. Is there a reason scope(success) needs to set up for exception handling? Or is this a bug / potential enhancement ?
In Andrei's book 'The D Programming Language' the following is written: { <statement1> scope(success) <statement2> <statement3> } is lowered to { <statement1> bool __succeeded = true; try { <statement3> } catch(Exception e) { __succeeded = false; throw e; } finally { if (__succeeded) <statement2> // vice-versa for scope(failure): `if (!__succeeded) ...` } } If it weren't and it would simply be integrated one would have to write potentiallyThrowingFunc(); scope(success) {...}; I suppose? And this seems like breaking how scope works with failure and exit?!
Jun 17 2018
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/17/18 8:24 AM, Timoses wrote:
 On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:
 Hello,
 I'm not sure whether I'm missing something obvious here, but is there 
 a reason for scope(success) being lowered to a try-catch statement?
 I would have expected only scope(exit) and scope(failure) to actually 
 interact with exception handling, while scope(success) simply places 
 code on the path of normal control flow.

 Example (windows x32):

 ---

 // main.d
 void main() {
     scope(success) {}
 }

 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC --- Regardless of whether -betterC is used, you can see in the disassembly that having a scope(success) anywhere in the function causes the SEH prologue to be emitted in the code. Is there a reason scope(success) needs to set up for exception handling? Or is this a bug / potential enhancement ?
In Andrei's book 'The D Programming Language' the following is written: {     <statement1>     scope(success) <statement2>     <statement3> } is lowered to {     <statement1>     bool __succeeded = true;     try {         <statement3>     } catch(Exception e) {         __succeeded = false;         throw e;     } finally {         if (__succeeded) <statement2> // vice-versa for scope(failure): `if (!__succeeded) ...`     } } If it weren't and it would simply be integrated one would have to write     potentiallyThrowingFunc();     scope(success) {...}; I suppose? And this seems like breaking how scope works with failure and exit?
I think the request isn't to integrate the code immediately, but to do: { <statement 1> <statement 3> <statement 2> } and be just fine. I think the reason the scope(failure) is done that way is likely for proper exception chaining in case one of the scope statements throws. -Steve
Jun 17 2018
prev sibling next sibling parent Mike Franklin <slavo5150 yahoo.com> writes:
On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:

 ---

 // main.d
 void main() {
 	scope(success) {}
 }

 dmd -betterC main.d
Error: Cannot use try-catch statements with -betterC ---
You can see what the compiler is doing at https://run.dlang.io/is/5BZOQV and clicking on the "AST" button. It produces the following: import object; void main() { bool __os2 = false; try { } catch(Throwable __o3) { __os2 = true; throw __o3; } if (!__os2) { } return 0; } The compiler could probably lower that to something more intelligent so it could be used in -betterC. I rule it a bug. Mike
Jun 17 2018
prev sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:
 Is there a reason scope(success) needs to set up for exception 
 handling?
 Or is this a bug / potential enhancement ?
If you had no exception handling in place, you'd need to duplicate code in the output. For instance: void foo() { scope(success) writeln("success!"); if (a) return; if (b) return; throw new Exception; } This would have to be lowered to: void foo() { if (a) { writeln("success!"); return; } if (b) { writeln("success!"); return; } throw new Exception; writeln("success!"); // maybe omitted with flow analysis } Now imagine there were 20 places you return from the function early. Now imagine this is in a loop body, where you can leave it via goto, break, continue, return, or end-of-block. And wrapped in several if statements. You generate smaller code with the exception handling system. The compiler only has to pay attention to scope guards in the code that handles it directly, instead of at every flow control statement. Add to that the fact that -betterC is pretty recent and scope guards are more than ten years old, and you get this hole in the compiler.
Jun 17 2018
next sibling parent Cauterite <cauterite gmail.com> writes:
On Monday, 18 June 2018 at 03:58:47 UTC, Neia Neutuladh wrote:
 ...
yeah, at an AST level it makes sense why it was implemented like this. it's unfortunate that there's no straightforward way to express 'finally(success) {'.
Jun 18 2018
prev sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 6/17/18 11:58 PM, Neia Neutuladh wrote:
 On Sunday, 17 June 2018 at 10:58:29 UTC, Cauterite wrote:
 Is there a reason scope(success) needs to set up for exception handling?
 Or is this a bug / potential enhancement ?
If you had no exception handling in place, you'd need to duplicate code in the output. For instance: void foo() {   scope(success) writeln("success!");   if (a) return;   if (b) return;   throw new Exception; } This would have to be lowered to: void foo() {   if (a) { writeln("success!"); return; }   if (b) { writeln("success!"); return; }   throw new Exception;   writeln("success!");  // maybe omitted with flow analysis }
Yep, it's a good point. But also not the only way to do this. If you are returning void, just a goto would work: void foo() { if(a) { goto L1; } if(b) { goto L1; } throw new Exception; L1: writeln("success1"); } If you are returning a value, then you can establish a local variable with the return value, and use a goto that way. It's already doing this with "did it succeed", so adding another local variable is pretty trivial. Bottom line is, the compiler understands flow control and can insert structures like this without a huge impact. I also think it's possible for the compiler to detect that a try/catch clause is trivially omitted if we do it in the right way.
 
 Now imagine there were 20 places you return from the function early. Now 
 imagine this is in a loop body, where you can leave it via goto, break, 
 continue, return, or end-of-block. And wrapped in several if statements.
These are all pretty easy to deal with. After all, they are essentially glorified gotos.
 
 You generate smaller code with the exception handling system. The 
 compiler only has to pay attention to scope guards in the code that 
 handles it directly, instead of at every flow control statement. Add to 
 that the fact that -betterC is pretty recent and scope guards are more 
 than ten years old, and you get this hole in the compiler.
I think the last point here is exactly why it was done this way -- the original design of the compiler was to expect there were always exception handling, so why not use it? -Steve
Jun 18 2018
parent aliak <something something.com> writes:
On Monday, 18 June 2018 at 12:48:46 UTC, Steven Schveighoffer 
wrote:
 On 6/17/18 11:58 PM, Neia Neutuladh wrote:
 [...]
Yep, it's a good point. But also not the only way to do this. If you are returning void, just a goto would work: [...]
I'm quite a noob when it comes to compiler stuff, and I see how this can be optimized when there're no exceptions, but I wonder what scope(success) actually means though without exceptions in play. It's just scope(exit) then right or? Cheers, - Ali
Jun 18 2018