Skip to content
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
20 changes: 16 additions & 4 deletions compiler/src/dotty/tools/dotc/cc/CaptureSet.scala
Original file line number Diff line number Diff line change
Expand Up @@ -588,6 +588,9 @@ object CaptureSet:
cs.mutability = Mutability.Reader
cs

class EmptyOfBoxed(val tp1: Type, val tp2: Type) extends Const(emptyRefs):
override def toString = "{} of boxed mismatch"

/** The universal capture set `{cap}` */
def universal(using Context): Const =
Const(SimpleIdentitySet(GlobalCap))
Expand Down Expand Up @@ -1341,15 +1344,19 @@ object CaptureSet:
case _ =>
false

/** An include failure F1 covers another include failure F2 unless F2
* strictly subsumes F1, which means they describe the same capture sets
* and the element in F2 is more specific than the element in F1.
/** An include failure F1 covers another include failure F2 unless one
* of the following two conditons holds:
* 1. F2 strictly subsumes F1, which means they describe the same capture sets
* and the element in F2 is more specific than the element in F1.
* 2. Both F1 and F2 are the empty set, but only F2 is an empty set synthesized
* when comparing types with different box status
*/
override def covers(other: Note)(using Context) = other match
case other @ IncludeFailure(cs1, elem1, _) =>
val strictlySubsumes =
cs.elems == cs1.elems
&& elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
&& (elem1.singletonCaptureSet.mightSubcapture(elem.singletonCaptureSet)
|| cs1.isInstanceOf[EmptyOfBoxed] && !cs.isInstanceOf[EmptyOfBoxed])
!strictlySubsumes
case _ => false

Expand Down Expand Up @@ -1390,6 +1397,11 @@ object CaptureSet:
else
trailing:
i"capability ${elem.showAsCapability} cannot be included in capture set $cs"
case cs: EmptyOfBoxed =>
trailing:
val (boxed, unboxed) =
if cs.tp1.isBoxedCapturing then (cs.tp1, cs.tp2) else (cs.tp2, cs.tp1)
i"${cs.tp1} does not conform to ${cs.tp2} because $boxed is boxed but $unboxed is not"
case _ =>
def why =
val reasons = cs.elems.toList.collect:
Expand Down
13 changes: 9 additions & 4 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -550,10 +550,15 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
|| !ctx.mode.is(Mode.CheckBoundsOrSelfType) && tp1.isAlwaysPure
|| parent1.isSingleton && refs1.elems.forall(parent1 eq _)
then
def remainsBoxed1 = parent1.isBoxedCapturing || parent1.dealias.match
case parent1: TypeRef =>
parent1.superType.isBoxedCapturing
// When comparing a type parameter with boxed upper bound on the left
// we should not strip the box on the right. See i24543.scala.
case _ =>
false
val tp2a =
if tp1.isBoxedCapturing && !parent1.isBoxedCapturing
then tp2.unboxed
else tp2
if tp1.isBoxedCapturing && !remainsBoxed1 then tp2.unboxed else tp2
recur(parent1, tp2a)
else thirdTry
compareCapturing
Expand Down Expand Up @@ -2915,7 +2920,7 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case _ =>
subc
&& (tp1.isBoxedCapturing == tp2.isBoxedCapturing
|| refs1.subCaptures(CaptureSet.empty, makeVarState()))
|| refs1.subCaptures(CaptureSet.EmptyOfBoxed(tp1, tp2), makeVarState()))

protected def logUndoAction(action: () => Unit) =
undoLog += action
Expand Down
2 changes: 1 addition & 1 deletion tests/neg-custom-args/captures/eta.check
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@
| Found: (g : () -> A)
| Required: () -> Proc^{f}
|
| Note that capability f is not included in capture set {}.
| Note that () ->{f} Unit does not conform to Proc^{f} because () ->{f} Unit is boxed but Proc^{f} is not.
|
| longer explanation available when compiling with `-explain`
12 changes: 12 additions & 0 deletions tests/pos-custom-args/captures/i24543.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import scala.language.experimental.captureChecking

class Ref
case class Box[T](elem: T)

def h1[T <: Ref^](x: Box[T]): Unit =
val y: (Ref^, Int) = (x.elem, 1) // was error

def h2[T <: Ref^](x: List[T]): (Ref^, Int) = (x.head, 1) // was error



9 changes: 9 additions & 0 deletions tests/pos-custom-args/captures/i24543a.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import scala.language.experimental.captureChecking
class Ref
case class Box[+T](elem: T)

def test1(a: Ref^): Unit =
def hh[T <: Ref^{a}](x: Box[T]): Unit =
val y: (Ref^{a}, Int) = (x.elem, 1)
val z = (x.elem, 1)
val _: (Ref^{a}, Int) = z
Loading