Skip to content

Capture checker triggers AssertionError on function application #15925

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Linyxus opened this issue Aug 27, 2022 · 1 comment · Fixed by #16542
Closed

Capture checker triggers AssertionError on function application #15925

Linyxus opened this issue Aug 27, 2022 · 1 comment · Fixed by #16542
Assignees
Labels
cc-experiment Intended to be merged with cc-experiment branch on origin itype:bug
Milestone

Comments

@Linyxus
Copy link
Contributor

Linyxus commented Aug 27, 2022

Compiler version

cc-experiment

Minimized code

class Unit
object unit extends Unit

type Foo[X] = [T] -> (op: X => T) -> T
type Lazy[X] = {*} Unit -> X

def force[X](fx: Foo[Lazy[X]]): X =
  fx[X](f => f(unit))

Output

exception occurred while compiling issues/funargs-minimised.scala
java.lang.AssertionError: assertion failed while compiling issues/funargs-minimised.scala
Exception in thread "main" java.lang.AssertionError: assertion failed
	at scala.runtime.Scala3RunTime$.assertFailed(Scala3RunTime.scala:11)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckApply(Recheck.scala:203)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckApply(CheckCaptures.scala:306)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckUnnamed$1(Recheck.scala:367)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:390)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckDefDef(Recheck.scala:173)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckDefDef(CheckCaptures.scala:443)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckDef(Recheck.scala:334)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.checkUnit$$anonfun$1$$anonfun$1(CheckCaptures.scala:662)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at dotty.tools.dotc.cc.Setup$$anon$4.complete(Setup.scala:433)
	at dotty.tools.dotc.core.SymDenotations$SymDenotation.completeFrom(SymDenotations.scala:173)
	at dotty.tools.dotc.core.Denotations$Denotation.completeInfo$1(Denotations.scala:187)
	at dotty.tools.dotc.core.Denotations$Denotation.info(Denotations.scala:189)
	at dotty.tools.dotc.core.SymDenotations$SymDenotation.ensureCompleted(SymDenotations.scala:380)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckNamed$1(Recheck.scala:353)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:389)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats$$anonfun$1(Recheck.scala:328)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats(Recheck.scala:328)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckBlock(Recheck.scala:240)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckBlock(Recheck.scala:248)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckBlock(CheckCaptures.scala:424)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckUnnamed$1(Recheck.scala:372)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:390)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckArgs$1(Recheck.scala:209)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckApply(Recheck.scala:218)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckApply(CheckCaptures.scala:306)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckUnnamed$1(Recheck.scala:367)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:390)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckDefDef(Recheck.scala:173)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckDefDef(CheckCaptures.scala:443)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckDef(Recheck.scala:334)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckNamed$1(Recheck.scala:355)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:389)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats$$anonfun$1(Recheck.scala:328)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats(Recheck.scala:328)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckClassDef(Recheck.scala:183)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheckClassDef(CheckCaptures.scala:464)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckNamed$1(Recheck.scala:361)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:389)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats$$anonfun$1(Recheck.scala:328)
	at scala.collection.immutable.List.foreach(List.scala:333)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStats(Recheck.scala:328)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckPackageDef(Recheck.scala:324)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckUnnamed$1(Recheck.scala:384)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheckStart(Recheck.scala:390)
	at dotty.tools.dotc.transform.Recheck$Rechecker.recheck(Recheck.scala:406)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.recheck(CheckCaptures.scala:509)
	at dotty.tools.dotc.transform.Recheck$Rechecker.checkUnit(Recheck.scala:441)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.checkUnit$$anonfun$2(CheckCaptures.scala:665)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at dotty.tools.dotc.cc.CaptureSet$.withCaptureSetsExplained(CaptureSet.scala:897)
	at dotty.tools.dotc.cc.CheckCaptures$CaptureChecker.checkUnit(CheckCaptures.scala:670)
	at dotty.tools.dotc.transform.Recheck.run(Recheck.scala:111)
	at dotty.tools.dotc.cc.CheckCaptures.run(CheckCaptures.scala:134)
	at dotty.tools.dotc.core.Phases$Phase.runOn$$anonfun$1(Phases.scala:316)
	at scala.collection.immutable.List.map(List.scala:246)
	at dotty.tools.dotc.core.Phases$Phase.runOn(Phases.scala:317)
	at dotty.tools.dotc.Run.runPhases$1$$anonfun$1(Run.scala:233)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:15)
	at scala.runtime.function.JProcedure1.apply(JProcedure1.java:10)
	at scala.collection.ArrayOps$.foreach$extension(ArrayOps.scala:1328)
	at dotty.tools.dotc.Run.runPhases$1(Run.scala:244)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$1(Run.scala:252)
	at dotty.tools.dotc.Run.compileUnits$$anonfun$adapted$1(Run.scala:261)
	at dotty.tools.dotc.util.Stats$.maybeMonitored(Stats.scala:68)
	at dotty.tools.dotc.Run.compileUnits(Run.scala:261)
	at dotty.tools.dotc.Run.compileSources(Run.scala:185)
	at dotty.tools.dotc.Run.compile(Run.scala:169)
	at dotty.tools.dotc.Driver.doCompile(Driver.scala:35)
	at dotty.tools.dotc.Driver.process(Driver.scala:195)
	at dotty.tools.dotc.Driver.process(Driver.scala:163)
	at dotty.tools.dotc.Driver.process(Driver.scala:175)
	at dotty.tools.dotc.Driver.main(Driver.scala:205)
	at dotty.tools.dotc.Main.main(Main.scala)

Expectation

The code should compile successfully.

According to the backtrace, the assertion fails because the number of arguments of the function type mismatches with actual arguments when rechecking f.apply(unit).

@Linyxus Linyxus added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 27, 2022
@Linyxus Linyxus changed the title Capture checker triggers AssertionError for function application Capture checker triggers AssertionError on function application Aug 27, 2022
@Linyxus
Copy link
Contributor Author

Linyxus commented Aug 27, 2022

Note: the failure is related to the type definition Lazy, since the following code compiles:

class Unit
object unit extends Unit

type Foo[X] = [T] -> (op: X => T) -> T
// type Lazy[X] = {*} Unit -> X

def force[X](fx: Foo[{*} Unit -> X]): X =
  fx[X](f => f(unit))

@Linyxus Linyxus added cc-experiment Intended to be merged with cc-experiment branch on origin and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Aug 27, 2022
@Linyxus Linyxus self-assigned this Oct 19, 2022
odersky added a commit that referenced this issue Dec 20, 2022
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.
@Kordyjan Kordyjan added this to the 3.3.0 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cc-experiment Intended to be merged with cc-experiment branch on origin itype:bug
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants