Skip to content

Reducing open types is potentially unsafe #2771

Closed
@sstucki

Description

@sstucki

The following currently causes a stack overflow in dotty (it's rejected by scalac):

trait A { type L[X] }
trait B { type L }
trait C { type M <: A }
trait D { type M >: B }

object Test {
  def test(x: C with D): Unit = {
    def f(y: x.M)(z: y.L[y.L]) = z
    f(new B { type L[F[_]] = F[F] })(1)
  }
}

The overflow is caused by the compiler attempting to evaluate the non-terminating type expression y.L[y.L] which is ill-kinded.

The type definition type L[F[_]] = F[F] } is clearly ill-kinded already and scalac actually reports this.

But the deeper issue is in the line above that definition: the type y.L has two conflicting kinds, it is both a type operator and a proper type. This is because L is declared as a type operator in trait A and as a proper type in trait B, which are the upper and lower bounds of x.M, respectively. So we can construct an instance y of B where y.L is a proper type and then apply it to some other type by up-casting y to x.M. Here's a variant of the example which illustrates that point more clearly:

//...
object Test {
  def test(x: C with D): Unit = {
    def f(y: x.M)(z: y.L[Any]) = z  // <-- y.L used as a type operator
    f(new B { type L = Any })(1)    // <-- y.L defined as a proper type
  }
}

It is rejected with the rather absurd error message

-- [E007] Type Mismatch Error:
 9 |		f(new B { type L = Any })(1)
   |		                          ^
   |		                          found:    Int(1)
   |		                          required: Any[Any]

Changing the type of z to y.L results instead in

-- [E055] Syntax Error:
 8 |    def f(y: x.M)(z: y.L) = z
   |                     ^^^
   |                     missing type parameter for y.L

This is another instance of a know problem about type members with absurd bounds (see also issue #50): evaluation is unsafe in a context containing variables/values (here x) of a type (here C & D) containing type members with absurd bounds (here x.M). In this particular case, the unsafe evaluation happens in an open type (namely y.L[Any]) rather than an open term, and at compile time (rather than at runtime).

Clarification: by "open type" I mean a type that contains free type or term variables (as opposed to a closed type which contains no free variables).

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions