Skip to content

Commit 0c9e224

Browse files
committed
Improve error message for failures of mutable capture sets
1 parent cdc9cf4 commit 0c9e224

File tree

4 files changed

+27
-4
lines changed

4 files changed

+27
-4
lines changed

compiler/src/dotty/tools/dotc/cc/CaptureSet.scala

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,7 @@ object CaptureSet:
553553
false
554554
}
555555

556-
def toReader()(using Context) = false
556+
def toReader()(using Context) = failWith(MutAdaptFailure(this))
557557

558558
def addDependent(cs: CaptureSet)(using Context, VarState) = true
559559

@@ -721,7 +721,7 @@ object CaptureSet:
721721
res
722722

723723
final def toReader()(using Context) =
724-
if isConst then false // TODO add error note when failing?
724+
if isConst then failWith(MutAdaptFailure(this))
725725
else
726726
mutability = Reader
727727
TypeComparer.logUndoAction(() => mutability = Mutable)
@@ -1205,6 +1205,7 @@ object CaptureSet:
12051205
*/
12061206
case class ExistentialSubsumesFailure(val ex: ResultCap, val other: Capability) extends ErrorNote
12071207

1208+
/** Failure indicating that `elem` cannot be included in `cs` */
12081209
case class IncludeFailure(cs: CaptureSet, elem: Capability, levelError: Boolean = false) extends ErrorNote, Showable:
12091210
private var myTrace: List[CaptureSet] = cs :: Nil
12101211

@@ -1224,6 +1225,14 @@ object CaptureSet:
12241225
else i"$elem cannot be included in $cs"
12251226
end IncludeFailure
12261227

1228+
/** Failure indicating that a read-only capture set of a mutable type cannot be
1229+
* widened to an exclusive set.
1230+
* @param cs the exclusive set in question
1231+
* @param lo the lower type of the orginal type comparison, or NoType if not known
1232+
* @param hi the upper type of the orginal type comparison, or NoType if not known
1233+
*/
1234+
case class MutAdaptFailure(cs: CaptureSet, lo: Type = NoType, hi: Type = NoType) extends ErrorNote
1235+
12271236
/** A VarState serves as a snapshot mechanism that can undo
12281237
* additions of elements or super sets if an operation fails
12291238
*/

compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import NameKinds.{DefaultGetterName, WildcardParamName, UniqueNameKind}
2525
import reporting.{trace, Message, OverrideError}
2626
import Annotations.Annotation
2727
import Capabilities.*
28+
import dotty.tools.dotc.cc.CaptureSet.MutAdaptFailure
2829

2930
/** The capture checker */
3031
object CheckCaptures:
@@ -1265,7 +1266,7 @@ class CheckCaptures extends Recheck, SymTransformer:
12651266
private def errorNotes(notes: List[TypeComparer.ErrorNote])(using Context): Addenda =
12661267
val printableNotes = notes.filter:
12671268
case IncludeFailure(_, _, true) => true
1268-
case _: ExistentialSubsumesFailure => true
1269+
case _: ExistentialSubsumesFailure | _: MutAdaptFailure => true
12691270
case _ => false
12701271
if printableNotes.isEmpty then NothingToAdd
12711272
else new Addenda:
@@ -1287,6 +1288,10 @@ class CheckCaptures extends Recheck, SymTransformer:
12871288
else " since that capability is not a SharedCapability"
12881289
i"""the existential capture root in ${ex.originalBinder.resType}
12891290
|cannot subsume the capability $other$since"""
1291+
case MutAdaptFailure(cs, lo, hi) =>
1292+
def ofType(tp: Type) = if tp.exists then i"of the mutable type $tp" else "of a mutable type"
1293+
i"""$cs is an exclusive capture set ${ofType(hi)},
1294+
|it cannot subsume a read-only capture set ${ofType(lo)}."""
12901295
i"""
12911296
|Note that ${msg.toString}"""
12921297

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2902,7 +2902,13 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
29022902
if tp1.derivesFromMutable && !tp2.derivesFromMutable
29032903
then refs1.readOnly
29042904
else refs1
2905-
subCaptures(refs1Adapted, refs2)
2905+
val subc = subCaptures(refs1Adapted, refs2)
2906+
if !subc then
2907+
errorNotes match
2908+
case (level, CaptureSet.MutAdaptFailure(cs, NoType, NoType)) :: rest =>
2909+
errorNotes = (level, CaptureSet.MutAdaptFailure(cs, tp1, tp2)) :: rest
2910+
case _ =>
2911+
subc
29062912
&& (tp1.isBoxedCapturing == tp2.isBoxedCapturing)
29072913
|| refs1.subCaptures(CaptureSet.empty, makeVarState())
29082914

tests/neg-custom-args/captures/ro-mut-conformance.check

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,7 @@
1111
|
1212
| where: ^ refers to a fresh root capability in the type of value t
1313
|
14+
| Note that {cap} is an exclusive capture set of the mutable type Ref^,
15+
| it cannot subsume a read-only capture set of the mutable type (a : Ref).
16+
|
1417
| longer explanation available when compiling with `-explain`

0 commit comments

Comments
 (0)