www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is this a bug? +goto

reply MatheusBN <m gmail.com> writes:
Hi,

I posted this in another thread but without any response.

This code:

void main(){
      goto Q;
      int x;
      Q:
      writeln("a");
}

Gives me this error: "source_file.d(4): Error: goto skips 
declaration of variable source.main.x at source_file.d(5)"


Now, if I add a pair of brackets:

void main(){
      {
          goto Q;
          int x;
      }
      Q:
      writeln("a");
}

It works. So Is this a bug?

MatheusBN.
Nov 05 2018
next sibling parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Monday, 5 November 2018 at 23:54:59 UTC, MatheusBN wrote:
 Hi,

 I posted this in another thread but without any response.

 This code:

 void main(){
      goto Q;
      int x;
      Q:
      writeln("a");
 }

 Gives me this error: "source_file.d(4): Error: goto skips 
 declaration of variable source.main.x at source_file.d(5)"


 Now, if I add a pair of brackets:

 void main(){
      {
          goto Q;
          int x;
      }
      Q:
      writeln("a");
 }

 It works. So Is this a bug?
No, it's not. Consider replacing that int with a type that has a destructor: struct S { ~this() { /* ... */ } } void main(){ goto Q; S x; Q: writeln("a"); } // <--- Now, what should happen at that closing paren is a destructor call, x.__dtor. However, goto jumps over initialization of 'x', which would lead to calling a destructor on an uninitialized value. That's why the compiler disallows such skips. But here it's fine: void main(){ { goto Q; S x; } // <--- Q: writeln("a"); } because goto jumps over both initialization *and* destruction, i.e. neither would even be performed. I'm guessing you misunderstood the author of that other thread. What he's saying is that code similar to the *second* version fails. That's what all the commotion is about over there. This simple example obviously works, yet in his more complicated code base something goes wrong.
Nov 05 2018
parent reply MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 00:13:52 UTC, Stanislav Blinov 
wrote:
 But here it's fine:

 void main(){
      {
          goto Q;
          S x;
      } // <---
      Q:
      writeln("a");
 }

 because goto jumps over both initialization *and* destruction, 
 i.e. neither would even be performed.
I see but at same time I found a bit confusing, because in this case we're just adding a new scope to fix the issue, and like I said to Jonathan, I thought that "x" wouldn't be initialized since it is never used. Thanks, MatheusBN.
Nov 05 2018
parent reply Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Tuesday, 6 November 2018 at 00:38:01 UTC, MatheusBN wrote:
 On Tuesday, 6 November 2018 at 00:13:52 UTC, Stanislav Blinov 
 wrote:
 But here it's fine:

 void main(){
      {
          goto Q;
          S x;
      } // <---
      Q:
      writeln("a");
 }

 because goto jumps over both initialization *and* destruction, 
 i.e. neither would even be performed.
I see but at same time I found a bit confusing, because in this case we're just adding a new scope to fix the issue, and like I said to Jonathan, I thought that "x" wouldn't be initialized since it is never used.
It's not as simple as that, that's why I specifically showed the destructor case. Even if you don't see any explicit use, it doesn't mean the compiler doesn't see an implicit one.
Nov 05 2018
parent MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 01:04:46 UTC, Stanislav Blinov 
wrote:
 ...Even if you don't see any explicit use, it doesn't mean the 
 compiler doesn't see an implicit one.
Sorry I don't think that I follow that. How a compiler could see an use when it's not being used/invoked on a program like in that snipped? MatheusBN.
Nov 05 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 5, 2018 4:54:59 PM MST MatheusBN via Digitalmars-d-learn 
wrote:
 Hi,

 I posted this in another thread but without any response.

 This code:

 void main(){
       goto Q;
       int x;
       Q:
       writeln("a");
 }

 Gives me this error: "source_file.d(4): Error: goto skips
 declaration of variable source.main.x at source_file.d(5)"


 Now, if I add a pair of brackets:

 void main(){
       {
           goto Q;
           int x;
       }
       Q:
       writeln("a");
 }

 It works. So Is this a bug?
All the spec says on the matter is that "It is illegal for a GotoStatement to be used to skip initializations." https://dlang.org/spec/statement.html#goto-statement In the first case, x exists at the label Q, and its initialization was skipped, so it's clearly illegal. However, in the second case, because of the braces, x does _not_ exist at the label Q, so its initialization was not skipped, so I don't see why it wouldn't be legal based on what the spec says, and I don't see any reason to make it illegal. Conceptually, it's doing exactly what you'd get with a break if the braces were for a loop. However, it is true that the spec could (and probably should) be more specific on the matter. - Jonathan M Davis
Nov 05 2018
parent reply MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 00:14:26 UTC, Jonathan M Davis 
wrote:
 On Monday, November 5, 2018 4:54:59 PM MST MatheusBN via 
 Digitalmars-d-learn wrote:
 Hi,

 I posted this in another thread but without any response.

 This code:

 void main(){
       goto Q;
       int x;
       Q:
       writeln("a");
 }

 Gives me this error: "source_file.d(4): Error: goto skips
 declaration of variable source.main.x at source_file.d(5)"


 Now, if I add a pair of brackets:

 void main(){
       {
           goto Q;
           int x;
       }
       Q:
       writeln("a");
 }

 It works. So Is this a bug?
All the spec says on the matter is that "It is illegal for a GotoStatement to be used to skip initializations." https://dlang.org/spec/statement.html#goto-statement In the first case, x exists at the label Q, and its initialization was skipped, so it's clearly illegal. However, in the second case, because of the braces, x does _not_ exist
Just to be clear, when you say "x exists at the label Q", you mean at the same scope, right? That's interesting but a bit confusing isn't? And I found a bit strange that in such code, since "x" is never used, why it isn't skipped. I know it's another language but in C at least in GCC there is no error over such code, so that's my confusion. Thanks, MatheusBN.
Nov 05 2018
next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Tue, 06 Nov 2018 00:33:56 +0000, MatheusBN wrote:
 Just to be clear, when you say "x exists at the label Q", you mean at
 the same scope, right?
The same or an outer scope. It's also invalid to write: goto Y; { int x; { Y: } }
 That's interesting but a bit confusing isn't?
 
 And I found a bit strange that in such code, since "x" is never used,
 why it isn't skipped.
Because simple rules are usually easier to understand and implement.
 I know it's another language but in C at least in GCC there is no error
 over such code, so that's my confusion.
Because C is a horribly unsafe language, far beyond necessary to have a low-level systems language. In C++, if you skip over `int i = 10;` it's an error, but not if you skip over `int i;`. Similarly, if you skip over a class variable declaration without an explicit initialization expression, if the class has a constructor or destructor, it's an error. In D, every variable of every type is initialized unless you opt out. The compiler *could* let you skip over declarations that are void-initialized, but there isn't a huge reason to do so.
Nov 05 2018
parent MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 01:05:04 UTC, Neia Neutuladh wrote:
 In C++, if you skip over `int i = 10;` it's an error, but not 
 if you skip over `int i;`.
In fact I agree with that rule more than the D one to be honest. Since It isn't initialized and never used, I think a warning should be enough instead of an error. On the other hand, if there is a possibility that the variable can be accessed, then an error should be throw. Like: { goto Q: int x; Q: x = 10; // An error is ok. } But here: { goto Q: int x; // An warning should be enough in my IMHO. Q: return; } MatheusBN.
Nov 05 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 5, 2018 5:33:56 PM MST MatheusBN via Digitalmars-d-learn 
wrote:
 On Tuesday, 6 November 2018 at 00:14:26 UTC, Jonathan M Davis

 wrote:
 On Monday, November 5, 2018 4:54:59 PM MST MatheusBN via

 Digitalmars-d-learn wrote:
 Hi,

 I posted this in another thread but without any response.

 This code:

 void main(){

       goto Q;
       int x;
       Q:
       writeln("a");

 }

 Gives me this error: "source_file.d(4): Error: goto skips
 declaration of variable source.main.x at source_file.d(5)"


 Now, if I add a pair of brackets:

 void main(){

       {

           goto Q;
           int x;

       }
       Q:
       writeln("a");

 }

 It works. So Is this a bug?
All the spec says on the matter is that "It is illegal for a GotoStatement to be used to skip initializations." https://dlang.org/spec/statement.html#goto-statement In the first case, x exists at the label Q, and its initialization was skipped, so it's clearly illegal. However, in the second case, because of the braces, x does _not_ exist
Just to be clear, when you say "x exists at the label Q", you mean at the same scope, right?
The scope that x was at is over at the label Q. So, x doesn't exist at the label Q. It has no address at that point. It's not on the stack. It doesn't exist in any sense other than the fact that it happens to be in the source code above it. In fact, the line with x never even ran, so x _never_ existed.
 That's interesting but a bit confusing isn't?
I don't see why. { goto Q; int x; } Q: is basically the same thing as while(1) { break; int x; } The same thing happens in both cases.
 And I found a bit strange that in such code, since "x" is never
 used, why it isn't skipped.
It's skipped right over. The goto jumps out of the scope, and the line with int x; is never run. In fact, if you compile with -w or -wi, the compiler will give you a warning about unreachable code.
 I know it's another language but in C at least in GCC there is no
 error over such code, so that's my confusion.
C is a different language, and it's one that generally doesn't care much about safety. It allows all kinds of horrible things that cause bugs. The folks behind D (and the folks behind _most_ languages since C/C++) tend to prefer a greater degree of safety than C provides. - Jonathan M Davis
Nov 05 2018
parent reply MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 01:55:04 UTC, Jonathan M Davis 
wrote:
 And I found a bit strange that in such code, since "x" is 
 never used, why it isn't skipped.
It's skipped right over. The goto jumps out of the scope, and the line with int x; is never run. In fact, if you compile with -w or -wi, the compiler will give you a warning about unreachable code.
That is exactly my point. Since "x" it's skipped and never used, it shouldn't just be a warning (unreachable code) instead of an error? I'm trying to understand why/when such code could give any problem. On the other hand if the code were: { goto Q: int x; Q: x = 10; // <- Now you are accessing an uninitialized variable. } Then I think an error would be ok. MatheusBN.
Nov 05 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, November 5, 2018 7:55:46 PM MST MatheusBN via Digitalmars-d-learn 
wrote:
 On Tuesday, 6 November 2018 at 01:55:04 UTC, Jonathan M Davis

 wrote:
 And I found a bit strange that in such code, since "x" is
 never used, why it isn't skipped.
It's skipped right over. The goto jumps out of the scope, and the line with int x; is never run. In fact, if you compile with -w or -wi, the compiler will give you a warning about unreachable code.
That is exactly my point. Since "x" it's skipped and never used, it shouldn't just be a warning (unreachable code) instead of an error? I'm trying to understand why/when such code could give any problem. On the other hand if the code were: { goto Q: int x; Q: x = 10; // <- Now you are accessing an uninitialized variable. } Then I think an error would be ok.
D tries to _very_ little with code flow analysis, because once you start having to do much with it, odds are that the compiler implementation is going to get it wrong. As such, any feature that involves code flow analysis in D tends to be _very_ simple. So, D avoids the issue here by saying that you cannot skip the initialization of a variable with goto. The compiler is not going to do the complicated logic of keeping track of where you access the variable in relation to the goto. That's exactly the sort of thing that might be obvious in the simple case but is highly likely to be buggy in more complex code. Code such as { goto Q; int x; } Q: or { if(foo) goto Q; int x; } Q: is fine, because the compiler can trivially see that it is impossible for x to be used after it's been skipped, whereas with something like goto Q; int x; Q: the compiler has to do much more complicated analysis of what the code is doing in order to determine that, and when the code isn't trivial, that can get _really_ complicated. You could argue that it would be nicer if the language required that the compiler be smarter about it, but by having the compiler be stupid, it reduces the risk of compiler bugs, and most people would consider code doing much with gotos like this to be poor code anyway. Most of the cases where goto is reasonable tend to be using goto from inside braces already, because it tends to be used as a way to more efficiently exit deeply nested code. And with D's labeled break and continue, the need for using goto outside of switch statements also tends to be lower than it is in C/C++. - Jonathan M Davis
Nov 05 2018
parent reply MatheusBN <m gmail.com> writes:
On Tuesday, 6 November 2018 at 05:46:40 UTC, Jonathan M Davis 
wrote:
 On Monday, November 5, 2018 7:55:46 PM MST MatheusBN via 
 Digitalmars-d-learn wrote:
 On Tuesday, 6 November 2018 at 01:55:04 UTC, Jonathan M Davis

 wrote:
 And I found a bit strange that in such code, since "x" is 
 never used, why it isn't skipped.
It's skipped right over. The goto jumps out of the scope, and the line with int x; is never run. In fact, if you compile with -w or -wi, the compiler will give you a warning about unreachable code.
That is exactly my point. Since "x" it's skipped and never used, it shouldn't just be a warning (unreachable code) instead of an error? I'm trying to understand why/when such code could give any problem. On the other hand if the code were: { goto Q: int x; Q: x = 10; // <- Now you are accessing an uninitialized variable. } Then I think an error would be ok.
D tries to _very_ little with code flow analysis, because once you start having to do much with it, odds are that the compiler implementation is going to get it wrong. As such, any feature that involves code flow analysis in D tends to be _very_ simple. So, D avoids the issue here by saying that you cannot skip the initialization of a variable with goto. The compiler is not going to do the complicated logic of keeping track of where you access the variable in relation to the goto. That's exactly the sort of thing that might be obvious in the simple case but is highly likely to be buggy in more complex code. Code such as { goto Q; int x; } Q: or { if(foo) goto Q; int x; } Q: is fine, because the compiler can trivially see that it is impossible for x to be used after it's been skipped, whereas with something like goto Q; int x; Q: the compiler has to do much more complicated analysis of what the code is doing in order to determine that, and when the code isn't trivial, that can get _really_ complicated. You could argue that it would be nicer if the language required that the compiler be smarter about it, but by having the compiler be stupid, it reduces the risk of compiler bugs, and most people would consider code doing much with gotos like this to be poor code anyway. Most of the cases where goto is reasonable tend to be using goto from inside braces already, because it tends to be used as a way to more efficiently exit deeply nested code. And with D's labeled break and continue, the need for using goto outside of switch statements also tends to be lower than it is in C/C++. - Jonathan M Davis
It's clear now about this decision and by the way thanks for replying all my doubts. MatheusBN.
Nov 06 2018
parent reply Michelle Long <HappyDance321 gmail.com> writes:
On Tuesday, 6 November 2018 at 13:53:41 UTC, MatheusBN wrote:
 On Tuesday, 6 November 2018 at 05:46:40 UTC, Jonathan M Davis 
 wrote:
 On Monday, November 5, 2018 7:55:46 PM MST MatheusBN via 
 Digitalmars-d-learn wrote:
 On Tuesday, 6 November 2018 at 01:55:04 UTC, Jonathan M Davis

 wrote:
 And I found a bit strange that in such code, since "x" is 
 never used, why it isn't skipped.
It's skipped right over. The goto jumps out of the scope, and the line with int x; is never run. In fact, if you compile with -w or -wi, the compiler will give you a warning about unreachable code.
That is exactly my point. Since "x" it's skipped and never used, it shouldn't just be a warning (unreachable code) instead of an error? I'm trying to understand why/when such code could give any problem. On the other hand if the code were: { goto Q: int x; Q: x = 10; // <- Now you are accessing an uninitialized variable. } Then I think an error would be ok.
D tries to _very_ little with code flow analysis, because once you start having to do much with it, odds are that the compiler implementation is going to get it wrong. As such, any feature that involves code flow analysis in D tends to be _very_ simple. So, D avoids the issue here by saying that you cannot skip the initialization of a variable with goto. The compiler is not going to do the complicated logic of keeping track of where you access the variable in relation to the goto. That's exactly the sort of thing that might be obvious in the simple case but is highly likely to be buggy in more complex code. Code such as { goto Q; int x; } Q: or { if(foo) goto Q; int x; } Q: is fine, because the compiler can trivially see that it is impossible for x to be used after it's been skipped, whereas with something like goto Q; int x; Q: the compiler has to do much more complicated analysis of what the code is doing in order to determine that, and when the code isn't trivial, that can get _really_ complicated. You could argue that it would be nicer if the language required that the compiler be smarter about it, but by having the compiler be stupid, it reduces the risk of compiler bugs, and most people would consider code doing much with gotos like this to be poor code anyway. Most of the cases where goto is reasonable tend to be using goto from inside braces already, because it tends to be used as a way to more efficiently exit deeply nested code. And with D's labeled break and continue, the need for using goto outside of switch statements also tends to be lower than it is in C/C++. - Jonathan M Davis
It's clear now about this decision and by the way thanks for replying all my doubts. MatheusBN.
Don't let their psychobabble fool you. They are wrong and you were right from the start. There is no initialization of the variable, or, if there is(because it's "on the tack, which is "initialized" at the start of the function"), the variable is still never used and that is the whole problem. What you will find with some of these guys is they start with the assumption that everything D does is correct then they try to disprove anything that goes against it by coming up with reasons that explain why D does it the way it does. It is circular reasoning and invalid. Each step they come up with some new explanation when you pick holes in their previous ones. Eventually it's either "It's because D is not designed to do that" or "write an enhancement yourself" type of answer. The fact is simple: Who ever implemented the goto statement did not create code to handle this case and chose the easiest route which is to error out. This was either oversight or "laziness". It's really simple as that. Not once has anyone proven that the semantics are illogical, which is what it would require for the compiler to be absolutely correct in it's error. In this case, they are simple wrong because it requires no flow analysis or any complex logic to determine. It's not because C is stupid and is unsafe, it's unreachable, etc... The compiler simply knows what line and scope a variable is initialized on(since it can determine if a variable is used for initialization, which is a logic error) and it simply has to determine if the goto escapes the scope before using any initialized variable. It can do this easily but the logic was not added. Case A: { if (true) goto X; int x; } X: Case B: { if (true) goto X; { int x; } } X: These two cases are EXACTLY the same semantically. It's like writing A + B and (A + B). What the extra scope does though is create a new scope in the compiler AST and this separates the goto logic, which is properly implemented to handle that case. The fact that one produces one error and the other is valid proves that the compiler is incomplete. Adding scopes does not change semantics no different than adding parenthesis(which is just scope). ((((((3)))))) is the same as 3. (obviously not all scopes can be eliminated in all cases, but this isn't one of those cases) And, so, the real answer is simply the compiler does not test this case. My point with the previous post was to point it out... but as you see, a lot of the fanboys come in and simply defend what D does as if it is the most valid way from the get go. This is their mind set. They reason from their conclusions. I've seen them do it quite often. I'm not sure what the motivations are. If they don't understand the problem(Sometimes simple is very confusing for some) or if they want to obfuscate or what. The idea for any sane person would be to check and see if the code has a semantically logical meaning first. In this case it does. Goto is a common control flow feature and sometimes necessary to greatly simplify certain problems(since D does not have the ability to escape nested scopes such as return3, which returns from 3 nested scopes in). If one can transform logically the "offending" code in to a semantically equivalent piece of code(this is known as mathematical transformation, such as rewriting a mathematical expression using logically valid rules) that involves no real changes(such as adding scopes), and one fails and the other doesn't, it means the compiler has a bug. It's like when people drop parenthesis: (3 + 4)*2 =?= 3 + 4*2. It's illogical. If the compiler did this transformation it would produce invalid results and it would be impossible to reason about code. If the compiler gives errors for one of two identical mathematical tree's(remember, programs are just mathematical formulas, just really complex, but AST's abstractly the same) then the compiler has a problem. It's like saying that (3 + 4)*2 is invalid but 3*2 + 4*2 is valid. It means the compiler did not implement the distributive property. People that don't know what they are talking about will then try to justify why one works and the other doesn't using some circular or invalid logic rather than actually understanding what is going on. It is damn near impossible to reason with these people because they always start with their conclusion and try to make all the pieces fit that conclusion. Sometimes they eventually come around to a logical conclusion but only they've created a rats nest of reasons and cannot proceed any further but to say, basically, "it is what it is". The problem is they still never understand what the actual problem is... (because of the rats nest they have just made themselves even more confused) The problem with the goto is clearly stated and to counter it as being illogical one must simply prove one example where it would result in invalid logic(not crapping out the compiler... the compiler is not perfect and so will have bugs and errors in it. The goal is not to justify those bugs and errors but to fix them so the compiler does a better job and is more logically expressive). e.g., two cases (the `Case` term is not part of a switch in D, just use to denote the two possible scenarios) Case A: { if (true) goto X; int x; } X: Case B: { if (true) goto X; { int x; } } X: Why is case A any different than case B(in general, the above is an example, the compiler might optimize things, which we don't want to do since optimizations are secondary effects that are not as important as logical consistency)? We are simply talking about the pure semantics of programming. It doesn't really matter what language we use to express it, This is not a problem in D but a problem in programming languages. The question is simply: Are the two case semantically equivalent? (e.g., does (3) = 3? (5) = 5, (x) = x, (((((x+y*3))))) = x+y*3, etc ) Since we are not thinking of any specific compiler(although we have to use the syntax and language grammar of D since ultimately it has to do with D and it has to be expressed in some language, so D is the obvious choice) we can't use circular reasoning(e.g., D does it this way and D is right so...). Now, the fact is, these are identical statements semantically... trivially so. It really can't get any simpler. Doesn't matter what D does. If D can't see that then D is incomplete. Now, since we ultimately have to translate in to D and compilers do strange things, it is possible that *in D* they are not identical. E.g., if D inserted initialization of locals at the start of scope and de-initializers at the end of scope, they would not be the same. which one could express as: Case A: int x; { if (true) goto X; //int x; } ~x; X: Case B: { if (true) goto X; int x; { //int x; } ~x; } X: Which, it is clear that x is initialized before the goto in case A and after in case B. This could cause problems(chances are if D did something like this then it would result in invalid programs and compilers bugs at some point). Sometimes though, because compilers are very complex, it is necessary to prevent certain cases from occurring so certain other semantics can be used. Sometimes compilers simply crap out precisely because that is the easiest thing to do. Of course, if this is done, someone should know about it and be able to explain why the compiler chose to do this rather than the most logical thing. Don't let people bludgeon you in to submission. Truth and logic is not dictatorial but absolute.
Nov 07 2018
next sibling parent Stanislav Blinov <stanislav.blinov gmail.com> writes:
On Wednesday, 7 November 2018 at 20:03:47 UTC, Michelle Long 
wrote:

 Case A:

 int x;
 {
    if (true) goto X;
    //int x;
 }
 ~x;
 X:
That is not "Case A". This one is: { if (true) goto X; T x; X: } // x.__dtor That should error as an easy cop-out, nothing wrong with that approach. However, this: { if (true) goto X; T x; } // x.__dtor X: should not error at all, since goto skips both initialization and use. No one is disputing that. From your posts if I understand correctly it's that second case that errors in your code. If it does, there's a bug, but it's on you to provide a test case. Simple as that. Yet you're all wound up as if the world is out to get you.
Nov 07 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 7, 2018 1:03:47 PM MST Michelle Long via Digitalmars-
d-learn wrote:
 Don't let their psychobabble fool you. They are wrong and you
 were right from the start.
...
 Case A:
 {
     if (true) goto X;
     int x;
 }
 X:


 Case B:
 {
     if (true) goto X;
     {
        int x;
     }
 }
 X:


 These two cases are EXACTLY the same semantically. It's like
 writing A + B and (A + B).
That's not the situation that the OP was describing. If adding braces in a situation where the braces have no semantic effect has any impact on goto, then it's a compiler bug. It's adding a new scope that affects the lifetime of a variable whose declaration is being jumped over by a goto that matters. I know that you're frustrated, because you've hit a problem with goto in complex code, and you don't have a simple example that shows the compiler bug, but the approach that D takes with goto (and any construct that potentially requires code flow analysis) of avoiding requiring that the compiler be smart is precisely to reduce the risk of there being cases where the compiler is going to screw it up in complex code even though it gets it right in the simple cases. If the language required the compiler to be smart about such things, we'd have a lot more problems with subtle, hard to track down compiler bugs in complex code. So, we'd just have _more_ cases where people would be hitting frustrating bugs like you are. Regardless, if you want to actually have your problem fixed, you're going to need to provide a reproducible test case in a bugzilla report, even if it's large, otherwise no one is going to be able to track it down for you. Now, a goto-related regression has recently been reported for joiner: https://issues.dlang.org/show_bug.cgi?id=19213 where some code worked with joiner in 2.081.2 but does not work with 2.082.0 or later, so you may want to test your code with an older compiler release to see if you've hit a compiler regression. If so, that could be a starting point for tracking down the problem. - Jonathan M Davis
Nov 07 2018
parent reply Michelle Long <HappyDance321 gmail.com> writes:
On Thursday, 8 November 2018 at 02:22:42 UTC, Jonathan M Davis 
wrote:
 On Wednesday, November 7, 2018 1:03:47 PM MST Michelle Long via 
 Digitalmars- d-learn wrote:
 Don't let their psychobabble fool you. They are wrong and you 
 were right from the start.
...
 Case A:
 {
     if (true) goto X;
     int x;
 }
 X:


 Case B:
 {
     if (true) goto X;
     {
        int x;
     }
 }
 X:


 These two cases are EXACTLY the same semantically. It's like
 writing A + B and (A + B).
That's not the situation that the OP was describing. If adding braces in a situation where the braces have no semantic effect has any impact on goto, then it's a compiler bug. It's adding a new scope that affects the lifetime of a variable whose declaration is being jumped over by a goto that matters. I know that you're frustrated, because you've hit a problem with goto in complex code, and you don't have a simple example that shows the compiler bug, but the approach that D takes with goto (and any construct that potentially requires code flow analysis) of avoiding requiring that the compiler be smart is precisely to reduce the risk of there being cases where the compiler is going to screw it up in complex code even though it gets it right in the simple cases. If the language required the compiler to be smart about such things, we'd have a lot more problems with subtle, hard to track down compiler bugs in complex code. So, we'd just have _more_ cases where people would be hitting frustrating bugs like you are.
That's fine. The D compiler writers can decide to do whatever they want. I can simply take my "business" elsewhere if I don't like it. What I am talking about is about an obvious error(Ok, I haven't produced a simplified test case but dustmite or visual D is not working for me to do so at this point in time, but it would be nice for sake of argument to assume I'm right...).
 Regardless, if you want to actually have your problem fixed, 
 you're going to need to provide a reproducible test case in a 
 bugzilla report, even if it's large, otherwise no one is going 
 to be able to track it down for you.
That's easier said than done. I wasn't expecting anyone to be able to fix a bug that can't be reproduced. What I expect is that, given my assumptions that I'm right, that people can agree the compiler does have a bug or flaw that can easily be fixed give then two simplified test cases basic purely what I have done in my own code. 1. foreach(...) { if (x) goto Y: int z; } Y: Fails. foreach(...) { if (x) goto Y: { int z; } } Y: Passes. THAT IS FACT! It doesn't matter if the examples work above. I have simplified what I have done and in my code I simply add brackets and it works! That is what people should be thinking about... not test cases, MWE's, compiler versions, etc. Is it logical that the compiler **should** error out in the first case and no in the second? That is what the discussion is ALL about. Once that is dealt with then we can move on to finding out more specifics. There is no reason to build the windows of a house before the foundation, it's just far more work without any benefit. Once people can say: If that is all you are doing is adding brackets around what follows the goto and break out of scope(which is what the code above says) and you can compile, then it is a compiler bug or flaw. Once people verify that, rather than trying to create rabbit holes, then I can do more work on finding out more specifics. Until then, it is pointless to do anything on my side because if people come back and say "No, it is suppose to work that way" then what the hell am I trying to simplify and fix? It's not a bug then. But what it seems is that people cannot reason about the purely theoretical underpinnings of the problem and need proof that there is even a problem in the first place, as if I'm making it up and then they create all kinds of obfuscation that doesn't help anyone. If you can agree that removing the brackets in the two test cases should work in ALL regular cases then I can attempt to provide more information. Again, until then, it is pointless. I could waste 10 hours trying to track the issue down to provide a test case and you can just come back and say "Oh, no, that is not a bug, it's suppose to be that way. We will not change anything". I know from previous history that is the typical mentality. Until We can agree that it is a bug, it is pointless for me to treat it as a bug.
 Now, a goto-related regression has recently been reported for 
 joiner:

 https://issues.dlang.org/show_bug.cgi?id=19213

 where some code worked with joiner in 2.081.2 but does not work 
 with 2.082.0 or later, so you may want to test your code with 
 an older compiler release to see if you've hit a compiler 
 regression. If so, that could be a starting point for tracking 
 down the problem.
Until I can simplify the code I have, it won't matter. If it is the same bug, then it will get fixed when it gets fixed with joiner. If it is a different bug, then it will get fixed with I can produce a test case. While I could prove that it is a version issue by trying a different compiler version(I guess I should keep an old one around for that purpose) it seems it is likely that it is the same bug...
Nov 07 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Wednesday, November 7, 2018 10:50:29 PM MST Michelle Long via 
Digitalmars-d-learn wrote:
 On Thursday, 8 November 2018 at 02:22:42 UTC, Jonathan M Davis

 wrote:
 On Wednesday, November 7, 2018 1:03:47 PM MST Michelle Long via

 Digitalmars- d-learn wrote:
 Don't let their psychobabble fool you. They are wrong and you
 were right from the start.
...
 Case A:
 {

     if (true) goto X;
     int x;

 }
 X:


 Case B:
 {

     if (true) goto X;
     {

        int x;

     }

 }
 X:


 These two cases are EXACTLY the same semantically. It's like
 writing A + B and (A + B).
That's not the situation that the OP was describing. If adding braces in a situation where the braces have no semantic effect has any impact on goto, then it's a compiler bug. It's adding a new scope that affects the lifetime of a variable whose declaration is being jumped over by a goto that matters. I know that you're frustrated, because you've hit a problem with goto in complex code, and you don't have a simple example that shows the compiler bug, but the approach that D takes with goto (and any construct that potentially requires code flow analysis) of avoiding requiring that the compiler be smart is precisely to reduce the risk of there being cases where the compiler is going to screw it up in complex code even though it gets it right in the simple cases. If the language required the compiler to be smart about such things, we'd have a lot more problems with subtle, hard to track down compiler bugs in complex code. So, we'd just have _more_ cases where people would be hitting frustrating bugs like you are.
That's fine. The D compiler writers can decide to do whatever they want. I can simply take my "business" elsewhere if I don't like it. What I am talking about is about an obvious error(Ok, I haven't produced a simplified test case but dustmite or visual D is not working for me to do so at this point in time, but it would be nice for sake of argument to assume I'm right...).
 Regardless, if you want to actually have your problem fixed,
 you're going to need to provide a reproducible test case in a
 bugzilla report, even if it's large, otherwise no one is going
 to be able to track it down for you.
That's easier said than done. I wasn't expecting anyone to be able to fix a bug that can't be reproduced. What I expect is that, given my assumptions that I'm right, that people can agree the compiler does have a bug or flaw that can easily be fixed give then two simplified test cases basic purely what I have done in my own code. 1. foreach(...) { if (x) goto Y: int z; } Y: Fails. foreach(...) { if (x) goto Y: { int z; } } Y: Passes. THAT IS FACT! It doesn't matter if the examples work above. I have simplified what I have done and in my code I simply add brackets and it works! That is what people should be thinking about... not test cases, MWE's, compiler versions, etc. Is it logical that the compiler **should** error out in the first case and no in the second? That is what the discussion is ALL about. Once that is dealt with then we can move on to finding out more specifics. There is no reason to build the windows of a house before the foundation, it's just far more work without any benefit. Once people can say: If that is all you are doing is adding brackets around what follows the goto and break out of scope(which is what the code above says) and you can compile, then it is a compiler bug or flaw. Once people verify that, rather than trying to create rabbit holes, then I can do more work on finding out more specifics. Until then, it is pointless to do anything on my side because if people come back and say "No, it is suppose to work that way" then what the hell am I trying to simplify and fix? It's not a bug then.
If you have code where you get a compiler error about a goto skipping the initializiation of a variable, and you add braces that should have no semantic effect on the code whatsoever, and the error goes away, then yes, that's a compiler bug. If, however, the braces do affect the semantics of the code, then that's not necessarily a compiler bug. At that point, whether it's a compiler bug or not would depend on what exactly the code was. In an example such as while(1) { if(cond) goto label; int x; } label: adding braces such as while(1) { if(cond) { goto label; } int x; } label: or while(1) { if(cond) goto label; { int x; } } label: should have no effect. However, in an example such as goto label; int x; label: adding braces such as goto label; { int x; } label: changes the code entirely. It puts the variable x in a new scope such that it no longer exists at the label, and as such, its entire existence is skipped. Braces matter for gotos because of how they affect scopes. If they're placed in a way that they don't affect scope, then they should have no effect on gotos. If they do affect scopes, then they can affect gotos, but whether they do or not is going to depend on the code. The key thing is that gotos cannot skip over the initialization of variables such that at the label, a variable now exists that did not exist at the goto. Variables can be skipped which would have come into scope and then left scope, but any declarations for variables which have not left scope are not allowed between the goto and the label. Braces can obviously affect that, and so braces can matter a great deal. But braces can also be used in ways that are optional, and if the braces are optional, then they don't affect scope, and they should have no effect on gotos.
 But what it seems is that people cannot reason about the purely
 theoretical underpinnings of the problem and need proof that
 there is even a problem in the first place, as if I'm making it
 up and then they create all kinds of obfuscation that doesn't
 help anyone.

 If you can agree that removing the brackets in the two test cases
 should work in ALL regular cases then I can attempt to provide
 more information.

 Again, until then, it is pointless. I could waste 10 hours trying
 to track the issue down to provide a test case and you can just
 come back and say "Oh, no, that is not a bug, it's suppose to be
 that way. We will not change anything". I know from previous
 history that is the typical mentality.

 Until We can agree that it is a bug, it is pointless for me to
 treat it as a bug.
Honestly, without seeing actual code that reproduces the problem, it's very difficult for any of us to accurately tell you whether you're seeing a bug or not. We can try to explain the language rules, and we can say whether we think what we're seeing is a bug based on what you describe, but with how easy it often is to misunderstand a problem when you have the actual code in front of you to study, dealing with everything second hand is bound to be a flawed process. I know that such situations can be very frustrating, but it is going to be very difficult for us to give you a definitive answer. I can tell you that if adding braces which should have zero semantic effect on the code makes the error about skipping a variable's initialization with goto go away, then it definitely sounds like you found a compiler bug. But otherwise, I have no way of knowing without an actual piece of code that exhibits the problem, and regardless, without an actual piece of code that exhibits the problem, the compiler devs won't be able to fix anything.
 Now, a goto-related regression has recently been reported for
 joiner:

 https://issues.dlang.org/show_bug.cgi?id=19213

 where some code worked with joiner in 2.081.2 but does not work
 with 2.082.0 or later, so you may want to test your code with
 an older compiler release to see if you've hit a compiler
 regression. If so, that could be a starting point for tracking
 down the problem.
Until I can simplify the code I have, it won't matter. If it is the same bug, then it will get fixed when it gets fixed with joiner. If it is a different bug, then it will get fixed with I can produce a test case. While I could prove that it is a version issue by trying a different compiler version(I guess I should keep an old one around for that purpose) it seems it is likely that it is the same bug...
Well, if an older version of the compiler works for you, it at least lets you work around the problem for now even if it doesn't actually fix your problem long term, and someone _has_ produced a reduced example for that bug report, so if you have the same problem, it should be only a matter of time before your problem is fixed. Also, if it turns out that an older compiler version works for you, but it's a different problem, then it would be possible (albeit a bit of work) to figure out which commit broke your code, which would go long way to finding a fix. If an older compiler version doesn't work for you, then it doesn't really help you, but at least you then know that whatever problem you're hitting is not the same as that bug report. - Jonathan M Davis
Nov 07 2018
parent reply Michelle Long <HappyDance321 gmail.com> writes:
On Thursday, 8 November 2018 at 06:56:14 UTC, Jonathan M Davis 
wrote:
 On Wednesday, November 7, 2018 10:50:29 PM MST Michelle Long 
 via Digitalmars-d-learn wrote:
 On Thursday, 8 November 2018 at 02:22:42 UTC, Jonathan M Davis

 wrote:
 On Wednesday, November 7, 2018 1:03:47 PM MST Michelle Long 
 via

 Digitalmars- d-learn wrote:
 Don't let their psychobabble fool you. They are wrong and 
 you were right from the start.
...
 Case A:
 {

     if (true) goto X;
     int x;

 }
 X:


 Case B:
 {

     if (true) goto X;
     {

        int x;

     }

 }
 X:


 These two cases are EXACTLY the same semantically. It's like
 writing A + B and (A + B).
That's not the situation that the OP was describing. If adding braces in a situation where the braces have no semantic effect has any impact on goto, then it's a compiler bug. It's adding a new scope that affects the lifetime of a variable whose declaration is being jumped over by a goto that matters. I know that you're frustrated, because you've hit a problem with goto in complex code, and you don't have a simple example that shows the compiler bug, but the approach that D takes with goto (and any construct that potentially requires code flow analysis) of avoiding requiring that the compiler be smart is precisely to reduce the risk of there being cases where the compiler is going to screw it up in complex code even though it gets it right in the simple cases. If the language required the compiler to be smart about such things, we'd have a lot more problems with subtle, hard to track down compiler bugs in complex code. So, we'd just have _more_ cases where people would be hitting frustrating bugs like you are.
That's fine. The D compiler writers can decide to do whatever they want. I can simply take my "business" elsewhere if I don't like it. What I am talking about is about an obvious error(Ok, I haven't produced a simplified test case but dustmite or visual D is not working for me to do so at this point in time, but it would be nice for sake of argument to assume I'm right...).
 Regardless, if you want to actually have your problem fixed, 
 you're going to need to provide a reproducible test case in 
 a bugzilla report, even if it's large, otherwise no one is 
 going to be able to track it down for you.
That's easier said than done. I wasn't expecting anyone to be able to fix a bug that can't be reproduced. What I expect is that, given my assumptions that I'm right, that people can agree the compiler does have a bug or flaw that can easily be fixed give then two simplified test cases basic purely what I have done in my own code. 1. foreach(...) { if (x) goto Y: int z; } Y: Fails. foreach(...) { if (x) goto Y: { int z; } } Y: Passes. THAT IS FACT! It doesn't matter if the examples work above. I have simplified what I have done and in my code I simply add brackets and it works! That is what people should be thinking about... not test cases, MWE's, compiler versions, etc. Is it logical that the compiler **should** error out in the first case and no in the second? That is what the discussion is ALL about. Once that is dealt with then we can move on to finding out more specifics. There is no reason to build the windows of a house before the foundation, it's just far more work without any benefit. Once people can say: If that is all you are doing is adding brackets around what follows the goto and break out of scope(which is what the code above says) and you can compile, then it is a compiler bug or flaw. Once people verify that, rather than trying to create rabbit holes, then I can do more work on finding out more specifics. Until then, it is pointless to do anything on my side because if people come back and say "No, it is suppose to work that way" then what the hell am I trying to simplify and fix? It's not a bug then.
If you have code where you get a compiler error about a goto skipping the initializiation of a variable, and you add braces that should have no semantic effect on the code whatsoever, and the error goes away, then yes, that's a compiler bug. If, however, the braces do affect the semantics of the code, then that's not necessarily a compiler bug. At that point, whether it's a compiler bug or not would depend on what exactly the code was. In an example such as while(1) { if(cond) goto label; int x; } label: adding braces such as while(1) { if(cond) { goto label; } int x; } label: or while(1) { if(cond) goto label; { int x; } } label: should have no effect. However, in an example such as goto label; int x; label: adding braces such as goto label; { int x; } label: changes the code entirely.
Obviously, but that is not the case I mentioned. You can assume that I know how scopes work. No need to assume everyone that shows two cases that you have to bring up an unrelated case as a potential cause unless it is relevant. Remember, I'm showing the starting case that has the bug and the final case that doesn't... it is simply "mental dustmite" on the code I have. You shouldn't assume everyone can't comprehend such trivialities. If a person cannot understand scope then I doubt they understand goto's... and definitely not that adding braces should not change the compiler behavior. What happens when you do that you just create noise that makes it more difficult to have a proper discussion that gets at the whole point of the conversation. You seem to default to assuming that the person you are talking to has no clue about the problem at hand. You might not do it intentionally but it is wrong to assume that because it almost never helps.
Nov 08 2018
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Thursday, November 8, 2018 2:34:34 AM MST Michelle Long via Digitalmars-
d-learn wrote:
 Obviously, but that is not the case I mentioned. You can assume
 that I know how scopes work. No need to assume everyone that
 shows two cases that you have to bring up an unrelated case as a
 potential cause unless it is relevant.

 Remember, I'm showing the starting case that has the bug and the
 final case that doesn't... it is simply "mental dustmite" on the
 code I have.

 You shouldn't assume everyone can't comprehend such trivialities.

 If a person cannot understand scope then I doubt they understand
 goto's... and definitely not that adding braces should not change
 the compiler behavior.

 What happens when you do that you just create noise that makes it
 more difficult to have a proper discussion that gets at the whole
 point of the conversation.

 You seem to default to assuming that the person you are talking
 to has no clue about the problem at hand. You might not do it
 intentionally but it is wrong to assume that because it almost
 never helps.
If you fully understood the situation, you wouldn't be asking whether your case was a compiler bug or not, and I don't know which parts you do and don't understand. So, I tried to be thorough in explaining the situation. And yes, by being thorough, I'm more likely to cover information that you already know and understand, but I'm also more likely to cover the information that you actually need. In order to avoid repeating anything you already understand and only tell you exactly what you need to know, I would have to somehow understand exactly which piece of the puzzle it is you're missing, and I don't. Honestly, that rarely seems to be easy to do, and for whatever reason, it seems to be even harder to do than normal when discussing this issue with you. It seems like most of the discussion of this issue between you and various folks has been people talking passed each other without really coming to an understanding, which always makes for a frustrating discussion. I'd like to help, but I don't know what else I can say. I've tried to explain how braces interact with goto and why, so you should be able to figure out whether you're dealing with a compiler bug or not. And if you are, unless you're dealing with one that someone else already has found independently and reported with a test case, somehow, you're going to need to reduce it enough to be able to create a bug report with an example if you want it fixed. A small example would be nice, but even a large example would be better than nothing. Either way, ultimately, without a reproducible test case, compiler bugs almost never get fixed. It just isn't practical for the compiler devs to track down bugs based on vague bug reports, even if they'd like to be helpful. - Jonathan M Davis
Nov 08 2018
prev sibling parent ShadoLight <ettienne.gilbert gmail.com> writes:
On Thursday, 8 November 2018 at 09:34:34 UTC, Michelle Long wrote:
 What I am talking about is about an obvious error...
OK, so here are your code slightly modified: void main(){ int i; W: if(i==0) goto Q; int x; Q: i++; if(i==1) goto W; writeln("a"); } This code does not even skip the 'int x;' declaration and initialization - it happens the 2nd time after the goto to W - but still errors with exactly the same error i.e. 'goto skips declaration of variable x'. so this is _clearly_ a bug in the compiler (right?), even more so than your original case, since the declaration + initialization are not in fact skipped here. The reason for this behavior is what Jonathan and others have tried to explain to you numerous times i.e. that the compiler does not do flow analysis, and hence cannot detect that the above is in fact working code. You seem to be stuck on your premise that the compiler should be able to detect your "trivially obvious case", but would you also expect the compiler to detect the above case? You seem unwilling to accept that it is a reasonable trade-off that the compiler does not detect your "trivially obvious case", since the general case can be arbitrarily complex - and D currently simply does not do flow analysis. Maybe in the future it will do some form of flow analysis (let's hope!), but currently it doesn't. This isn't some 'conspiracy' or 'mindless' defense of the status quo like you seem to claim:
 What you will find with some of these guys is they start with 
 the assumption that everything D does is correct then they 
 try to disprove anything that goes against it by coming up 
 with reasons that explain why D does it the way it does. It 
 is circular reasoning and invalid. Each step >>> they come up 
 with some new explanation when you pick holes in their 
 previous >>> ones.
If the above was really the case there wouldn't be over 4000 bugs open in D at the moment [1], since they would be (according to you) be rationalized as 'features', not 'bugs', right? But above all ... I really think that you need to calm down a notch or 2, you are seeing way too many conspiracies here. Stuff like this...
 Don't let their psychobabble fool you. They are wrong and 
 you were right from the start.
[snip]
 Is it logical that the compiler **should** error out in the 
 first case and no in the second?

 That is what the discussion is ALL about.
[snip]
 THAT IS FACT! It doesn't matter if the examples work above. I 
 have simplified what I have done and in my code I simply add 
 brackets and it works! That is what people should be thinking 
 about...
[snip]
 ... You can assume that I know how scopes work.
[snip]
 You shouldn't assume everyone can't comprehend such 
 trivialities.
[snip]
 What happens when you do that you just create noise that makes 
 it more difficult to have a proper discussion that gets at the 
 whole point of the conversation.

 You seem to default to assuming that the person you are talking 
 to has no clue about the problem at hand.
You really seem just a tad too certain of your facts and that everyone else are just being obstructionists and incapable of seeing your point. In fact you seem to be the one to block out any 'other' factors raised by others (such as flow analysis), and just keep seeing the issue in this simplistic "this is my view and (I quote) 'what the discussion is ALL about'... ". Are you really so convinced that only 'you' are seeing the forest for the trees..? I notice you started off the same way in your 'Dustmite + Visual D' thread in the IDE category, chastising Valdimir who 'dared' to contradict you:
 As the error message says, the problem occurs because another 
 file is accessing it.
No, have you not learned in your life time that just because something or someeone says something doesn't make it true?
And then, in your typical self-assured way, corrected Vladimir for his 'foolish idea' that the issue is unlikely to be the junction:
 Let me enlighten you a little: The reason why this is most 
 likely a junction is:
[snip] Wow, to be so certain of yourself must be really nice... but... maybe a bit foolish, since in the end it turned out ... Vladimir was correct. But I guess the words "I was wrong" are not really in your vocabulary, is it? Maybe you need to take a long calm look at yourself in the mirror... no-one here is out to "get you", in fact no-one here thinks that the "D-way" is perfect! In fact, most of the old-timers here are probably, more than most, aware of the warts in D. Stick around on these forums for a few years and you will see epic debates between the core developers on why feature 'X' is broken, and what should be done to fix it! But most of all... virtually every person that responded on this thread tried to help you, or at least debate with you on the why's and the how's... but you seem to take disagreements as personal attacks. They are not. They are simply differences of opinion. Just mellow down a notch of 2. Please. [1] https://dlang.org/bugstats.html
Nov 09 2018