www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Should "a is b" compile if a and b have unrelated classes?

reply Michael Coulombe <kirsybuu gmail.com> writes:
I had a bug in my code that was messing me up for a while, and it 
boiled down to an identity check between two Object references 
with unrelated static types, like below:

class A {}
class B {}
void main() {
     A a = new A;
     B b = new B;
     if (a is b) {} // compiles
}

I was surprised that the type system failed me here. It's true 
that A and B could be upcast to Object and then comparisons would 
make sense type-wise, but the comparison should never pass (and 
the compiler should know it won't since they are in separate 
inheritance subtrees) unless the programmer is intentionally 
breaking the type system.

Is there reasoning for this? If not, should it be a warning or 
error, as it is for example when comparing two pointers to 
structs of different types?
Apr 10 2018
next sibling parent Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, April 10, 2018 21:52:22 Michael Coulombe via Digitalmars-d-learn 
wrote:
 I had a bug in my code that was messing me up for a while, and it
 boiled down to an identity check between two Object references
 with unrelated static types, like below:

 class A {}
 class B {}
 void main() {
      A a = new A;
      B b = new B;
      if (a is b) {} // compiles
 }

 I was surprised that the type system failed me here. It's true
 that A and B could be upcast to Object and then comparisons would
 make sense type-wise, but the comparison should never pass (and
 the compiler should know it won't since they are in separate
 inheritance subtrees) unless the programmer is intentionally
 breaking the type system.

 Is there reasoning for this? If not, should it be a warning or
 error, as it is for example when comparing two pointers to
 structs of different types?
Well, the compiler can't simply say that they're different classes and refuse to compare their references. It could look at the class hierarchy and determine that there's no way that the two are the same reference, because neither A nor B is a subclass of the other and thus treat it as an error - which I gather is what you're suggesting it should do. However, because doing that is more complicated, I suspect that it just doesn't bother. With struct pointers, it can at least see at a glance that one can't be the other, whereas it has to do a lot more examination of the type if it's class references. It's probably a reasonable enhancement request, though I'm not sure that it really matters much. One potential issue is that since opEquals is unfortunately part of Object, it's perfectly legal to do if(a == b) even though a and b can never be equal, so if is were disallowed in this case, then we'd get a situation where a == b compiles but a is b doesn't which would be a bit weird - though presumably, if we do end up going with some form of Andrei's ProtoObject proposal where we get a new root class beneath Object, then we'd definitely have classes that could be compared with is but not ==. So, that may not be all that big a deal as weird as it may be. My bigger concern would be how it would interact with generic code. It's not uncommon that stuff that at first glance seems like it should be disallowed would actually cause a fair but of grief in generic code (e.g. that's at least part of why it's usually the case that function attributes that don't apply are just ignored rather than treated as errors). If it definitely wasn't going to cause problems for generic code, then such a change would probably be reasonable, but I have no idea how likely it would be to cause problems in generic code. So, maybe? You can certainly open an enhancement request about it. - Jonathan M Davis
Apr 10 2018
prev sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
On 4/10/18 5:52 PM, Michael Coulombe wrote:
 I had a bug in my code that was messing me up for a while, and it boiled 
 down to an identity check between two Object references with unrelated 
 static types, like below:
 
 class A {}
 class B {}
 void main() {
      A a = new A;
      B b = new B;
      if (a is b) {} // compiles
 }
 
 I was surprised that the type system failed me here. It's true that A 
 and B could be upcast to Object and then comparisons would make sense 
 type-wise, but the comparison should never pass (and the compiler should 
 know it won't since they are in separate inheritance subtrees) unless 
 the programmer is intentionally breaking the type system.
 
 Is there reasoning for this? If not, should it be a warning or error, as 
 it is for example when comparing two pointers to structs of different 
 types?
Probably the reason is laziness. Sure it could be outlawed. But `is` has a special connotation of "identity", meaning it's EXACTLY the same. Let's look at what does compile: https://run.dlang.io/is/KHcHCc So there is at least some type checking for `is`, when it comes to pointers. There's definitely a precedent for this. The question is, should it just fold to "false", or should it fail to compile? If we change it to an error, there may be some unintentional code breakage out there. -Steve
Apr 10 2018