Type bounds on methods not enforced

111 Views Asked by At

I can't comprehend why the compiler allows for the current code.

I use phantom types to protect access to methods. Only under specific "states" should methods be allowed to be called.

In most scenarios, this invariant is indeed verified by the compilation. Sometimes however, the compiler just ignores the constraint imposed by the phantom type. This feels like a major bug. What am I not understanding?

I tried to simplify the problem as much as possible. My use case is more complex:

class Door[State <: DoorState] private { 
    def doorKey: Int = 1
    def open[Phatom >: State <: Closed.type]: Door[Open.type] = new Door[Open.type]
    def close[Phatom >: State <: Open.type]: Door[Closed.type] = new Door[Closed.type]
}

object Door {
    def applyOpenDoor: Door[Open.type] = new Door[Open.type]
    def applyClosedDoor: Door[Closed.type] = new Door[Closed.type]
}

sealed trait DoorState
case object Closed extends DoorState
case object Open extends DoorState

Then

val aClosedDoor = Door.applyClosedDoor
val res1 = aClosedDoor.close                       // does not compile. Good!
val res2 = aClosedDoor.open.close.close            // does not compile. Good!
println(aClosedDoor.open.close.close)              // does not compile. Good!
println(aClosedDoor.open.close.close.doorKey)      // does not compile. Good!
aClosedDoor.open.close.close.doorKey == 1          // does not compile. Good!
println(aClosedDoor.open.close.close.doorKey == 1) // compiles! WTF?

As you can see above, the client can close a closed door. In my library, the corresponding behaviour is throwing a runtime exception. (I was confident the exception was well protected and impossible to reach)

I have only been able to replicate this problem for expressions returning boolean and when this is the argument to a function (in the example, println)

I am not looking for alternative solutions as much as I am looking for an explanation as to how this can happen? Don't you agree this is a considerable flaw? Or maybe I am missing something?

Scala version: 2.13.5

Edit
After a discussion on gitter, I opened bug request @ https://github.com/scala/bug
The problem does not seem to occur in scala 3.

1

There are 1 best solutions below

2
Mario Galic On

Seems it is an bug related to usage of nullary method

def doorKey: Int = 1

if instead it is defined as nilary method

def doorKey(): Int = 1

then it works as expected

println(aClosedDoor.open.close.close.doorKey() == 1) // compiler error