@@ -15,7 +15,7 @@ import typer.Inferencing.isFullyDefined
15
15
import typer .RefChecks .{checkAllOverrides , checkSelfAgainstParents , OverridingPairsChecker }
16
16
import typer .Checking .{checkBounds , checkAppliedTypesIn }
17
17
import typer .ErrorReporting .{Addenda , NothingToAdd , err }
18
- import typer .ProtoTypes .{LhsProto , WildcardSelectionProto }
18
+ import typer .ProtoTypes .{LhsProto , WildcardSelectionProto , SelectionProto }
19
19
import util .{SimpleIdentitySet , EqHashMap , EqHashSet , SrcPos , Property }
20
20
import transform .{Recheck , PreRecheck , CapturedVars }
21
21
import Recheck .*
@@ -1309,17 +1309,61 @@ class CheckCaptures extends Recheck, SymTransformer:
1309
1309
override def checkConformsExpr (actual : Type , expected : Type , tree : Tree , addenda : Addenda )(using Context ): Type =
1310
1310
testAdapted(actual, expected, tree, addenda)(err.typeMismatch)
1311
1311
1312
+ @ annotation.tailrec
1313
+ private def widenNamed (tp : Type )(using Context ): Type = tp match
1314
+ case stp : SingletonType => widenNamed(stp.widen)
1315
+ case ntp : NamedType => ntp.info match
1316
+ case info : TypeBounds => widenNamed(info.hi)
1317
+ case _ => tp
1318
+ case _ => tp
1319
+
1312
1320
inline def testAdapted (actual : Type , expected : Type , tree : Tree , addenda : Addenda )
1313
1321
(fail : (Tree , Type , Addenda ) => Unit )(using Context ): Type =
1322
+
1314
1323
var expected1 = alignDependentFunction(expected, actual.stripCapturing)
1315
1324
val falseDeps = expected1 ne expected
1316
- val actualBoxed = adapt(actual, expected1, tree)
1325
+ val actual1 =
1326
+ if expected.stripCapturing.isInstanceOf [SelectionProto ] then
1327
+ // If the expected type is a `SelectionProto`, we should be careful about cases when
1328
+ // the actual type is a type parameter (for instance, `X <: box IO^`).
1329
+ // If `X` were not widen to reveal the boxed type, both sides are unboxed and thus
1330
+ // no box adaptation happens. But it is unsound: selecting a member from `X` implicitly
1331
+ // unboxes the value.
1332
+ //
1333
+ // Therefore, when the expected type is a selection proto, we conservatively widen
1334
+ // the actual type to strip type parameters.
1335
+ widenNamed(actual)
1336
+ else actual
1337
+ val actualBoxed = adapt(actual1, expected1, tree)
1317
1338
// println(i"check conforms $actualBoxed <<< $expected1")
1318
1339
1319
1340
if actualBoxed eq actual then
1320
1341
// Only `addOuterRefs` when there is no box adaptation
1321
1342
expected1 = addOuterRefs(expected1, actual, tree.srcPos)
1322
- TypeComparer .compareResult(isCompatible(actualBoxed, expected1)) match
1343
+
1344
+ def tryCurrentType : Boolean =
1345
+ isCompatible(actualBoxed, expected1)
1346
+
1347
+ /** When the actual type is a named type, and the previous attempt failed, try to widen the named type
1348
+ * and try another time.
1349
+ *
1350
+ * This is useful for cases like:
1351
+ *
1352
+ * def id[X <: box IO^{a}](x: X): IO^{a} = x
1353
+ *
1354
+ * When typechecking the body, we need to show that `(x: X)` can be typed at `IO^{a}`.
1355
+ * In the first attempt, since `X` is simply a parameter reference, we treat it as non-boxed and perform
1356
+ * no box adptation. But its upper bound is in fact boxed, and adaptation is needed for typechecking the body.
1357
+ * In those cases, we widen such types and try box adaptation another time.
1358
+ */
1359
+ def tryWidenNamed : Boolean =
1360
+ val actual1 = widenNamed(actual)
1361
+ (actual1 ne actual) && {
1362
+ val actualBoxed1 = adapt(actual1, expected1, tree)
1363
+ isCompatible(actualBoxed1, expected1)
1364
+ }
1365
+
1366
+ TypeComparer .compareResult(tryCurrentType || tryWidenNamed) match
1323
1367
case TypeComparer .CompareResult .Fail (notes) =>
1324
1368
capt.println(i " conforms failed for ${tree}: $actual vs $expected" )
1325
1369
if falseDeps then expected1 = unalignFunction(expected1)
@@ -1477,7 +1521,8 @@ class CheckCaptures extends Recheck, SymTransformer:
1477
1521
(actualShape, CaptureSet ())
1478
1522
end adaptShape
1479
1523
1480
- def adaptStr = i " adapting $actual ${if covariant then " ~~>" else " <~~" } $expected"
1524
+ // val adaptStr = i"adapting $actual ${if covariant then "~~>" else "<~~"} $expected"
1525
+ // println(adaptStr)
1481
1526
1482
1527
// Get wildcards out of the way
1483
1528
expected match
0 commit comments