Bug 3543 – [tdpl] ternary operator can't find common type for classes/interfaces

Status
RESOLVED
Resolution
FIXED
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D1 (retired)
Platform
Other
OS
All
Creation time
2009-11-23T03:24:15Z
Last change time
2024-02-07T10:36:21Z
Keywords
pull, rejects-valid, TDPL
Assigned to
No Owner
Creator
nfxjfg
See also
https://issues.dlang.org/show_bug.cgi?id=24178

Comments

Comment #0 by nfxjfg — 2009-11-23T03:24:15Z
The following fails to compile, but I think it should be allowed: interface Root { } interface A : Root { } interface B : Root { } bool bla; void main() { A a; B b; Root r; //this works r = a; r = b; //this line fails, although there's an unambiguous common type "Root" r = bla ? a : b; } If the compiler rejects this intentionally, this bug report is a feature request.
Comment #1 by nfxjfg — 2009-11-23T03:27:52Z
This also fails if you're doing this with classes (just replace "interface" with "class" in the example above). With classes, this should definitely work, but it fails.
Comment #2 by nfxjfg — 2009-12-26T10:47:29Z
Note how this makes array literal type inference suck in dmd 2.037: class Root { } class A : Root { } class B : Root { } bool bla; void main() { A a; B b; Root t; t = a; t = b; //works //Error: cannot implicitly convert expression (a) of type fgd.Root to fgd.B Root[] t2 = [a, b]; } If this bug gets fixed, array literal type inference in D2 will be improved.
Comment #3 by nfxjfg — 2010-08-28T16:42:33Z
*** Issue 4030 has been marked as a duplicate of this issue. ***
Comment #4 by simen.kjaras — 2010-11-02T11:30:21Z
*** Issue 5156 has been marked as a duplicate of this issue. ***
Comment #5 by bugzilla — 2011-04-05T21:21:03Z
(In reply to comment #1) > This also fails if you're doing this with classes (just replace "interface" > with "class" in the example above). > With classes, this should definitely work, but it fails. It works with classes when I try it.
Comment #6 by bugzilla — 2011-04-05T21:39:01Z
I'm going to mark this as an enhancement. When the inheritance graph is non-trivial, I don't think it's so obvious which, if any, of the ancestors should be picked. Perhaps the user should decide. So I'm skeptical this is a good idea. Meanwhile, it does work for classes, which make sense because classes are single inheritance. A common root, if there is one, would be only one.
Comment #7 by andrei — 2011-04-05T22:51:07Z
Interface hierarchies for a single-rooted DAG. In the DAG there are one or more common ancestors for any pair of nodes. The most specific one (farthest from the root) must be chosen. If there are two or more at the same depth that's an ambiguity error.
Comment #8 by bugzilla — 2011-04-06T10:51:03Z
(In reply to comment #7) > Interface hierarchies for a single-rooted DAG. In the DAG there are one or more > common ancestors for any pair of nodes. The most specific one (farthest from > the root) must be chosen. If there are two or more at the same depth that's an > ambiguity error. Interface hierarchies are not necessarily single rooted. They are multiply inherited.
Comment #9 by andrei — 2011-04-06T12:44:13Z
One can always connect an imaginary root to all rootless interfaces and apply the rule I mentioned. Let me add that "?:" is a cornerstone operator for a lot of traits and type deduction paraphernalia (e.g. std.traits.CommonType). I wouldn't insist in the matter if it weren't important beyond the convenience of "?:" itself.
Comment #10 by bugzilla — 2011-04-06T13:10:03Z
(In reply to comment #9) > One can always connect an imaginary root to all rootless interfaces and apply > the rule I mentioned. Let me add that "?:" is a cornerstone operator for a lot > of traits and type deduction paraphernalia (e.g. std.traits.CommonType). I > wouldn't insist in the matter if it weren't important beyond the convenience of > "?:" itself. I agree about the importance of the ?: algorithm. But I wonder if the compiler picking a common ancestor from a complex inheritance graph is really a good thing - it may prove surprising to the user. For example: interface A : B,C,D,E interface X : M,N,O,C Is it really the right idea to pick C for the common type? I'm skeptical.
Comment #11 by andrei — 2011-04-06T13:26:45Z
(In reply to comment #10) > (In reply to comment #9) > > One can always connect an imaginary root to all rootless interfaces and apply > > the rule I mentioned. Let me add that "?:" is a cornerstone operator for a lot > > of traits and type deduction paraphernalia (e.g. std.traits.CommonType). I > > wouldn't insist in the matter if it weren't important beyond the convenience of > > "?:" itself. > > I agree about the importance of the ?: algorithm. But I wonder if the compiler > picking a common ancestor from a complex inheritance graph is really a good > thing - it may prove surprising to the user. For example: > > interface A : B,C,D,E > interface X : M,N,O,C > > Is it really the right idea to pick C for the common type? I'm skeptical. Yes, in this case D is unequivocally the common type. A more interesting example is: interface A {} interface B {} interface C : B {} interface X : A, C {} interface Y : A, C {} By my suggested rule, C would be the common type as it's the most specific (most remote from a root). At the same time both A and C are equally distanced from X and Y so it's legitimate to ask on why A is not an equally good fit (and therefore an ambiguity). A rule that would avoid that issue would be to count all common ancestors and find the one that is closest to both, i.e. shortest sum of steps from both interfaces. I thought of it but eliminated it because it can create rather subtle distinctions. I agree the problem is not as clear cut as we'd like. OP?
Comment #12 by bearophile_hugs — 2011-04-06T13:44:34Z
Even modifying :? just for class instances will be a good improvement, this is not accepted by dmd 2.052: class A {} class B : A {} class C : A {} void main() { B b = new B; C c = new C; A[] array = [b, c]; }
Comment #13 by hsteoh — 2013-12-21T19:21:28Z
Related: issue #5498.
Comment #14 by schveiguy — 2014-04-22T17:32:36Z
(In reply to Andrei Alexandrescu from comment #11) > A rule that would avoid that issue would be to count all common ancestors > and find the one that is closest to both, i.e. shortest sum of steps from > both interfaces. I thought of it but eliminated it because it can create > rather subtle distinctions. > > I agree the problem is not as clear cut as we'd like. It's pretty clear cut to me: 1. If you have a single common root, that is it. Any roots that are possibilities only because they are ancestors of another root do not count. 2. If you have multiple common roots, it's ambiguous, a cast is required. Note, this has to be considered: Currently in D, although it is actually impossible to have a non-D object implement a D interface, interfaces are not derived from Object. So technically, Object would always be a root separate from any interface, which would make virtually any expression that involves interfaces ambiguous. I think in at LEAST this context (though I would argue for complete acceptance of interfaces always deriving from Object), Object should be considered a root of all interfaces, thereby allowing interfaces to validly be the result. Note, I would make also an exception for literal array expressions to take the usage into consideration. Currently, this does not work, but I think it should: interface A {} class B : A {} class C : B {} class D : B {} void main() { wstring ws = "hello"; // ok, compiler knows the literal should be typed as // wstring. A[] a = [new C, new D]; // error, [new C, new D] is typed as B[] }
Comment #15 by bearophile_hugs — 2014-07-17T17:15:22Z
The same problem found by "Uranuz": http://forum.dlang.org/thread/[email protected] interface IBase {} class Impl(T): IBase { T value; } void main() { IBase a = true ? (new Impl!uint) : (new Impl!string); } test.d(6,23): Error: cannot implicitly convert expression (new Impl!uint) of type object.Object to test.IBase
Comment #16 by dlang-bot — 2024-01-17T06:50:34Z
@WalterBright updated dlang/dmd pull request #16051 "fix Issue 3543 - [tdpl] ternary operator can't find common type for c…" fixing this issue: - fix Bugzilla Issue 3543 - [tdpl] ternary operator can't find common type for classes/interfaces https://github.com/dlang/dmd/pull/16051
Comment #17 by dlang-bot — 2024-02-07T10:36:21Z
dlang/dmd pull request #16051 "fix Bugzilla Issue 3543 - [tdpl] ternary operator can't find common type for c…" was merged into master: - 7f613ea4ca4cfa7d57848ab7c26d357a9d71a93a by Walter Bright: fix Bugzilla Issue 3543 - [tdpl] ternary operator can't find common type for classes/interfaces https://github.com/dlang/dmd/pull/16051