From 6b0cf1543067b4d3904a04e5face86fb993413dd Mon Sep 17 00:00:00 2001 From: Oron Date: Wed, 22 Apr 2026 16:39:16 +0300 Subject: [PATCH 1/2] Fix #25894: break cross-unit cyclic export on partial recompile MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a file is recompiled against TASTy of its sibling files, resolving an import on the enclosing package can chain into an export in a loaded-from-TASTy sibling that points back at a definition in the file currently being typed, producing a spurious `Cyclic reference involving val `. In `typedPackageDef`, after completing the current source's `$package` class (existing behaviour for i13669), also force-complete any sibling `$package` classes in this package that came from the classpath. This pre-resolves their exports before any import in the current file runs its completer, breaking the cycle. Narrowed to avoid unnecessary work: - skipped for the root / empty package (i13669 is unaffected — it uses the existing top-level call); - only `isDefinedInBinary` `$package` classes are forced, so source- defined siblings still complete in their natural order. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../src/dotty/tools/dotc/typer/Typer.scala | 13 +++++++ tests/pos/i25894/DFVal_1.scala | 15 ++++++++ tests/pos/i25894/MutableDB_1.scala | 2 ++ tests/pos/i25894/hdl_1.scala | 6 ++++ tests/pos/i25894/stubs_1.scala | 34 +++++++++++++++++++ tests/pos/i25894/stubs_2.scala | 34 +++++++++++++++++++ 6 files changed, 104 insertions(+) create mode 100644 tests/pos/i25894/DFVal_1.scala create mode 100644 tests/pos/i25894/MutableDB_1.scala create mode 100644 tests/pos/i25894/hdl_1.scala create mode 100644 tests/pos/i25894/stubs_1.scala create mode 100644 tests/pos/i25894/stubs_2.scala diff --git a/compiler/src/dotty/tools/dotc/typer/Typer.scala b/compiler/src/dotty/tools/dotc/typer/Typer.scala index 2ebff48d82dc..47506f82f710 100644 --- a/compiler/src/dotty/tools/dotc/typer/Typer.scala +++ b/compiler/src/dotty/tools/dotc/typer/Typer.scala @@ -3615,6 +3615,19 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer val packageObjectName = desugar.packageObjectName(ctx.source) val topLevelClassSymbol = pkg.moduleClass.info.decls.lookup(packageObjectName.moduleClassName) topLevelClassSymbol.ensureCompleted() + // When sibling files in this package come from the classpath (TASTy), + // force their `$package` classes now to avoid cross-unit cycles + // as in #25894: otherwise the first lookup on this package (e.g. an + // import qualifier at the top of this file) forces them during the + // import's completer, and their unpickling can chain through source + // exports back to the import itself. + if !pkg.isEffectiveRoot && pkg != defn.EmptyPackageVal then + pkg.moduleClass.denot match + case pcd: SymDenotations.PackageClassDenotation => + for pobj <- pcd.packageObjs do + if pobj.symbol.isDefinedInBinary then + pobj.symbol.ensureCompleted() + case _ => var stats1 = typedStats(tree.stats, pkg.moduleClass)._1 if (!ctx.isAfterTyper) stats1 = stats1 ++ typedBlockStats(MainProxies.proxies(stats1))._1 diff --git a/tests/pos/i25894/DFVal_1.scala b/tests/pos/i25894/DFVal_1.scala new file mode 100644 index 000000000000..855e5a15f0b8 --- /dev/null +++ b/tests/pos/i25894/DFVal_1.scala @@ -0,0 +1,15 @@ +package dfhdl + +final class DFVal[+T <: DFType, +M] + +type DFValAny = DFVal[DFType, ModifierAny] +type DFConstOf[+T <: DFType] = DFVal[T, Modifier.CONST] +type DFValTP[+T <: DFType] = DFVal[T, Modifier] + +inline def x = ${ ??? } + +object DFVal: + export DFXInt.Ops.{c1, c2} + object Ops: + type BoolOnlyOp + type CarryOp diff --git a/tests/pos/i25894/MutableDB_1.scala b/tests/pos/i25894/MutableDB_1.scala new file mode 100644 index 000000000000..eee22bdccdcc --- /dev/null +++ b/tests/pos/i25894/MutableDB_1.scala @@ -0,0 +1,2 @@ +package dfhdl +class MutableDB diff --git a/tests/pos/i25894/hdl_1.scala b/tests/pos/i25894/hdl_1.scala new file mode 100644 index 000000000000..9e072f2d03e5 --- /dev/null +++ b/tests/pos/i25894/hdl_1.scala @@ -0,0 +1,6 @@ +package dfhdl +object hdl: + export DFBoolOrBit.given + export DFDecimal.Ops.* + +export hdl.* diff --git a/tests/pos/i25894/stubs_1.scala b/tests/pos/i25894/stubs_1.scala new file mode 100644 index 000000000000..ddd100937475 --- /dev/null +++ b/tests/pos/i25894/stubs_1.scala @@ -0,0 +1,34 @@ +package dfhdl +import DFVal.Ops.CarryOp +import DFVal.Ops.BoolOnlyOp + +trait ExactOp2Aux[Op, C, O] + +trait Modifier +type ModifierAny = Modifier +object Modifier: + type CONST = Modifier + +class DFType + +class DFC(mutableDB: MutableDB) + +object DFBoolOrBit: + given bl[Op, O](using + ExactOp2Aux[Op, DFC, O] + ): ExactOp2Aux[BoolOnlyOp, DFC, O] = ??? + +object DFDecimal: + object Ops: + export DFXInt.Ops.* + +object DFXInt: + object Ops: + type A = Int + type B = String + given arith1[Op <: A]: ExactOp2Aux[Op, DFC, DFValTP[DFType]] = ??? + given arith2[Op <: B]: ExactOp2Aux[Op, DFC, DFValTP[DFType]] = ??? + given c1: ExactOp2Aux[CarryOp, DFC, DFValTP[DFType]] = ??? + given c2: ExactOp2Aux[CarryOp, DFC, DFValTP[DFType]] = ??? + +type DFConstInt32 = DFConstOf[DFType] diff --git a/tests/pos/i25894/stubs_2.scala b/tests/pos/i25894/stubs_2.scala new file mode 100644 index 000000000000..ddd100937475 --- /dev/null +++ b/tests/pos/i25894/stubs_2.scala @@ -0,0 +1,34 @@ +package dfhdl +import DFVal.Ops.CarryOp +import DFVal.Ops.BoolOnlyOp + +trait ExactOp2Aux[Op, C, O] + +trait Modifier +type ModifierAny = Modifier +object Modifier: + type CONST = Modifier + +class DFType + +class DFC(mutableDB: MutableDB) + +object DFBoolOrBit: + given bl[Op, O](using + ExactOp2Aux[Op, DFC, O] + ): ExactOp2Aux[BoolOnlyOp, DFC, O] = ??? + +object DFDecimal: + object Ops: + export DFXInt.Ops.* + +object DFXInt: + object Ops: + type A = Int + type B = String + given arith1[Op <: A]: ExactOp2Aux[Op, DFC, DFValTP[DFType]] = ??? + given arith2[Op <: B]: ExactOp2Aux[Op, DFC, DFValTP[DFType]] = ??? + given c1: ExactOp2Aux[CarryOp, DFC, DFValTP[DFType]] = ??? + given c2: ExactOp2Aux[CarryOp, DFC, DFValTP[DFType]] = ??? + +type DFConstInt32 = DFConstOf[DFType] From 01dd529437132d1daa2181ac7f1e661168db3d91 Mon Sep 17 00:00:00 2001 From: Oron Date: Wed, 22 Apr 2026 18:48:40 +0300 Subject: [PATCH 2/2] Empty commit to retrigger CI (known-flaky pipelining-scala-java-basic, #25797) Co-Authored-By: Claude Opus 4.7 (1M context)