diff --git a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala index a5bb8792af2c..5e96d30d28fb 100644 --- a/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala +++ b/compiler/src/dotty/tools/dotc/cc/CheckCaptures.scala @@ -1135,6 +1135,13 @@ class CheckCaptures extends Recheck, SymTransformer: else actual end adaptBoxed + /** Replace all variable capture sets with constants */ + class MakeCapturesConstant(using Context) extends TypeMap with IdempotentCaptRefMap: + def apply(tp: Type): Type = tp match + case CapturingType(parent, refs: CaptureSet.Var) => + tp.derivedCapturingType(mapOver(parent), CaptureSet(refs.elems)) + case _ => mapOver(tp) + /** Check overrides again, taking capture sets into account. * TODO: Can we avoid doing overrides checks twice? * We need to do them here since only at this phase CaptureTypes are relevant @@ -1162,7 +1169,13 @@ class CheckCaptures extends Recheck, SymTransformer: adapted.stripCapturing case _ => adapted finally curEnv = saved - actual1 frozen_<:< expected1 + + // Make variable capture sets constant before performing the check + val makeConst = new MakeCapturesConstant + val actual2 = makeConst(actual1) + val expected2 = makeConst(expected1) + + actual2 frozen_<:< expected2 override def needsCheck(overriding: Symbol, overridden: Symbol)(using Context): Boolean = !setup.isPreCC(overriding) && !setup.isPreCC(overridden) diff --git a/compiler/src/dotty/tools/dotc/cc/Setup.scala b/compiler/src/dotty/tools/dotc/cc/Setup.scala index e6953dbf67b7..48a72503ea64 100644 --- a/compiler/src/dotty/tools/dotc/cc/Setup.scala +++ b/compiler/src/dotty/tools/dotc/cc/Setup.scala @@ -329,10 +329,10 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: end transformExplicitType /** Transform type of type tree, and remember the transformed type as the type the tree */ - private def transformTT(tree: TypeTree, boxed: Boolean, exact: Boolean)(using Context): Unit = + private def transformTT(tree: TypeTree, boxed: Boolean)(using Context): Unit = if !tree.hasRememberedType then val transformed = - if tree.isInstanceOf[InferredTypeTree] && !exact + if tree.isInstanceOf[InferredTypeTree] then transformInferredType(tree.tpe) else transformExplicitType(tree.tpe, tptToCheck = Some(tree)) tree.rememberType(if boxed then box(transformed) else transformed) @@ -394,8 +394,6 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: transformTT(tpt, boxed = !ccConfig.allowUniversalInBoxed && sym.is(Mutable, butNot = Method), // types of mutable variables are boxed in pre 3.3 codee - exact = sym.allOverriddenSymbols.hasNext, - // types of symbols that override a parent don't get a capture set TODO drop ) catch case ex: IllegalCaptureRef => capt.println(i"fail while transforming result type $tpt of $sym") @@ -437,7 +435,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: // No need to box type arguments of an asInstanceOf call. See #20224. case _ => for case arg: TypeTree <- args do - transformTT(arg, boxed = true, exact = false) // type arguments in type applications are boxed + transformTT(arg, boxed = true) // type arguments in type applications are boxed case tree: TypeDef if tree.symbol.isClass => inContext(ctx.withOwner(tree.symbol)): @@ -454,7 +452,7 @@ class Setup extends PreRecheck, SymTransformer, SetupAPI: def postProcess(tree: Tree)(using Context): Unit = tree match case tree: TypeTree => - transformTT(tree, boxed = false, exact = false) + transformTT(tree, boxed = false) case tree: ValOrDefDef => val sym = tree.symbol diff --git a/tests/neg-custom-args/captures/lazylist.check b/tests/neg-custom-args/captures/lazylist.check index 09352ec648ce..ca2a8c11a232 100644 --- a/tests/neg-custom-args/captures/lazylist.check +++ b/tests/neg-custom-args/captures/lazylist.check @@ -1,10 +1,3 @@ --- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:17:15 ------------------------------------- -17 | def tail = xs() // error - | ^^^^ - | Found: lazylists.LazyList[T]^{LazyCons.this.xs} - | Required: lazylists.LazyList[T] - | - | longer explanation available when compiling with `-explain` -- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:35:29 ------------------------------------- 35 | val ref1c: LazyList[Int] = ref1 // error | ^^^^ @@ -33,6 +26,13 @@ | Required: lazylists.LazyList[Int]^{cap1, ref3, cap3} | | longer explanation available when compiling with `-explain` +-- [E164] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:17:6 ---------------------------------------- +17 | def tail = xs() // error // error + | ^ + | error overriding method tail in class LazyList of type -> lazylists.LazyList[T]; + | method tail of type -> lazylists.LazyList[box T^?]^{LazyCons.this.xs} has incompatible type + | + | longer explanation available when compiling with `-explain` -- [E164] Declaration Error: tests/neg-custom-args/captures/lazylist.scala:22:6 ---------------------------------------- 22 | def tail: LazyList[Nothing]^ = ??? // error overriding | ^ @@ -40,3 +40,16 @@ | method tail of type -> lazylists.LazyList[Nothing]^ has incompatible type | | longer explanation available when compiling with `-explain` +-- [E007] Type Mismatch Error: tests/neg-custom-args/captures/lazylist.scala:17:15 ------------------------------------- +17 | def tail = xs() // error // error + | ^^^^ + | Found: lazylists.LazyList[T]^{LazyCons.this.xs} + | Required: lazylists.LazyList[T] + | + | Note that the expected type lazylists.LazyList[T] + | is the previously inferred result type of method tail + | which is also the type seen in separately compiled sources. + | The new inferred type lazylists.LazyList[T]^{LazyCons.this.xs} + | must conform to this type. + | + | longer explanation available when compiling with `-explain` diff --git a/tests/neg-custom-args/captures/lazylist.scala b/tests/neg-custom-args/captures/lazylist.scala index e6e4d003f7ae..91dc4c6fec32 100644 --- a/tests/neg-custom-args/captures/lazylist.scala +++ b/tests/neg-custom-args/captures/lazylist.scala @@ -14,7 +14,7 @@ abstract class LazyList[+T]: class LazyCons[+T](val x: T, val xs: () => LazyList[T]^) extends LazyList[T]: def isEmpty = false def head = x - def tail = xs() // error + def tail = xs() // error // error object LazyNil extends LazyList[Nothing]: def isEmpty = true