Re: Strange behavior of opEquals for structs

2019-06-19 Thread harfel via Digitalmars-d-learn

On Wednesday, 19 June 2019 at 17:15:31 UTC, Ali Çehreli wrote:

On 06/19/2019 08:28 AM, harfel wrote:

You need to define toHash() member function as well:

  https://dlang.org/spec/hash-map.html#using_struct_as_key

Ali


Thanks Ali,

This fixed my problem, of course. Amazing that such a beginner's 
mistake still receives attention by one of the gurus :-)


harfel


Re: Strange behavior of opEquals for structs

2019-06-19 Thread Ali Çehreli via Digitalmars-d-learn

On 06/19/2019 08:28 AM, harfel wrote:

Everything works nicely if I compare the structs directly. Yet when they 
are used as keys in an associative array, the code throws an exception 
that I do not understand.


You need to define toHash() member function as well:

  https://dlang.org/spec/hash-map.html#using_struct_as_key

Ali


Strange behavior of opEquals for structs

2019-06-19 Thread harfel via Digitalmars-d-learn
I am trying to overload opEquals for a struct. The struct will 
hold class objects that define their own opEquals so the default 
bitwise comparison is not good for me.
Everything works nicely if I compare the structs directly. Yet 
when they are used as keys in an associative array, the code 
throws an exception that I do not understand.


My minimal code example is this:

debug import std.stdio;

struct Foo(T) {
int[T] content;

bool opEquals(const(Foo!T) that) {
debug writeln("opEquals called");
return true;
}

alias content this;
}

class Bar { }

void main()
{
Foo!Bar a = Foo!Bar();
Foo!Bar b = Foo!Bar();
assert(a == b);

debug writeln("This works");

Foo!Bar[int] x = [1: Foo!Bar()];
x[1][new Bar] = 1;
Foo!Bar[int] y = [1: Foo!Bar()];
y[1][new Bar] = 1;

assert(x == y);

debug writeln("This does not work");
}


Here is what I get (using DMD64 D Compiler v2.086.0):

opEquals called
This works
object.Error@(0): TypeInfo.equals is not implemented

??:? bool object._xopEquals(const(void*), const(void*)) [0x49e844]
??:? const pure nothrow @trusted bool 
object.TypeInfo_Struct.equals(const(void*), const(void*)) 
[0x49dcdb]

??:? _aaEqual [0x4a850b]
source/app.d:29 _Dmain [0x46c52f]
Program exited with code 1

Strange thing is that everything works nicely (but produces the 
expected AssertionError) if I comment out Foo.opEquals. What is 
the error message telling me and how can I fix it?


Thanks!!


Re: Behavior of opEquals

2015-09-17 Thread Timon Gehr via Digitalmars-d

On 09/09/2015 09:20 PM, Timon Gehr wrote:


On 09/09/2015 01:32 AM, Jonathan M Davis wrote: (moved from above)

I really don't see any reason why it would even make sense to declare
operators separately from a type.


One reason is that single dispatch can be awkward. A textbook example
would be: ...


I just noticed that I missed to concretely mention one obvious use case:

Overloading mutating operators on reference types with ("logical") value 
semantics, possibly in combination with hash consing. E.g. 
classObject++, or classObject+=x.


Re: Behavior of opEquals

2015-09-09 Thread Jacob Carlborg via Digitalmars-d

On 2015-09-08 18:40, Jonathan M Davis wrote:


Well, it might be a bit annoying, but it's simply a matter of adjusting
your code to call opEquals explicitly when trying to call the base
version


Given that fact that I found this problem in std.xml.Document shows 
either that this is not so easy to remember or that std.xml is basically 
never used and it doesn't have a test for this. That it doesn't have a 
test is true regardless.



,whereas without the free function opEquals, you have subtle
correctness problems. For instance, if you have base == derived and
derived == base, you'll get the same result for both for D, whereas the
equivalent Java or C# could would likely not, because the free function
opEquals checks both lhs.opEquals(rhs) and rhs.OpEquals(lhs) whether you
did base == derived or derived == base.

So, while what we have is by no means perfect, I think that it is an
improvement over what Java and C# did.


It should be possible for the compiler to see that it's a super call and 
generate a call to opSuperEquals (or similar) instead, which would make 
a non-virtual call when calling opEquals on the instance.


--
/Jacob Carlborg


Re: Behavior of opEquals

2015-09-09 Thread deadalnix via Digitalmars-d
On Saturday, 5 September 2015 at 09:44:13 UTC, Jacob Carlborg 
wrote:

On 2015-09-05 08:18, Jonathan M Davis wrote:

There is nothing in the spec about supporting operator 
overloading with
free functions, so I don't know where you get the idea that 
it's even
intended to be a feature. UFCS applies to functions which use 
the member
function call syntax, and operators aren't used that way. 
There is no
plan whatsoever to support operator overloading via free 
functions.


Since "a == b" would be lowered to "a.opEquals(b)" one could 
argue that the compile would also try UFCS since it would do 
that if the code had been "a.opEquals(b)" from the beginning.


The voice of reason.


Re: Behavior of opEquals

2015-09-09 Thread Timon Gehr via Digitalmars-d

On 09/09/2015 01:32 AM, Jonathan M Davis wrote:

On Tuesday, 8 September 2015 at 20:55:35 UTC, Timon Gehr wrote:

On 09/08/2015 06:49 PM, Jonathan M Davis wrote:

Sure, it _could_ be implemented that way, but the only reason I
see to do that is if we're specifically looking to support defining
overloaded operators outside of the types that they apply to. I can't
think of anything else that would be affected by it.
...


The compiler does not match the specification. I see no reason to
change the specification here, but it would be easy.


I don't know where you get this idea.


Lowering transforms D code to other D code.

See http://dlang.org/operatoroverloading.html :

We have that e.g. "-e" gets rewritten to "e.opUnary!("-")()".
"e.opUnary!("-")()" is D code. To specify the semantics implemented in 
DMD, the rewrite should be __traits(getMember,e,"opUnary")!("-")() 
(assuming that the parser is fixed so it can actually parse this, which 
would be useful in its own right.)



Regardless, I honestly think that it would be a very bad technical
decision to support defining overloaded operators outside of the type
itself - _especially_ when you take into account operators that have
defaults generated by the compiler (e.g. opEquals), since that would
allow a third party to change what your code does by adding their own
overloaded operator.


Well, how? "Overloaded operators" are just specially named functions
that support an additional call syntax. There are no strange issues
with modularity that somehow only apply to overloaded operators. ...


There would be no way to disambiguate overloaded operators if an
operator were overloaded in multiple modules. ...
...


Yes, there would be. Just use aliases. (Also, overloaded operators are 
not actually that likely to cause conflicts, because they are best 
written to match a suitably constrained set of types, just like other 
functions.)



UFCS is already enough of a problem on its own. It has some benefits,
but it doesn't work when conflicts come into play, forcing you to just
call the function normally,


No, it does not force you to do that.


and its overload rules are such that it
doesn't actually prevent hijacking in all cases (e.g. your code could
suddenly change behavior, because you used UFCS with a type that then
had a function with the same name and parameters added to it).


I think that's universally understood, but I don't see how this relates 
to the issue at hand.



And
overloaded operators are closely tied to what a type does, whereas
functions are far more diverse.  So, there's a lot more to be gained with
UFCS than with declaring overloaded operators separately from a type.


UFCS and this ad-hoc terminology do not describe separate things.


So that you can declare multiple
overloads of it for when you import different modules? That would just
be plain confusing and incredibly error-prone.


(Straw man.)


And if the problem is
that you're dealing with someone else's type, then just declare a
function to do what you want and be done with it.


I agree with this part, overloaded operators are just such functions.


And do you really want
people to be able to overload stray operators for stuff like strings and
then write code that uses + and - and / or whatever on them? That would
be ludicrous. It's one thing for people to do that with their own types.
It's quite another to do that to built-in types or to someone else's
types.


This is not distinct from being able to declare functions operating on 
built-in types or someone else's types that have other unsuitable names. 
Also, in case you missed it, I also briefly promoted the idea that 
built-in types should define the operator overloading functions in order 
to make treatment of operators uniform (as far as the specification is 
concerned, the compiler could just rewrite the special members to 
whatever internal representation it uses now for the built-in types). In 
particular, "+", "-" and "/" can't be overloaded externally on strings 
in such a setting.



What do you want next?


(That's a nice way of introducing a straw man.)


To be able to declare constructors for someone else's type?


That's not even close to being similar.


That kind of stuff


:o)

AFAICT, they were just clustered together randomly in order to 
artificially boost the argument.



is part of the type, and
allowing 3rd parties to declare it as well just makes it that much
harder to figure out what's going on. At least when using UFCS, it's the
exception that the function is on the type, so the overload rules don't
usually shoot you in the foot (though they can), and you know to look
elsewhere for the function just like you would with a function that was
called normally. It's the complete opposite with operators.
...


I don't see where you get this idea. It should be noted that operator 
overloading isn't even that common and basically all legitimate usages 
are obvious. (They are those cases where a operator is 

Re: Behavior of opEquals

2015-09-08 Thread Timon Gehr via Digitalmars-d

On 09/08/2015 06:49 PM, Jonathan M Davis wrote:

On Monday, 7 September 2015 at 10:26:00 UTC, Timon Gehr wrote:

On 09/05/2015 08:18 AM, Jonathan M Davis wrote:

On Friday, 4 September 2015 at 20:39:14 UTC, Timon Gehr wrote:

On 09/04/2015 09:21 PM, H. S. Teoh via Digitalmars-d wrote:


Wait, wait, did I miss something? Since when was operator overloading
allowed as free functions?


Since UFCS, but DMD does not implement it.


There is nothing in the spec about supporting operator overloading with
free functions, so I don't know where you get the idea that it's even
intended to be a feature. UFCS applies to functions which use the member
function call syntax, and operators aren't used that way.


Specifying semantics via lowering is somewhat pointless if rewrites
are not transitive.


Specifying semantics via lowering makes the compiler simpler


Not necessarily. The compiler can do lowering (even using internal 
syntax tree nodes that have no D syntax equivalent!) no matter how the 
behaviour is specified. It's just easier to come up with the required 
compiler code and to verify it if the specification does it this way.



and the
expected behavior easier to understand. Nothing about that  requires that
it transitively apply all syntactic sugar, and UFCS is simply syntactic
sugar.



All about that requires that it applies all rewrites transitively. It is 
the entire point that the lowered version is again plain D code. (Also, 
UFCS is not "simply syntactic sugar". There are special lookup rules. 
Operator overloading ought to be the simple syntactic sugar here, but it 
isn't, because built-in types don't define the respective operator 
overloading functions. They should.)



Sure, it _could_ be implemented that way, but the only reason I
see to do that is if we're specifically looking to support defining
overloaded operators outside of the types that they apply to. I can't
think of anything else that would be affected by it.
...


The compiler does not match the specification. I see no reason to change 
the specification here, but it would be easy.



Regardless, I honestly think that it would be a very bad technical
decision to support defining overloaded operators outside of the type
itself - _especially_ when you take into account operators that have
defaults generated by the compiler (e.g. opEquals), since that would
allow a third party to change what your code does by adding their own
overloaded operator.


Well, how? "Overloaded operators" are just specially named functions 
that support an additional call syntax. There are no strange issues with 
modularity that somehow only apply to overloaded operators. UFCS calls 
can never ignore methods of the type. It does not matter how they were 
generated. Was this your strongest point against having the compiler 
combine UFCS and operator call syntax in the straightforward fashion?




Re: Behavior of opEquals

2015-09-08 Thread Jonathan M Davis via Digitalmars-d

On Tuesday, 8 September 2015 at 20:55:35 UTC, Timon Gehr wrote:

On 09/08/2015 06:49 PM, Jonathan M Davis wrote:

Sure, it _could_ be implemented that way, but the only reason I
see to do that is if we're specifically looking to support 
defining
overloaded operators outside of the types that they apply to. 
I can't

think of anything else that would be affected by it.
...


The compiler does not match the specification. I see no reason 
to change the specification here, but it would be easy.


I don't know where you get this idea. The spec says _nothing_ 
about UFCS applying to overloaded operators or that UFCS would 
apply to lowered code in any way shape or form.


Regardless, I honestly think that it would be a very bad 
technical
decision to support defining overloaded operators outside of 
the type
itself - _especially_ when you take into account operators 
that have
defaults generated by the compiler (e.g. opEquals), since that 
would
allow a third party to change what your code does by adding 
their own

overloaded operator.


Well, how? "Overloaded operators" are just specially named 
functions that support an additional call syntax. There are no 
strange issues with modularity that somehow only apply to 
overloaded operators. UFCS calls can never ignore methods of 
the type. It does not matter how they were generated. Was this 
your strongest point against having the compiler combine UFCS 
and operator call syntax in the straightforward fashion?


There would be no way to disambiguate overloaded operators if an 
operator were overloaded in multiple modules. foo + bar has no 
import paths involved in it at all. So, what would you do, write 
opBinary!"+"(foo, bar) instead? That's downright hideous, and it 
relies on you using the function call to emulate the operator 
correctly. For something like bar++, you'd be even more screwed, 
because it doesn't lower to a simple function call.


UFCS is already enough of a problem on its own. It has some 
benefits, but it doesn't work when conflicts come into play, 
forcing you to just call the function normally, and its overload 
rules are such that it doesn't actually prevent hijacking in all 
cases (e.g. your code could suddenly change behavior, because you 
used UFCS with a type that then had a function with the same name 
and parameters added to it). And overloaded operators are closely 
tied to what a type does, whereas functions are far more diverse. 
So, there's a lot more to be gained with UFCS than with declaring 
overloaded operators separately from a type. I really don't see 
any reason why it would even make sense to declare operators 
separately from a type. So that you can declare multiple 
overloads of it for when you import different modules? That would 
just be plain confusing and incredibly error-prone. And if the 
problem is that you're dealing with someone else's type, then 
just declare a function to do what you want and be done with it. 
And do you really want people to be able to overload stray 
operators for stuff like strings and then write code that uses + 
and - and / or whatever on them? That would be ludicrous. It's 
one thing for people to do that with their own types. It's quite 
another to do that to built-in types or to someone else's types. 
What do you want next? To be able to declare constructors for 
someone else's type? That kind of stuff is part of the type, and 
allowing 3rd parties to declare it as well just makes it that 
much harder to figure out what's going on. At least when using 
UFCS, it's the exception that the function is on the type, so the 
overload rules don't usually shoot you in the foot (though they 
can), and you know to look elsewhere for the function just like 
you would with a function that was called normally. It's the 
complete opposite with operators.


We have overloaded operators so that someone who is writing a 
user-defined type can make it act like a built-in type where 
appropriate. Allowing 3rd party code to declare overloaded 
operators for a type doesn't help with that at all.


There is _nothing_ in the spec which supports UFCS having 
anything to do with overloaded operators and nothing to support 
overloading operators separately from a type. And I recall Walter 
Bright stating that it was on purpose that you can only overload 
operators on a type. So, even if the spec _could_ be interpreted 
to mean that you should be able to declare overloaded operators 
separately from the type that they apply to, that's _not_ its 
intention, and you're going to have to convince Walter if you 
want anything else.


- Jonathan M Davis


Re: Behavior of opEquals

2015-09-08 Thread Jonathan M Davis via Digitalmars-d
On Saturday, 5 September 2015 at 09:45:36 UTC, Jacob Carlborg 
wrote:

On 2015-09-05 08:26, Jonathan M Davis wrote:


Clearly, you haven't read TDPL recently enough. ;)

There is a free function, opEquals, in object.d which gets 
called for
classes, and _it_ is what == gets translated to for classes, 
and it

calls the member function version of opEquals on classes:

https://github.com/D-Programming-Language/druntime/blob/master/src/object.d#L143


This allows us to avoid a number of fun bugs with opEquals 
that you get
in languages like Java and makes it completely unnecessary to 
do stuff
like check whether the argument to opEquals is null. Timon 
gave the link

to the explanation in the spec:


Bu you don't see my example as a problem?


Well, it might be a bit annoying, but it's simply a matter of 
adjusting your code to call opEquals explicitly when trying to 
call the base version, whereas without the free function 
opEquals, you have subtle correctness problems. For instance, if 
you have base == derived and derived == base, you'll get the same 
result for both for D, whereas the equivalent Java or C# could 
would likely not, because the free function opEquals checks both 
lhs.opEquals(rhs) and rhs.OpEquals(lhs) whether you did base == 
derived or derived == base.


So, while what we have is by no means perfect, I think that it is 
an improvement over what Java and C# did.


- Jonathan M Davis


Re: Behavior of opEquals

2015-09-08 Thread Jonathan M Davis via Digitalmars-d

On Monday, 7 September 2015 at 10:26:00 UTC, Timon Gehr wrote:

On 09/05/2015 08:18 AM, Jonathan M Davis wrote:

On Friday, 4 September 2015 at 20:39:14 UTC, Timon Gehr wrote:

On 09/04/2015 09:21 PM, H. S. Teoh via Digitalmars-d wrote:


Wait, wait, did I miss something? Since when was operator 
overloading

allowed as free functions?


Since UFCS, but DMD does not implement it.


There is nothing in the spec about supporting operator 
overloading with
free functions, so I don't know where you get the idea that 
it's even
intended to be a feature. UFCS applies to functions which use 
the member

function call syntax, and operators aren't used that way.


Specifying semantics via lowering is somewhat pointless if 
rewrites are not transitive.


Specifying semantics via lowering makes the compiler simpler and 
the expected behavior easier to understand. Nothing about that 
requires that it transitively apply all syntactic sugar, and UFCS 
is simply syntactic sugar. Sure, it _could_ be implemented that 
way, but the only reason I see to do that is if we're 
specifically looking to support defining overloaded operators 
outside of the types that they apply to. I can't think of 
anything else that would be affected by it.


Regardless, I honestly think that it would be a very bad 
technical decision to support defining overloaded operators 
outside of the type itself - _especially_ when you take into 
account operators that have defaults generated by the compiler 
(e.g. opEquals), since that would allow a third party to change 
what your code does by adding their own overloaded operator. And 
IIRC, Walter has explicitly stated that it's purposeful that you 
cannot define overloaded operators outside of the struct/class 
that they're for. So, anyone who wants it to be otherwise is 
going to have to convince him.


- Jonathan M Davis


Re: Behavior of opEquals

2015-09-08 Thread via Digitalmars-d
On Thursday, 3 September 2015 at 06:37:20 UTC, Jacob Carlborg 
wrote:

On 2015-09-02 22:25, w0rp wrote:


Yeah, I would just call super.opEquals, like so.


I know that's the workaround, but the question is if it's a 
good implementation/behavior of opEquals.


Strictly speaking OO equality ought to yield three outcomes: yes, 
no, maybe.


You can't really tell if an instance of Cat and and instance of 
Animal are equal based on features. It would be better to project 
an aspect of the instance and compare that instead ("equally 
powerful" etc).




Re: Behavior of opEquals

2015-09-07 Thread Timon Gehr via Digitalmars-d

On 09/05/2015 08:18 AM, Jonathan M Davis wrote:

On Friday, 4 September 2015 at 20:39:14 UTC, Timon Gehr wrote:

On 09/04/2015 09:21 PM, H. S. Teoh via Digitalmars-d wrote:


Wait, wait, did I miss something? Since when was operator overloading
allowed as free functions?


Since UFCS, but DMD does not implement it.


There is nothing in the spec about supporting operator overloading with
free functions, so I don't know where you get the idea that it's even
intended to be a feature. UFCS applies to functions which use the member
function call syntax, and operators aren't used that way.


Specifying semantics via lowering is somewhat pointless if rewrites are 
not transitive.



There is no plan whatsoever to support operator overloading via free functions.
...


Then specify operator overloading using __traits(getMember,...). (I 
consider this unwise though.)


Re: Behavior of opEquals

2015-09-05 Thread Jonathan M Davis via Digitalmars-d

On Friday, 4 September 2015 at 19:25:35 UTC, H. S. Teoh wrote:
Wait, wait, did I miss something? Since when was operator 
overloading allowed as free functions? Or is opEquals a special 
case?


Clearly, you haven't read TDPL recently enough. ;)

There is a free function, opEquals, in object.d which gets called 
for classes, and _it_ is what == gets translated to for classes, 
and it calls the member function version of opEquals on classes:


https://github.com/D-Programming-Language/druntime/blob/master/src/object.d#L143

This allows us to avoid a number of fun bugs with opEquals that 
you get in languages like Java and makes it completely 
unnecessary to do stuff like check whether the argument to 
opEquals is null. Timon gave the link to the explanation in the 
spec:


http://dlang.org/operatoroverloading.html#equals

but TDPL also talks about it. But as part of the fix for

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

we need to templatize the free function opEquals so that it 
doesn't require Object. Then opEquals on a class will take 
whatever the most base class in your class hierarchy is that has 
opEquals rather than Object, and we can actually deprecate 
opEquals on Object (though that may require some additional 
compiler help rather than simply deprecating it; I'm not sure). 
Regardless, templatizing the free function version of opEquals is 
the first step towards that.


- Jonathan M Davis


Re: Behavior of opEquals

2015-09-05 Thread Jacob Carlborg via Digitalmars-d

On 2015-09-05 08:26, Jonathan M Davis wrote:


Clearly, you haven't read TDPL recently enough. ;)

There is a free function, opEquals, in object.d which gets called for
classes, and _it_ is what == gets translated to for classes, and it
calls the member function version of opEquals on classes:

https://github.com/D-Programming-Language/druntime/blob/master/src/object.d#L143


This allows us to avoid a number of fun bugs with opEquals that you get
in languages like Java and makes it completely unnecessary to do stuff
like check whether the argument to opEquals is null. Timon gave the link
to the explanation in the spec:


Bu you don't see my example as a problem?

--
/Jacob Carlborg


Re: Behavior of opEquals

2015-09-05 Thread Jonathan M Davis via Digitalmars-d

On Friday, 4 September 2015 at 20:39:14 UTC, Timon Gehr wrote:

On 09/04/2015 09:21 PM, H. S. Teoh via Digitalmars-d wrote:


Wait, wait, did I miss something? Since when was operator 
overloading

allowed as free functions?


Since UFCS, but DMD does not implement it.


There is nothing in the spec about supporting operator 
overloading with free functions, so I don't know where you get 
the idea that it's even intended to be a feature. UFCS applies to 
functions which use the member function call syntax, and 
operators aren't used that way. There is no plan whatsoever to 
support operator overloading via free functions.


- Jonathan M Davis


Re: Behavior of opEquals

2015-09-05 Thread Jacob Carlborg via Digitalmars-d

On 2015-09-05 08:18, Jonathan M Davis wrote:


There is nothing in the spec about supporting operator overloading with
free functions, so I don't know where you get the idea that it's even
intended to be a feature. UFCS applies to functions which use the member
function call syntax, and operators aren't used that way. There is no
plan whatsoever to support operator overloading via free functions.


Since "a == b" would be lowered to "a.opEquals(b)" one could argue that 
the compile would also try UFCS since it would do that if the code had 
been "a.opEquals(b)" from the beginning.


--
/Jacob Carlborg


Re: Behavior of opEquals

2015-09-04 Thread H. S. Teoh via Digitalmars-d
On Fri, Sep 04, 2015 at 07:10:25PM +, Jonathan M Davis via Digitalmars-d 
wrote:
> On Thursday, 3 September 2015 at 13:05:49 UTC, Steven Schveighoffer wrote:
> >On 9/2/15 2:57 PM, Jacob Carlborg wrote:
> >
> >>In this case the solution/workaround is to explicitly call
> >>super.opEquals, but that will miss some optimizations implemented in
> >>object.opEquals.
> >
> >Those optimizations have already been exploited by the time you get
> >to Foo.opEquals, so I wouldn't worry about that.
> >
> >However, the avoidance of casting would be a good goal. One of the
> >things I don't like about the current == implementation for objects
> >is it cannot take any advantage of type knowledge at the call site.
> 
> Every time I've tried to templatize the free function opEquals, [...]
[...]

Wait, wait, did I miss something? Since when was operator overloading
allowed as free functions? Or is opEquals a special case?


T

-- 
If it tastes good, it's probably bad for you.


Re: Behavior of opEquals

2015-09-04 Thread Jonathan M Davis via Digitalmars-d
On Thursday, 3 September 2015 at 13:05:49 UTC, Steven 
Schveighoffer wrote:

On 9/2/15 2:57 PM, Jacob Carlborg wrote:


In this case the solution/workaround is to explicitly call
super.opEquals, but that will miss some optimizations 
implemented in

object.opEquals.


Those optimizations have already been exploited by the time you 
get to Foo.opEquals, so I wouldn't worry about that.


However, the avoidance of casting would be a good goal. One of 
the things I don't like about the current == implementation for 
objects is it cannot take any advantage of type knowledge at 
the call site.


Every time I've tried to templatize the free function opEquals, 
I've run into compiler bugs, but we'll get there eventually. It 
looks like Kenji has a PR now to fix one of the issues:


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

So, I'll have to make another stab at it soon.

- Jonathan M Davis


Re: Behavior of opEquals

2015-09-04 Thread Timon Gehr via Digitalmars-d

On 09/04/2015 09:21 PM, H. S. Teoh via Digitalmars-d wrote:


Wait, wait, did I miss something? Since when was operator overloading
allowed as free functions?


Since UFCS, but DMD does not implement it.


Or is opEquals a special case?



Yup, quite special: http://dlang.org/operatoroverloading.html#equals



Re: Behavior of opEquals

2015-09-03 Thread Jacob Carlborg via Digitalmars-d

On 2015-09-02 22:25, w0rp wrote:


Yeah, I would just call super.opEquals, like so.


I know that's the workaround, but the question is if it's a good 
implementation/behavior of opEquals.


--
/Jacob Carlborg


Re: Behavior of opEquals

2015-09-03 Thread Steven Schveighoffer via Digitalmars-d

On 9/2/15 2:57 PM, Jacob Carlborg wrote:


In this case the solution/workaround is to explicitly call
super.opEquals, but that will miss some optimizations implemented in
object.opEquals.


Those optimizations have already been exploited by the time you get to 
Foo.opEquals, so I wouldn't worry about that.


However, the avoidance of casting would be a good goal. One of the 
things I don't like about the current == implementation for objects is 
it cannot take any advantage of type knowledge at the call site.


-Steve


Behavior of opEquals

2015-09-02 Thread Jacob Carlborg via Digitalmars-d
I encountered a problem in the implementation of 
std.xml.Document.opEquals (yes, I've reported an issue). The problem is 
demonstrated with this example:


class Base
{
int a;

override bool opEquals (Object o)
{
if (auto base = cast(Base) o)
return base.a == a;
else
return false;
}
}

class Foo : Base
{
int b;

override bool opEquals (Object o)
{
if (auto foo = cast(Foo) o)
return super == cast(Base) foo && foo.b == b;
else
return false;
}
}

void main()
{
auto f1 = new Foo;
auto f2 = new Foo;
assert(f1 == f2);
}

This code will result in an infinite recursion. I think the problem is 
in the super call, due to == being rewritten to call object.opEquals. 
The implementation of object.opEquals will call opEquals on the actual 
instances. The call will be dynamically resolved and end up calling 
Foo.opEquals instead of Base.opEquals.


Is this really good behavior, something a developer would expect? I 
mean, in every other case calling super.someMethod will actually call 
the method in the base class.


In this case the solution/workaround is to explicitly call 
super.opEquals, but that will miss some optimizations implemented in 
object.opEquals.


--
/Jacob Carlborg


Re: Behavior of opEquals

2015-09-02 Thread w0rp via Digitalmars-d
On Wednesday, 2 September 2015 at 18:57:11 UTC, Jacob Carlborg 
wrote:
I encountered a problem in the implementation of 
std.xml.Document.opEquals (yes, I've reported an issue). The 
problem is demonstrated with this example:


class Base
{
int a;

override bool opEquals (Object o)
{
if (auto base = cast(Base) o)
return base.a == a;
else
return false;
}
}

class Foo : Base
{
int b;

override bool opEquals (Object o)
{
if (auto foo = cast(Foo) o)
return super == cast(Base) foo && foo.b == b;
else
return false;
}
}

void main()
{
auto f1 = new Foo;
auto f2 = new Foo;
assert(f1 == f2);
}

This code will result in an infinite recursion. I think the 
problem is in the super call, due to == being rewritten to call 
object.opEquals. The implementation of object.opEquals will 
call opEquals on the actual instances. The call will be 
dynamically resolved and end up calling Foo.opEquals instead of 
Base.opEquals.


Is this really good behavior, something a developer would 
expect? I mean, in every other case calling super.someMethod 
will actually call the method in the base class.


In this case the solution/workaround is to explicitly call 
super.opEquals, but that will miss some optimizations 
implemented in object.opEquals.


Yeah, I would just call super.opEquals, like so.

class Base {
int a;

override bool opEquals(Object o) {
if (auto other = cast(Base) o)
return a == other.a;

return false;
}
}

class Foo : Base {
int b;

override bool opEquals(Object o) {
if (!super.opEquals(o))
return false;

if (auto other = cast(Foo) o)
return b == other.b;

return false;
}
}

void main()
{
auto f1 = new Foo;
auto f2 = new Foo;
assert(f1 == f2);
}

If some optimisations are missed by structuring the methods in 
this way, then maybe that's something the compiler should be 
programmed to handle.