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 ;)