Skip to content

Commit 0397de1

Browse files
authored
Fix dependent function setup for capture checking (#16542)
Fixes #15925. This issue is caused by a bug in `mapInferred` during CC `Setup`. In this function we convert all top-level non-dependent functions to its dependent counterpart. However, it does not correctly handle the case when the type is an alias to function types. The problematic case in the pattern matching: ```scala case tp @ AppliedType(tycon, args) => val tycon1 = this(tycon) if defn.isNonRefinedFunction(tp) then // Convert toplevel generic function types to dependent functions val args0 = args.init var res0 = args.last val args1 = mapNested(args0) val res1 = this(res0) // ... do the conversion ``` It retrieves the argument and the result type of the function directly from the applied type arguments. This works for function classes. However, if the type here is a type alias to the function, e.g. `type Lazy[X] = Unit => X`, it is still recognized as a function (since `defn.isNonRefinedFunction` does dealiasing) but it is incorrect to get the argument and result type of the function from the type parameter list here (which is just `X :: Nil`). To fix this, we will dealias the type and recurse when it is an alias to the function type.
2 parents 3b3e33b + 96e4c74 commit 0397de1

File tree

2 files changed

+28
-10
lines changed

2 files changed

+28
-10
lines changed

compiler/src/dotty/tools/dotc/cc/Setup.scala

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -212,17 +212,22 @@ extends tpd.TreeTraverser:
212212
val tycon1 = this(tycon)
213213
if defn.isNonRefinedFunction(tp) then
214214
// Convert toplevel generic function types to dependent functions
215-
val args0 = args.init
216-
var res0 = args.last
217-
val args1 = mapNested(args0)
218-
val res1 = this(res0)
219-
if isTopLevel then
220-
depFun(tycon1, args1, res1)
221-
.showing(i"add function refinement $tp --> $result", capt)
222-
else if (tycon1 eq tycon) && (args1 eq args0) && (res1 eq res0) then
223-
tp
215+
if !defn.isFunctionSymbol(tp.typeSymbol) && (tp.dealias ne tp) then
216+
// This type is a function after dealiasing, so we dealias and recurse.
217+
// See #15925.
218+
this(tp.dealias)
224219
else
225-
tp.derivedAppliedType(tycon1, args1 :+ res1)
220+
val args0 = args.init
221+
var res0 = args.last
222+
val args1 = mapNested(args0)
223+
val res1 = this(res0)
224+
if isTopLevel then
225+
depFun(tycon1, args1, res1)
226+
.showing(i"add function refinement $tp ($tycon1, $args1, $res1) (${tp.dealias}) --> $result", capt)
227+
else if (tycon1 eq tycon) && (args1 eq args0) && (res1 eq res0) then
228+
tp
229+
else
230+
tp.derivedAppliedType(tycon1, args1 :+ res1)
226231
else
227232
tp.derivedAppliedType(tycon1, args.mapConserve(arg => this(arg)))
228233
case tp @ RefinedType(core, rname, rinfo) if defn.isFunctionType(tp) =>
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import language.experimental.captureChecking
2+
3+
class Unit
4+
object unit extends Unit
5+
6+
type Foo[X] = [T] -> (op: X => T) -> T
7+
type Lazy[X] = Unit => X
8+
9+
def force[X](fx: Foo[Lazy[X]]): X =
10+
fx[X](f => f(unit)) // error
11+
12+
def force2[X](fx: Foo[Unit => X]): X =
13+
fx[X](f => f(unit)) // error

0 commit comments

Comments
 (0)