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
Type members are shadowed by self type's type members #7255
Comments
Imported From: https://issues.scala-lang.org/browse/SI-7255?orig=1 |
@paulp said: trait A { type C }
trait B { this: A =>
type C <: { def foo }
(??? : C).foo
} |
@paulp said: Abstract type members aren't unified as normally implied by the word; they can only be refined. What that means in this context is that a type is created which is the intersection of B with its self type, that is, B with A. The intersection type is created assuming it can just use the last 'C' in the linearization, since the compile will fail in refchecks unless the last 'C' is a refinement of any earlier 'C's. This system breaks down with self types, because a class sees itself as having the intersection of the declared class and the declared self type. Either X with Y or Y with X might be valid instantiations; there is no way a priori to know which is the "last" member C. // Declare this way, one can create both new A with B and new B with A
trait A { self: B => type M }
trait B { self: A => type M }
// Given some constraints, it can only be created in one direction
trait A { self: B => type M <: AnyRef }
trait B { self: A => type M <: String }
scala> new A with B { }
res2: A with B = $anon$1@3e59503b
scala> new B with A { }
<console>:13: error: overriding type M in trait B with bounds <: String;
type M in trait A with bounds <: AnyRef has incompatible type
new B with A { }
^ ...but that analysis hasn't taken place yet, and performing that analysis during typing would be a huge change. So it uses the last C it finds, which (at random) is the self type. It could as easily have been the class, and if I switch them, your example compiles: --- i/src/compiler/scala/tools/nsc/typechecker/Namers.scala
+++ w/src/compiler/scala/tools/nsc/typechecker/Namers.scala
@@ -773,7 +773,7 @@ trait Namers extends MethodSynthesis {
val selftpe = typer.typedType(tree).tpe
sym setInfo {
if (selftpe.typeSymbol isNonBottomSubClass sym.owner) selftpe
- else intersectionType(List(sym.owner.tpe, selftpe))
+ else intersectionType(List(selftpe, sym.owner.tpe))
}
} ...except of course that would mean this modified example, which does compile right now, would turn into an error. trait A {
type C <: D
trait D { def foo: Unit }
}
trait B { this: A =>
type C <: Any
def bar(c: C) = c.foo
} tl;dr it should definitely be fixed, but it may require nontrivial changes to language, specification, and/or implementation. |
@paulp said: |
@odersky said: |
@retronym said: Workaround is to explicitly include the enclosing class in the self type so as to be able to control the linearization order: trait A { type C <: Any }
trait B { this: A with B =>
type C <: D
trait D { def foo: Unit }
def bar(c: C) = c.foo
}
|
The following code
produces the error:
Apparently C in B's self type A shadows B.C instead of getting unified with it. This compiles if A.C is removed or if B extends A.
The text was updated successfully, but these errors were encountered: