-
Notifications
You must be signed in to change notification settings - Fork 21
Conformance of type projections in SLS 3.5.2 violates expectations, conflicts with intersection types and isn't supported #10916
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
BTW, that SLS clause appears to be implemented rather closely by Scalac in |
where's the problem there? |
All that's true is that |
Here's an example where conformance would be wrong. Fortunately scalac doesn't follow the spec here, scala> object Foo { type T = Int } ; object Bar { type T = String }
defined object Foo
defined object Bar
scala> type FB = Foo.type with Bar.type
defined type alias FB
scala> def frob(ft: Foo.type#T): FB#T = ft
<console>:13: error: type mismatch;
found : Foo.T
(which expands to) Int
required: String
def frob(ft: Foo.type#T): FB#T = ft
|
Exactly, and incidentally this is how I think they should behave :) |
why? it obviously shouldn't; plus in that case |
@milessabin that's true and it is an issue, but since scala> def frob(bt: Bar.type#T): FB#T = bt
1 |def frob(bt: Bar.type#T): FB#T = bt
| ^
|FB is not a legal path
|since it has a member T with possibly conflicting bounds Int | String <: ... <: Int & String So I'd like to focus on "issues with this SLS rule for type projections that we'll keep allowing in Dotty". (Reposting with fixes) In the OP I've replaced "unsound" with "incorrect" to clarify (though I didn't mean to talk about type unsoundness). FWIW, that clause also appears problematic for another reason: it's false for any implementation that does eager dealiasing of type projections. Below it implies scala> class A { type T }
defined class A
scala> class B extends A { type T = Any }
defined class B
scala> def downcast(x: Any): A#T = x
<console>:12: error: type mismatch;
found : x.type (with underlying type Any)
required: A#T
def downcast(x: Any): A#T = x
^
scala> def downcast(x: Any): A#T = (x: B#T): A#T
<console>:13: error: type mismatch;
found : Any
required: A#T
def downcast(x: Any): A#T = (x: B#T): A#T
^ |
Fair enough, but then I think this discussion belongs on the Dotty issue. I don't see this being fixed in scalac any time soon. |
Uh right, it's only
That's indeed the Dotty semantics of @milessabin can your example be made to work then?
Ignoring the issues with your example, that might be true. Tho the only Scala spec currently lives here and I'm uneasy with hosting a diverging spec in Dotty. |
I don't quite see what |
Whoops, yes, back to front. But it's still an issue the right way around, scala> object Foo { type T = Int } ; object Bar { type T = String }
defined object Foo
defined object Bar
scala> type FB = Foo.type with Bar.type
defined type alias FB
scala> def frob(fbt: FB#T): Foo.type#T = fbt
<console>:13: error: type mismatch;
found : String
required: Foo.T
(which expands to) Int
def frob(fbt: FB#T): Foo.type#T = fbt
^ |
your |
Oh, fixed, I meant
First, I can make But thanks for explaining your reasoning. I used to think that too, but this is actually by design and I agree — Scalac was changed to allow it. Undefined type members are supposed to behave as existentials, and they'll be the only way to have existentials in Scala 3. |
I think we're talking at cross purposes here. I agree with you wrt Dotty, but given the semantics of |
That's true, and that's indeed one reason 3.5.2 can't be true for Scala 2. |
I see it the other way round: first, what's wrong at any rate is the implementation; otherwise, let's just definitely throw away the spec for good. But also, why is 3.5.2 wrong and not the semantics of |
Let's at least agree that 3.5.2 conflicts with the semantics of Whether or not that's that right thing to do in Dotty is a different question altogether. |
Yes, I'll have to figure this out for Dotty, and confirm the intended semantics of type projections. |
Looking at the problematic example in light of lampepfl/dotty-feature-requests#14 (comment), I'd now blame (EDITED): I'd suggest closing this issue, or focusing it on @milessabin valid complaints on intersections. |
The discussion here is long and difficult to follow. If someone thinks the intersections aspect deserves its own ticket, I invite y'all to open one that concisely focuses on that alone. |
Just noticed a likely bug in the SLS 3.5.2 on conformance, possibly relevant to #7278 — it says that
and that looks incorrect.
That's only correct for nested classes in an inheritance relation (scala/scala3#4361 (comment)), as when you emulate virtual classes in Scala; but it's wrong for nested classes in general, and it appears incorrect even for abstract types.
Take
T <: U
andT#t <: U#t
, then we can haveU#t >: S <: U1
andT#t = U1
, which leads us to the false conclusion thatU1 <: U#t
.What is true is that
T#t
's bounds must be at least as restrictive thanU#t
's, or, borrowing @sstucki's terminology,T#t
must have a interval kind that is a (non-strict) subkind ofU#t
's kind. That seems analogue to the rule for term-level members, after you shift one level up everything: the type of term memberT.method
must be a (non-strict) subtype ofU.method
's type.Proposed replacement
Quoting myself in scala/scala3#4583 (comment):
Proposed new clauses:
SLS 3.5.1:
SLS 3.5.2:
The text was updated successfully, but these errors were encountered: