Skip to content

Erased context of captured values #17949

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
WojciechMazur opened this issue Jun 9, 2023 · 5 comments
Closed

Erased context of captured values #17949

WojciechMazur opened this issue Jun 9, 2023 · 5 comments
Labels
cc-experiment Intended to be merged with cc-experiment branch on origin itype:bug stat:needs spec

Comments

@WojciechMazur
Copy link
Contributor

WojciechMazur commented Jun 9, 2023

Compiler version

3.3.2-RC1-bin-20230608-0af8809-NIGHTLY

Minimized code

import scala.language.experimental.captureChecking

trait SafeZone
object SafeZone:
  final def apply[T](f: (SafeZone^) ?=> T): T = f(using new SafeZone {})

def allocate[T](sz: SafeZone^, obj: T): T^{sz} = obj

class A(v: Int = 0)

@main def Test(): Unit = 
  val result = SafeZone: sz0 ?=>
    val a = SafeZone: sz1 ?=> 
      val a1: A^{sz1} = allocate(sz1, new A(1))
      a1
    println(a)
    

Output

Compiles

Compilation output (after typer)

   final def apply[T >: Nothing <: Any](f: (SafeZone^) ?=> T): T = ???
}
final module class Test$package() extends Object() { this: Test$package.type =>
    def allocate[T >: Nothing <: Any](sz: SafeZone^{cap}, obj: T): T^{sz} = obj
    @main def Test(): Unit = {
    val result: A = SafeZone.apply[A]({
          def $anonfun(using sz0: SafeZone^): A = {
              val a: A = SafeZone.apply[A]({
                  def $anonfun(using sz1: SafeZone^): A = {
                      val a1: A^{sz1} = allocate[A](sz1, new A(1))
                      a1:A // erasure of ^{sz1} context
                    }
                  closure($anonfun)
                })
              a:A
            }
          closure($anonfun)
        })
    ()
  }
}

Expectation

It probably should fail to compile similarly as with last checked version 3.3.1-RC1-bin-20230503-b8d2966-NIGHTLY

//> using scala 3.3.1-RC1-bin-20230503-b8d2966-NIGHTLY
import scala.language.experimental.captureChecking

trait SafeZone
object SafeZone:
  final def apply[T](f: ({*}SafeZone) ?=> T): T = f(using new SafeZone {})

def allocate[T](sz: {*}SafeZone, obj: T): {sz}T = obj

class A(v: Int = 0)

@main def Test(): Unit = 
  val result = SafeZone: sz0 ?=> //error
    val a = SafeZone: sz1 ?=> 
      val a1: {sz1}A = allocate(sz1, new A(1))
      a1
    a

Output

[error] ./sandbox/src/main/scala-next/Test.scala:12:16
[error] box {x$0, *} A cannot be box-converted to {x$0} A
[error] since one of their capture sets contains the root capability `*`
@WojciechMazur WojciechMazur added itype:bug stat:needs spec cc-experiment Intended to be merged with cc-experiment branch on origin labels Jun 9, 2023
@yawen-guan
Copy link

yawen-guan commented Jun 10, 2023

Here is a simplified example:

import scala.language.experimental.captureChecking

class A
class B
object B:
  def apply[T](f: ((B^) => T)): T = f(new B())

@main def Test() =
  val a = B { b =>
    val a1: A^{b} = new A()
    a1
  }
  println(s"a = $a")

In version: 3.3.2-RC1-bin-20230608-0af8809-NIGHTLY, this example compiles. In cc phase, a has type A^{x$0} (one may check this type by updating val a = B {...} to val a: A = B {...} to trigger the error message).
Under -Xprint:cc, the right hand side of val a = B {...} is

B.apply[box A^](
  {
    {
      def $anonfun(b: B^): A^{b} =
        {
          val a1: A^{b} = new A()
          a1:A^{a1}
        }
      closure($anonfun)
    }
  }
)

Same example in the old syntax:

import scala.language.experimental.captureChecking

class A
class B
object B:
  def apply[T](f: (({*} B) => T)): T = f(new B())

@main def Test() =
  val a = B { b =>
    val a1: {b} A = new A
    a1
  }
  println(s"a = $a")

Using the old version of compiler(3.3.1-RC1-bin-20230503-b8d2966-NIGHTLY), in cc phase, the right hand side of val a = B {...} is

B.apply[box {x$0, *} A](
  {
    {
      def $anonfun(b: {*} B): {b} A =
        {
          val a1: {b} A = new A()
          a1:({a1} A)
        }
      closure($anonfun)
    }
  }
)

Which triggered :

-- Error: local/i17949-old.scala:9:12 ----------------------------------------------
 9 |  val a = B { b =>
   |          ^
   |        box {x$0, *} A cannot be box-converted to {x$0} A
   |        since one of their capture sets contains the root capability `*`
10 |    val a1: {b} A = new A
11 |    a1
12 |  }

It seems in this concrete example, the problem is the the type of the right hand side of val a = B {...} in the new version of compiler somehow successfully adapted to {x$0} A.

@yawen-guan
Copy link

yawen-guan commented Jun 10, 2023

Sorry for closing it by mistake! I reopened it.

@Linyxus
Copy link
Contributor

Linyxus commented Jun 10, 2023

After changing the example to the following it will be rejected as expected:

  import scala.language.experimental.captureChecking

  class A
  class B
  object B:
    def apply[sealed T](f: ((B^) => T)): T = f(new B())

  @main def Test() =
    val a = B { b =>
      val a1: A^{b} = new A()
      a1
    }

In a newer version of CC (after #17422), we use the sealed keyword on a type parameter to enforce the scoping of local capabilities. Previously we forbid a * in all boxed capture sets, but now we forbid * in the capture sets of the instance of a type variable annotated with sealed.

@yawen-guan
Copy link

I see. Thank you Yichen!

@WojciechMazur
Copy link
Contributor Author

Thank you for help, it fixed the problem

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 stat:needs spec
Projects
None yet
Development

No branches or pull requests

3 participants