Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Unexpectedly retained constructor argument #22979

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

Open
rcano opened this issue Apr 11, 2025 · 3 comments · May be fixed by #23286
Open

Unexpectedly retained constructor argument #22979

rcano opened this issue Apr 11, 2025 · 3 comments · May be fixed by #23286

Comments

@rcano
Copy link

rcano commented Apr 11, 2025

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 rcano added itype:bug stat:needs triage Every issue needs to have an "area" and "itype" label labels Apr 11, 2025
@Gedochao Gedochao added area:annotations and removed stat:needs triage Every issue needs to have an "area" and "itype" label labels Apr 14, 2025
@rcano
Copy link
Author

rcano commented May 27, 2025

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

@som-snytt
Copy link
Contributor

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

@som-snytt som-snytt linked a pull request May 29, 2025 that will close this issue
@som-snytt som-snytt self-assigned this May 29, 2025
@som-snytt
Copy link
Contributor

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants