On Tuesday, 20 November 2018 at 23:14:27 UTC, Johan Engelen wrote:
On Tuesday, 20 November 2018 at 19:11:46 UTC, Steven
Schveighoffer wrote:
On 11/20/18 1:04 PM, Johan Engelen wrote:
D does not make dereferencing on class objects explicit,
which makes it harder to see where the dereference is
happening.
Again, the terms are confusing. You just said the dereference
happens at a.foo(), right? I would consider the dereference to
happen when the object's data is used. i.e. when you read or
write what the pointer points at.
But `a.foo()` is already using the object's data: it is
accessing a function of the object and calling it. Whether it
is a virtual function, or a final function, that shouldn't
matter.
It matters a lot. A virtual function is a pointer that is in the
instance, so there is a derefernce of the this pointer to get the
address of the function.
For a final function, the address of the function is known at
compile time and no dereferencing is necessary.
That is a thing that a lot of people do not get, a member
function and a plain function are basically the same thing. What
distinguishes them, is their mangled name. You can call a non
virtual member function from an assembly source if you know the
symbol name.
UFCS uses this fact, that member function and plain function are
indistinguishable in a object code point of view, to fake member
functions.
There are different ways of implementing class function calls,
but here often people seem to pin things down to one specific
way. I feel I stand alone in the D community in treating the
language in this abstract sense (like C and C++ do, other
languages I don't know). It's similar to that people think that
local variables and the function return address are put on a
stack; even though that is just an implementation detail that
is free to be changed (and does often change: local variables
are regularly _not_ stored on the stack [*]).
Optimization isn't allowed to change behavior of a program, yet
already simple dead-code-elimination would when null
dereference is not treated as UB or when it is not guarded by a
null check. Here is an example of code that also does what you
call a "dereference" (read object data member):
```
class A {
int i;
final void foo() {
int a = i; // no crash with -O
}
}
void main() {
A a;
a.foo(); // dereference happens
}
No. There's no dereferencing. foo does nothing visible and can be
replaced by a NOP. For the call, no dereferencing required.
```
When you don't call `a.foo()` a dereference, you basically say
Again, no dereferencing for a (final) function call. `a.foo()` is
the same thing as `foo(a)` by reverse UFCS. The generated code is
identical. It is only the compiler that will use different
mangled names.
that `this` is allowed to be `null` inside a class member
function. (and then it'd have to be normal to do `if (this)
...` inside class member functions...)
These discussions are hard to do on a mailinglist, so I'll stop
here. Until next time at DConf, I suppose... ;-)
-Johan
[*] intentionally didn't say where those local variables _are_
stored, so that people can solve that little puzzle for
themselves ;-)