Skip to content

Fix setup of class constructors #22980

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

Merged
merged 1 commit into from
Apr 13, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 11 additions & 3 deletions compiler/src/dotty/tools/dotc/cc/Setup.scala
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,13 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
def apply(x: Boolean, tp: Type): Boolean =
if x then true
else if tp.derivesFromCapability && variance >= 0 then true
else tp match
else tp.dealiasKeepAnnots match
case AnnotatedType(_, ann) if ann.symbol.isRetains && variance >= 0 => true
case t: TypeRef if t.symbol.isAbstractOrParamType && !seen.contains(t.symbol) =>
seen += t.symbol
apply(x, t.info.bounds.hi)
case _ => foldOver(x, tp)
case tp1 =>
foldOver(x, tp1)
def apply(tp: Type): Boolean = apply(false, tp)

if symd.symbol.isRefiningParamAccessor
Expand Down Expand Up @@ -270,6 +271,8 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
def mapInferred(refine: Boolean): TypeMap = new TypeMap with SetupTypeMap:
override def toString = "map inferred"

var refiningNames: Set[Name] = Set()

/** Refine a possibly applied class type C where the class has tracked parameters
* x_1: T_1, ..., x_n: T_n to C { val x_1: T_1^{CV_1}, ..., val x_n: T_n^{CV_n} }
* where CV_1, ..., CV_n are fresh capture set variables.
Expand All @@ -282,7 +285,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
cls.paramGetters.foldLeft(tp) { (core, getter) =>
if atPhase(thisPhase.next)(getter.hasTrackedParts)
&& getter.isRefiningParamAccessor
&& !getter.is(Tracked)
&& !refiningNames.contains(getter.name) // Don't add a refinement if we have already an explicit one for the same name
then
val getterType =
mapInferred(refine = false)(tp.memberInfo(getter)).strippedDealias
Expand All @@ -306,6 +309,11 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI:
tp.derivedLambdaType(
paramInfos = tp.paramInfos.mapConserve(_.dropAllRetains.bounds),
resType = this(tp.resType))
case tp @ RefinedType(parent, rname, rinfo) =>
val saved = refiningNames
refiningNames += rname
val parent1 = try this(parent) finally refiningNames = saved
tp.derivedRefinedType(parent1, rname, this(rinfo))
case _ =>
mapFollowingAliases(tp)
addVar(
Expand Down
32 changes: 32 additions & 0 deletions tests/neg-custom-args/captures/leaky.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:14:2 -----------------------------------------
14 | val f: Any ->{a} Any = _ => // error
| ^
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
| Required: test.runnable.Transform
15 | a.print()
16 | ()
17 | Transform(f)
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:20:2 -----------------------------------------
20 | val f: Any ->{a} Any = _ => // error
| ^
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
| Required: test.runnable.Transform
21 | a.print()
22 | ()
23 | val x = Transform(f)
24 | x
|
| longer explanation available when compiling with `-explain`
-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/leaky.scala:27:2 -----------------------------------------
27 | val f: Any ->{a} Any = _ => // error
| ^
| Found: test.runnable.Transform{val fun: Any ->{a} Any}^{a}
| Required: test.runnable.Transform
28 | a.print()
29 | ()
30 | val x = Transform.app(f)
31 | x
|
| longer explanation available when compiling with `-explain`
37 changes: 37 additions & 0 deletions tests/neg-custom-args/captures/leaky.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package test.runnable
import language.experimental.captureChecking

case class A() extends caps.Capability:
def print() = println("leaking...")

class Transform(fun: Any => Any):
def run() = fun(())
object Transform:
def app(f: Any => Any): Transform { val fun: Any->{f} Any } ^ {f} =
Transform(f)

def leak(a: A): Transform^{} =
val f: Any ->{a} Any = _ => // error
a.print()
()
Transform(f)

def leak1(a: A): Transform^{} =
val f: Any ->{a} Any = _ => // error
a.print()
()
val x = Transform(f)
x

def leak2(a: A): Transform^{} =
val f: Any ->{a} Any = _ => // error
a.print()
()
val x = Transform.app(f)
x

def withA[T](body: A => T): T = body(A())

@main def Main() =
val t = withA(leak)
t.run()