September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu schrieb:
> Frank Benoit wrote:
>> Andrei Alexandrescu schrieb:
>>> Consider two objects a and b with a of class type. Currently, the
>>> expression a == b is blindly rewritten as a.opEquals(b). I argue it
>>> should be rewritten into a call to an (imaginary/inlined) function
>>> equalObjects(a, b), with the following definition:
>>>
>>> bool equalObjects(T, U)(T a, U b) if (is(T == class))
>>> {
>>> static if (is(U == class))
>>> {
>>> if (b is null) return a is null;
>>> if (a is null) return b is null;
>>> }
>>> else
>>> {
>>> enforce(a !is null);
>>> }
>>> return a.opEquals(b);
>>> }
>>>
>>> This hoists the identity test outside the opEquals call and also deals with null references. What do you think?
>>>
>>>
>>> Andrei
>>
>> What about interfaces?
>
> Good question! What do they do now? I ran this:
>
> interface A {}
> class Widget : A {}
>
> void main() {
> auto a = cast(A) new Widget;
> A b = null;
> writeln(a == b);
> writeln(b == a);
> }
>
> To my surprise, the program printed false twice. If I replace A with Widget inside main, the program prints false then crashes with the mythical segfault :o).
>
> So how are interfaces compared?
>
>
> Andrei
Hm, i would have expected it not to compile, because A does not have opEquals.
In DWT, I cast always first to Object.
Java> if( intf1.equals(intf2) ){
D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){
| |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Frank Benoit | Frank Benoit wrote:
> Andrei Alexandrescu schrieb:
>> Frank Benoit wrote:
>>> Andrei Alexandrescu schrieb:
>>>> Consider two objects a and b with a of class type. Currently, the
>>>> expression a == b is blindly rewritten as a.opEquals(b). I argue it
>>>> should be rewritten into a call to an (imaginary/inlined) function
>>>> equalObjects(a, b), with the following definition:
>>>>
>>>> bool equalObjects(T, U)(T a, U b) if (is(T == class))
>>>> {
>>>> static if (is(U == class))
>>>> {
>>>> if (b is null) return a is null;
>>>> if (a is null) return b is null;
>>>> }
>>>> else
>>>> {
>>>> enforce(a !is null);
>>>> }
>>>> return a.opEquals(b);
>>>> }
>>>>
>>>> This hoists the identity test outside the opEquals call and also deals
>>>> with null references. What do you think?
>>>>
>>>>
>>>> Andrei
>>> What about interfaces?
>> Good question! What do they do now? I ran this:
>>
>> interface A {}
>> class Widget : A {}
>>
>> void main() {
>> auto a = cast(A) new Widget;
>> A b = null;
>> writeln(a == b);
>> writeln(b == a);
>> }
>>
>> To my surprise, the program printed false twice. If I replace A with
>> Widget inside main, the program prints false then crashes with the
>> mythical segfault :o).
>>
>> So how are interfaces compared?
>>
>>
>> Andrei
>
> Hm, i would have expected it not to compile, because A does not have
> opEquals.
>
> In DWT, I cast always first to Object.
> Java> if( intf1.equals(intf2) ){
> D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){
I think in D the cast is inserted automatically. Walter?
Andrei
| |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu schrieb: > Frank Benoit wrote: >> In DWT, I cast always first to Object. >> Java> if( intf1.equals(intf2) ){ >> D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){ > > I think in D the cast is inserted automatically. Walter? > > Andrei there is a related bug report http://d.puremagic.com/issues/show_bug.cgi?id=2794 | |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sun, 27 Sep 2009 10:32:29 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > Frank Benoit wrote: >> Andrei Alexandrescu schrieb: >>> Frank Benoit wrote: >>>> Andrei Alexandrescu schrieb: >>>>> Consider two objects a and b with a of class type. Currently, the >>>>> expression a == b is blindly rewritten as a.opEquals(b). I argue it >>>>> should be rewritten into a call to an (imaginary/inlined) function >>>>> equalObjects(a, b), with the following definition: >>>>> >>>>> bool equalObjects(T, U)(T a, U b) if (is(T == class)) >>>>> { >>>>> static if (is(U == class)) >>>>> { >>>>> if (b is null) return a is null; >>>>> if (a is null) return b is null; >>>>> } >>>>> else >>>>> { >>>>> enforce(a !is null); >>>>> } >>>>> return a.opEquals(b); >>>>> } >>>>> >>>>> This hoists the identity test outside the opEquals call and also deals >>>>> with null references. What do you think? >>>>> >>>>> >>>>> Andrei >>>> What about interfaces? >>> Good question! What do they do now? I ran this: >>> >>> interface A {} >>> class Widget : A {} >>> >>> void main() { >>> auto a = cast(A) new Widget; >>> A b = null; >>> writeln(a == b); >>> writeln(b == a); >>> } >>> >>> To my surprise, the program printed false twice. If I replace A with >>> Widget inside main, the program prints false then crashes with the >>> mythical segfault :o). >>> >>> So how are interfaces compared? >>> >>> >>> Andrei >> Hm, i would have expected it not to compile, because A does not have >> opEquals. >> In DWT, I cast always first to Object. >> Java> if( intf1.equals(intf2) ){ >> D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){ > > I think in D the cast is inserted automatically. Walter? From the assembly, it appears that the compiler is comparing the reference values directly. Which is not what you want, you want the opEquals from Object. -Steve | |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > Robert Jacques wrote: >> On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: >> >>> Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: >>> >>> bool equalObjects(T, U)(T a, U b) if (is(T == class)) >>> { >>> static if (is(U == class)) >>> { >>> if (b is null) return a is null; >>> if (a is null) return b is null; >>> } >>> else >>> { >>> enforce(a !is null); >>> } >>> return a.opEquals(b); >>> } >>> >>> This hoists the identity test outside the opEquals call and also deals with null references. What do you think? >>> >>> >>> Andrei >> I like this. I think optimizing away opEquals for identical objects would also be a good idea: >> static if (is(U == class)) >> if(a is b || a is null || b is null) return a is b; >> else >> enforce(a !is null); > > This code has an inefficiency, it seems, because it makes a bit more checks than necessary (e.g. checks a is b twice). Let's simplify: > > bool equalObjects(T, U)(T a, U b) if (is(T == class)) > { > static if (is(U == class)) > { > if (a is b) return true; > if (b is null || a is null) return false; > } > else > { > enforce(a !is null); > } > return a.opEquals(b); > } > > > Andrei Are the extra branch and return statement faster? Besides, I thought the optimizer would cache a is b: auto a_is_b = a is b; if (a_is_b || b is null || a is null) return a_is_b; | |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Robert Jacques | Robert Jacques wrote: > On Sun, 27 Sep 2009 09:11:38 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: > >> Robert Jacques wrote: >>> On Sat, 26 Sep 2009 21:32:13 -0400, Andrei Alexandrescu <SeeWebsiteForEmail@erdani.org> wrote: >>> >>>> Consider two objects a and b with a of class type. Currently, the expression a == b is blindly rewritten as a.opEquals(b). I argue it should be rewritten into a call to an (imaginary/inlined) function equalObjects(a, b), with the following definition: >>>> >>>> bool equalObjects(T, U)(T a, U b) if (is(T == class)) >>>> { >>>> static if (is(U == class)) >>>> { >>>> if (b is null) return a is null; >>>> if (a is null) return b is null; >>>> } >>>> else >>>> { >>>> enforce(a !is null); >>>> } >>>> return a.opEquals(b); >>>> } >>>> >>>> This hoists the identity test outside the opEquals call and also deals with null references. What do you think? >>>> >>>> >>>> Andrei >>> I like this. I think optimizing away opEquals for identical objects would also be a good idea: >>> static if (is(U == class)) >>> if(a is b || a is null || b is null) return a is b; >>> else >>> enforce(a !is null); >> >> This code has an inefficiency, it seems, because it makes a bit more checks than necessary (e.g. checks a is b twice). Let's simplify: >> >> bool equalObjects(T, U)(T a, U b) if (is(T == class)) >> { >> static if (is(U == class)) >> { >> if (a is b) return true; >> if (b is null || a is null) return false; >> } >> else >> { >> enforce(a !is null); >> } >> return a.opEquals(b); >> } >> >> >> Andrei > > Are the extra branch and return statement faster? Just as fast. Short-circuit evaluation also generates code with branches. > Besides, I thought the optimizer would cache a is b: > > auto a_is_b = a is b; > if (a_is_b || b is null || a is null) return a_is_b; I'm trying to not rely on such... Andrei | |||
September 27, 2009 Re: putting more smarts into a == b | ||||
|---|---|---|---|---|
| ||||
Posted in reply to Andrei Alexandrescu | Andrei Alexandrescu wrote:
> Frank Benoit wrote:
>> Andrei Alexandrescu schrieb:
>>> Frank Benoit wrote:
>>>> Andrei Alexandrescu schrieb:
>>>>> Consider two objects a and b with a of class type. Currently, the
>>>>> expression a == b is blindly rewritten as a.opEquals(b). I argue it
>>>>> should be rewritten into a call to an (imaginary/inlined) function
>>>>> equalObjects(a, b), with the following definition:
>>>>>
>>>>> bool equalObjects(T, U)(T a, U b) if (is(T == class))
>>>>> {
>>>>> static if (is(U == class))
>>>>> {
>>>>> if (b is null) return a is null;
>>>>> if (a is null) return b is null;
>>>>> }
>>>>> else
>>>>> {
>>>>> enforce(a !is null);
>>>>> }
>>>>> return a.opEquals(b);
>>>>> }
>>>>>
>>>>> This hoists the identity test outside the opEquals call and also deals
>>>>> with null references. What do you think?
>>>>>
>>>>>
>>>>> Andrei
>>>> What about interfaces?
>>> Good question! What do they do now? I ran this:
>>>
>>> interface A {}
>>> class Widget : A {}
>>>
>>> void main() {
>>> auto a = cast(A) new Widget;
>>> A b = null;
>>> writeln(a == b);
>>> writeln(b == a);
>>> }
>>>
>>> To my surprise, the program printed false twice. If I replace A with
>>> Widget inside main, the program prints false then crashes with the
>>> mythical segfault :o).
>>>
>>> So how are interfaces compared?
>>>
>>>
>>> Andrei
>>
>> Hm, i would have expected it not to compile, because A does not have
>> opEquals.
>>
>> In DWT, I cast always first to Object.
>> Java> if( intf1.equals(intf2) ){
>> D1.0> if( ((cast(Object)intf1).opEquals( cast(Object)intf2 )){
>
> I think in D the cast is inserted automatically. Walter?
>
> Andrei
Using the compile-time view of Descent, if I have this code:
---
interface I {
}
class C {
}
int main(char[][] args) {
C c = new C();
I i = null;
auto x = i == c;
auto y = c == i;
return 0;
}
---
the compiler turns it into:
---
interface I {
}
class C: Object {
}
int main(char[][] args) {
C c = new C;
I i = null;
int x = c.opEquals(cast(Object) i);
int y = c.opEquals(cast(Object) i);
return 0;
}
---
That's why it doesn't segfault.
Debugging the code it turns out the logic is very simple, it's just applying operator overloading: (for the first comparison) "opEquals" is searched in "I". Since "opEquals" is not found in it, the compiler checks if "opEquals" is commutative. It is, so it searches "opEquals" in "C", finds it and does the rewrite. (more or less)
| |||
Copyright © 1999-2021 by the D Language Foundation
Permalink
Reply