On Friday, 4 January 2013 at 20:59:44 UTC, Jonathan M Davis wrote:
On Friday, January 04, 2013 21:47:42 js.mdnq wrote:
On Friday, 4 January 2013 at 16:47:38 UTC, Jonathan M Davis wrote:
> On Friday, January 04, 2013 13:12:52 js.mdnq wrote:
>> can you show an example of such a bug? I assume you mean >> that a >> "struct literal" ends up being a local object and a ref to >> it
>> can
>> easily become invalid?(in your example this is not possible
>> inside main). But your example basically contradicts what >> you
>> say.
>> >> There is no semantic difference between >> >> S s = S(2); foo(s) >> >> and >> >> foo(S(2)); > > There's a _huge_ difference between those two. In the first
> case, you have a
> variable which exists beyond the end of the function call. In
> the second, you
> have temporary which is destroyed as soon as the statement > has
> completed.

Nope, sorry, it's not. That is only a compiler optimization. Just
because the compiler decides to do something to make them
different does not mean they are. The compiler could easily just
make S(2) null for no obvious reason because it wants to.

But if the compiler decides to make a different because of some
reason(such as optimization then it also can decide to not do so.

For example, tell me why the compiler can't just expand

foo(S(2));

to

S ___s = S(2);
foo(___s)

where ___s is hidden?

S(2) _must_ leave scope after the statement foo(S(2)) completes, whereas with

S s = S(2);
foo(s);

the variable must continue to exist after foo(s) completes. That's fundamentally different. We're _not_ talking about compiler optimizations here. We're talking about the semantics of how the code works. And creating a variable on the stack would change the code's semantics, especially because foo(S(2)) should involve a move operation, whereas creating a variable on the stack would require that the object be destroyed after the call to foo. But even if declaring a hidden variable didn't change the semantics, __s must be destroyed once the statement with foo has completed, so it won't exist beyond the call to foo (it can't or it would alter the semantics of the code), so it's still fundamentally different from declaring a variable and passing it to foo, since the variable must continue to exist afterwards whereas the
temporary must be gone.

- Jonathan M Davis


Nope, technically the "struct literal" does not go out of scope because it exists on the stack till the end of the outer scope... just as the local variable does. As I said, it is equivalent, due to the substitution principle to the hidden variable:

S s = S(2); foo(s) <==> foo(S(2))

because

foo(S(2)) can be shorthand for "hidden S __s = S(2); foo(s);"

(so, computationally these would all(or should) produce identical results).

You are saying because "visually" foo(S(2)) leaves the "scope" it is different than the others. This is true, but only visually(or syntactically). But the compiler could give you access to the hidden variable and then you would be wrong(it would not go out of scope).

Scope is not a physical but logical syntactical construct imposed by the compiler to help the user break a complex structure into nested units.

an only possibility would be something like:

e.g., we could have (pseudo)

  S s = S(2);
  foo(s);
  clear(s);

identical to

  hidden S __s = S(2); // hidden
  foo(__s);  // but seen has foo(S(2)) by user
  clear(__s); // hidden


and so both cases, as the original are the same.

The only way you would be right is if we had:


  hidden S __s = S(2); // hidden
  foowrap(__s);  // but seen has foo(S(2)) by user

foowrap(ref S s) if (s is hidden) { foo(s); clear(s); } else { foo(s); }

BUT then we could still have

  S s = S(2);
  foowrap(s);


The proof is in the pudding:

The statement:

foo(S(2));

causes the compiler to "create" the struct S(2) on the stack at some location or puts the value into registers(these methods are essentially identical, at the very least for our purpose, one being faster and having some limitations). We'll assume S(2) exists on the stack as an identical* argument can be made for the registers

foo is passed a ptr to a memory location on the stack. foo does what it does and returns. S(2) still exists at the same memory location on the stack after the return call(possibly modified).

Now, take the statement:

S s = S(2);
foo(s);

The exact same argument is used except we would replace S(2) with s so to speak. The compiler "creates" s on the stack, passes it to foo, and returns.

Both cases the stack is "cleaned" up at the end of the scope. So, the difference you are talking about is only due to the compiler hiding the variable. To show that it is not we could have a compiler construct:

foo(S(2));
foo!LastArgument; // (the hidden variable used by the compiler)

in which case, using the notation I've used before,

foo!LastArgument == __s;

Hence, such a **compiler** construct rectifies the issue you are talking about which proves that with foo(S(2)), S(2) only goes out of scope logically due to the compiler not providing such a construct but what I have talked about with the stack/registers shows that they do not go out of scope physically(so to speak).

I mean, I agree with you, except the part about "fundamental", in the statement you have said, but it is a different argument as it is all about how the compiler deals with it, which is what I said initially that it is part of the compiler but there is no semantic difference.

Obviously there is a symbolic difference, there is a difference in syntax, but those differences are for wusses. Real men see things abstractly and realize that function is more important than form! (well, unless we are talking about women then it becomes much more difficult ;)











Reply via email to