Closed
Description
Compiler version
3.6.1, PR
Minimized example
import language.experimental.captureChecking
trait Iterator[+A] extends IterableOnce[A]:
self: Iterator[A]^ =>
def next(): A
trait IterableOnce[+A] extends Any:
def iterator: Iterator[A]^{this}
final class Cell[A](head: => IterableOnce[A]^):
def headIterator: Iterator[A]^{this} = head.iterator
class File private ():
private var closed = false
def close() = closed = true
def read() =
assert(!closed, "File closed")
1
object File:
def open[T](f: File^ => T): T =
val file = File()
try
f(file)
finally
file.close()
object Seq:
def apply[A](xs: A*): IterableOnce[A] = ???
@main def Main() =
val cell: Cell[File] = File.open(f => Cell(Seq(f)))
val file = cell.headIterator.next()
file.read()
Output
compiles
Expectation
Should give an error, as in the slightly changed version where we replace by-name with explicit functions:
import language.experimental.captureChecking
trait Iterator[+A] extends IterableOnce[A]:
self: Iterator[A]^ =>
def next(): A
trait IterableOnce[+A] extends Any:
def iterator: Iterator[A]^{this}
final class Cell[A](head: () => IterableOnce[A]^):
def headIterator: Iterator[A]^{this} = head().iterator
class File private ():
private var closed = false
def close() = closed = true
def read() =
assert(!closed, "File closed")
1
object File:
def open[T](f: File^ => T): T =
val file = File()
try
f(file)
finally
file.close()
object Seq:
def apply[A](xs: A*): IterableOnce[A] = ???
@main def Main() =
val cell: Cell[File] = File.open(f => Cell(() => Seq(f)))
val file = cell.headIterator.next()
file.read()
This gives:
-- [E007] Type Mismatch Error: unsound-byname-2.scala:34:34 --------------------
34 | val cell: Cell[File] = File.open(f => Cell(() => Seq(f)))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|Found: Cell[box File^{f, f²}]{val head: () ->? IterableOnce[box File^{f, f²}]^?}^?
|Required: Cell[File]
|
|where: f is a reference to a value parameter
| f² is a reference to a value parameter
|
| longer explanation available when compiling with `-explain`
1 error found
I searched for a long time without getting to the root cause. What I did:
I compiled both programs with the first called unsound-byname.scala
and the second called unsound-byname-2.scala
with the following commands:
scc unsound-byname.scala -Ycc-log -color:never >& x
scc unsound-byname-2.scala -Ycc-log -color:never >& y
I then diffed the test output. There were several rabbit holes which were unproductive.
- The second program expanded a PolyType
[R] -> () -> R
during setup while the first did not. It turned out this was because the second program uses an alias type while the first used the alias directly. - The target type of the by-name closure had different capture sets. I fixed that but it turned out it did not matter since the atrget type is an inferred type, so all capture sets are erased.
The one difference that remained was that the unsound program unsound-byname.scala
produced several error messages of the form
existential match not found for ...
where the other one did not. Something is inconsistent with existentials and that leads to errors being suppressed. I tried to dig deeper but could not find anything conclusive.