Skip to content

Conversation

@bracevac
Copy link
Contributor

@bracevac bracevac commented Nov 14, 2025

Builds on #24424

Tasks

  • Take a path's consume capabilities into account in consume check in checkUse.
  • More neg tests.
  • Improve the error message if possible.

@bracevac
Copy link
Contributor Author

@odersky it seems to be stable now. I will add some more neg tests. Do you agree with the general direction, or should we try something else?

@bracevac
Copy link
Contributor Author

I'm also surprised that reach capabilities would pop up where there are no boxes involved whatsoever.

@bracevac bracevac force-pushed the consume-in-constructor-new branch from 87012a8 to a539295 Compare November 17, 2025 10:10
case _ =>
acc

def capsFromConsumeFields(ref: TermRef): Refs =
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need that? Would not the path root already contain the field consumed set?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed offline:

class B
class A(consume val b: B^)
class Holder(val a: A^)  // Note: NOT consume

def test =
  val b: B^ = B()
  val a = A(b) // b is gone
  val holder = Holder(a) // separation failure, but should be ok

At the end of checkUse, we can see b is consumed and know a clashes with b. To conclude that the use of a is ok, we also need to inspect its consume constructor parameter to see that it has consumed b, so we should ignore the clash.

@bracevac
Copy link
Contributor Author

We came to the conclusion that the solution with fixing the logic at the end of checkUse is apparently not needed, and that instead we should tweak the logic around captureSetImpliedByFields to type something like A(b) where b is consumed as A{val b: B^{b}}^ instead of A{val b: B^{b}}^{b} as it is typed now, analogous to how consume methods work.

@odersky
Copy link
Contributor

odersky commented Nov 19, 2025

#24470 now contains code to require consume parameters to be covered by a fresh of the class. Can you rebase the tests here and see what they see with these changes?

 - FreshCaps with Unscoped classifier are assumed to be owned by the enclosing toplevel class
 - FreshCaps with unscoped classifier are not turned into ResultCaps in the return types of
   anonymous functions.
This reverts commit 9abbb03.
This reverts commit 0a0e666.
This reverts commit 67ba9b5.

Read-only cannot be a classifier anymore, since it would overlap with Unscoped,
but neither subsumes the other.
Allow both .only[C] and .rd (in this order).
If a class has fresh fields and all fields are read-only, only imply `cap.rd`,
not `cap`.
Treat it like an expression `e` instead.
@bracevac bracevac force-pushed the consume-in-constructor-new branch from a4f1c60 to 4636639 Compare November 19, 2025 19:46
@bracevac bracevac requested a review from a team as a code owner November 19, 2025 19:46
@bracevac
Copy link
Contributor Author

@odersky I only added the tests to #24470 here, and I'm seeing

-- Error: tests/pos-custom-args/captures/paths-simple-suffix-consume.scala:9:20 
9 |class A(consume val b: B^)
  |                    ^
  |A consume parameter is only allowed for classes producing a `cap` in their constructor.
  |This can be achieved by having the class extend caps.Separate.

and can't seem to be able to add extends caps.Separate:

-- [E008] Not Found Error: tests/pos-custom-args/captures/paths-simple-suffix-consume.scala:9:40 
9 |class A(consume val b: B^) extends caps.Separate
  |                                   ^^^^^^^^^^^^^
  |                                   type Separate is not a member of caps

It's missing?

@bracevac
Copy link
Contributor Author

bracevac commented Nov 19, 2025

Spying at your test cases, I changed it to

class A(consume val b: B^) extends Mutable { var x: Int = 1 }

in tests/pos-custom-args/captures/paths-simple-suffix-consume.scala.
That seemed to help. However, I'm now getting

-- Error: tests/pos-custom-args/captures/paths-simple-suffix-consume.scala:19:12 
19 |  println(a.b)       // OK - consumed field
   |          ^^^
   |Separation failure: Illegal access to (a.b : B^{b²}), which was passed as a consume parameter to constructor of class A
   |on line 16 and therefore is no longer available.
   |
   |where:    b  is a value in class A
   |          b² is a value in method test1

But it should work, no?

Edit: When we check for a clash in checkUse, I see:

check use a.b, (a.b : B^{b}) clashes at (b,Argument(Ident(b),method <init>))
prefix = (a : A{val b: B^{b}}^)

The b field of a is still capturing the consumed b. Shouldn't we see

prefix = (a : A{val b: B^}^)

instead?

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants