Skip to content

Commit

Permalink
Treat by-name closures specially in recheck
Browse files Browse the repository at this point in the history
A by-name Closure node, which is produced by phase ElimByName gets a target type to indicate
it's a contextual zero parameter closure. But for the purposes of rechecking and capture checking,
it needs to be treated like a function. In particular the type of the closure needs to be derived
from the result type of the anonymous function.

Fixes #21920
  • Loading branch information
odersky committed Nov 11, 2024
1 parent 4c8bd42 commit d34ce30
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 2 deletions.
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/ElimByName.scala
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,9 @@ class ElimByName extends MiniPhase, InfoTransformer:
Closure(meth,
_ => arg.changeOwnerAfter(ctx.owner, meth, thisPhase),
targetType = defn.ByNameFunction(argType)
// Note: this will forget any captures on the original by-name type
// But that's not a problem since we treat these closures specially
// anyway during recheck.
).withSpan(arg.span)

private def isByNameRef(tree: Tree)(using Context): Boolean =
Expand Down
4 changes: 4 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/Recheck.scala
Original file line number Diff line number Diff line change
Expand Up @@ -387,6 +387,10 @@ abstract class Recheck extends Phase, SymTransformer:
def recheckClosure(tree: Closure, pt: Type, forceDependent: Boolean = false)(using Context): Type =
if tree.tpt.isEmpty then
tree.meth.tpe.widen.toFunctionType(tree.meth.symbol.is(JavaDefined), alwaysDependent = forceDependent)
else if defn.isByNameFunction(tree.tpt.tpe) then
val mt @ MethodType(Nil) = tree.meth.tpe.widen: @unchecked
val cmt = ContextualMethodType(Nil, Nil, mt.resultType)
cmt.toFunctionType(alwaysDependent = forceDependent)
else
recheck(tree.tpt)

Expand Down
4 changes: 2 additions & 2 deletions tests/neg-custom-args/captures/byname.scala
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
class Cap extends caps.Capability

def test(cap1: Cap, cap2: Cap) =
def f() = if cap1 == cap1 then g else g // error
def f() = if cap1 == cap1 then g else g
def g(x: Int) = if cap2 == cap2 then 1 else x
def g2(x: Int) = if cap1 == cap1 then 1 else x
def f2() = if cap1 == cap1 then g2 else g2
def h(ff: => Int ->{cap2} Int) = ff
h(f())
h(f2())
h(f2()) // error

class I

Expand Down
10 changes: 10 additions & 0 deletions tests/neg-custom-args/captures/i21920.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/i21920.scala:34:34 ---------------------------------------
34 | val cell: Cell[File] = File.open(f => Cell(Seq(f))) // error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| 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`
36 changes: 36 additions & 0 deletions tests/neg-custom-args/captures/i21920.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
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))) // error
val file = cell.headIterator.next()
file.read()

0 comments on commit d34ce30

Please sign in to comment.