Bug 18541 – comparison `==` of two typeid() should always be rewritten as a "is"

Status
RESOLVED
Resolution
WONTFIX
Severity
enhancement
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2018-03-01T11:12:44Z
Last change time
2018-03-06T22:06:27Z
Assigned to
No Owner
Creator
Basile B.

Comments

Comment #0 by b2.temp — 2018-03-01T11:12:44Z
I cant see why anyone would want a full bit per bit comparison of the result of two `typeid()` since `typeid()` returns static instances, shared for each stuff of a given type. The idea is to have an AST rewrite of `typeid(T1) == typeid(T2)` as `typeid(T1) is typeid(T2)` which is compiled as faster code.
Comment #1 by schveiguy — 2018-03-01T12:17:05Z
I thought in the cases of DLLs, the typeids from different object files could be identical, but different instances.
Comment #2 by b2.temp — 2018-03-01T12:26:40Z
(In reply to Steven Schveighoffer from comment #1) > I thought in the cases of DLLs, the typeids from different object files > could be identical, but different instances. Nobody has said you're wrong. It's even a case where the lowering is invalid.
Comment #3 by b2.temp — 2018-03-01T12:28:12Z
With this dll stuff it's hardly applicable...
Comment #4 by schveiguy — 2018-03-01T12:34:31Z
All I'm saying is that I don't think we need to enforce this. There are reasons to use ==.
Comment #5 by schveiguy — 2018-03-01T12:35:08Z
Note that == will shortcut and return true if the two instances are the same object.
Comment #6 by b2.temp — 2018-03-02T11:00:45Z
(In reply to Steven Schveighoffer from comment #5) > Note that == will shortcut and return true if the two instances are the same > object. With someone yesterday we have verified that it's really not the case. In a specific hotspot, using "==" instead of "is" made something running in 7 minutes instead of 4 ! This is exactly why i opened this issue but unfortunately i understand now that it's not applicable, we just take care to use "is".
Comment #7 by issues.dlang — 2018-03-02T11:13:33Z
(In reply to Basile B. from comment #6) > (In reply to Steven Schveighoffer from comment #5) > > Note that == will shortcut and return true if the two instances are the same > > object. > > With someone yesterday we have verified that it's really not the case. In a > specific hotspot, using "==" instead of "is" made something running in 7 > minutes instead of 4 ! > > This is exactly why i opened this issue but unfortunately i understand now > that it's not applicable, we just take care to use "is". The very first thing in the implementation of the free function opEquals is if (lhs is rhs) return true; So, it most definitely shortcuts. Now, that doesn't mean that it's as efficient, since unless it's inlined, you're calling a function that isn't called if you use is directly, but the shortcut definitely happens.
Comment #8 by schveiguy — 2018-03-02T14:03:53Z
Yeah, there is no inlining of it. There is a bit of magic in the compiler for Object == Object, and I think that magic is stopping the inlining. Of course, the compiler magic could be made to simply do the `is` comparison first, and then call the object function (we can take `lhs is rhs` out of the function itself). I think this would be a worthy enhancement request, especially considering the performance benefits. I'm not sure how keen someone is to delve into updating that code, it's kind of a mess.
Comment #9 by ketmar — 2018-03-06T22:06:27Z
compiler transforms `==` for objects to virtual call, and dmd cannot devirtualize calls (yet? ;-). so yeah, no inlining. it is quite possible to rewrite the call into `e1 is e1 || .object.opEquals(e1, e2)`, tho. --- a/src/ddmd/opover.d +++ b/src/ddmd/opover.d @@ -30,6 +30,7 @@ import ddmd.globals; import ddmd.id; import ddmd.identifier; import ddmd.mtype; +import ddmd.sideeffect; import ddmd.statement; import ddmd.tokens; import ddmd.visitor; @@ -1162,7 +1163,7 @@ extern (C++) Expression op_overload(Expression e, Scope* sc) if (!(cd1.cpp || cd2.cpp)) { /* Rewrite as: - * .object.opEquals(e1, e2) + * e1 is e2 || .object.opEquals(e1, e2) */ Expression e1x = e.e1; Expression e2x = e.e2; @@ -1176,12 +1177,38 @@ extern (C++) Expression op_overload(Expression e, Scope* sc) if (cd2.isInterfaceDeclaration()) e2x = new CastExp(e.loc, e.e2, t2.isMutable() ? to : to.constOf()); + /* create temporaries, to avoid invalid rewriting of this: + * if (arr[i++] is obj || .object.opEquals(arr[i++], obj) + * rewrite to: + * tmp1 = e1x, tmp2 = e2x, then use temps + */ + // load e1x to temporary + auto tmp1 = copyToTemp(STCnodtor, "__opEqualsTmp1", e1x); + auto e1xtmp = new CommaExp(e.loc, new DeclarationExp(e.loc, tmp1), new VarExp(e.loc, tmp1)); + + // load e2x to temporary + auto tmp2 = copyToTemp(STCnodtor, "__opEqualsTmp2", e2x); + auto e2xtmp = new CommaExp(e.loc, new DeclarationExp(e.loc, tmp2), new VarExp(e.loc, tmp2)); + + // e1 is e2 + auto expis = new IdentityExp(TOKidentity, e.loc, e1xtmp, e2xtmp); + + // and use fresh varexps with temps (previous ones can be changed by semanticing) + e1x = new VarExp(e.loc, tmp1); + e2x = new VarExp(e.loc, tmp2); + + // .object.opEquals(e1, e2) result = new IdentifierExp(e.loc, Id.empty); result = new DotIdExp(e.loc, result, Id.object); result = new DotIdExp(e.loc, result, Id.eq); result = new CallExp(e.loc, result, e1x, e2x); + + // expis || result + result = new LogicalExp(e.loc, TOKoror, expis, result); + if (e.op == TOKnotequal) result = new NotExp(e.loc, result); + result = result.semantic(sc); return; }