scala3 icon indicating copy to clipboard operation
scala3 copied to clipboard

Unexpectedly retained constructor argument

Open rcano opened this issue 8 months ago • 3 comments

Compiler version

3.6.4 and 3.7.0-RC1

Minimized code

import scala.util.boundary
import scala.annotation.constructorOnly

class Leak()(using @constructorOnly l: boundary.Label[String]) {
  Seq("a", "b").foreach(_ => boundary.break("stop"))
}

Output

Compilation error

l is marked `@constructorOnly` but it is retained as a field in class Leak

Expectation

It should compile successfully, as the constructor argument shouldn't be captured.

Note

The compiler currently does capture the variable the moment it sees it passed to a lambda. Running CFR on the compiled code (when the annotation is not present), reveals:

import java.io.Serializable;
import scala.Function1;
import scala.collection.SeqOps;
import scala.collection.immutable.$colon$colon;
import scala.collection.immutable.List;
import scala.collection.immutable.Nil$;
import scala.runtime.Nothing$;
import scala.util.boundary;
import scala.util.boundary$;

public class Leak {
    private final boundary.Label<String> l;

    public Leak(boundary.Label<String> l) {
        this.l = l;
        ((SeqOps)new $colon$colon<Nothing$>((Nothing$)((Object)"a"), (List<Nothing$>)new $colon$colon<Nothing$>((Nothing$)((Object)"b"), Nil$.MODULE$))).foreach((Function1<String, Object> & Serializable)_$1 -> {
            throw boundary$.MODULE$.break("stop", l);
        });
    }
}

where one can see that this.l is indeed never used, the Lambda simply closes over the constructor argument as expected.

rcano avatar Apr 11 '25 16:04 rcano

I'd like to clarify, the bug is not about the annotation misfiring, but the compiler violating expectation and retaining the field.

rcano avatar May 27 '25 14:05 rcano

In Scala 2, the lambda uses the instance field, so perhaps this is residual behavior.

som-snytt avatar May 29 '25 10:05 som-snytt

The member starts out captured. The field is dropped if it is only used from the constructor. For the lambda case, lambda construction must happen before the constructor finishes.

som-snytt avatar May 29 '25 20:05 som-snytt