diff --git a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala index d83817dd90e0..5a8111a4cc28 100644 --- a/compiler/src/dotty/tools/dotc/inlines/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/inlines/Inliner.scala @@ -144,9 +144,28 @@ object Inliner: else Nil case _ => Nil val refinements = openOpaqueAliases(cls.givenSelfType) + + // Map references in the refinements from the proxied termRef + // to the recursive type of the refined type + // e.g.: Obj.type{type A = Obj.B; type B = Int} -> Obj.type{type A = .B; type B = Int} + def mapRecTermRefReferences(recType: RecType, refinedType: Type) = + new TypeMap { + def apply(tp: Type) = tp match + case RefinedType(a: RefinedType, b, info) => RefinedType(apply(a), b, apply(info)) + case RefinedType(a, b, info) => RefinedType(a, b, apply(info)) + case TypeRef(prefix, des) => TypeRef(apply(prefix), des) + case termRef: TermRef if termRef == ref => recType.recThis + case _ => mapOver(tp) + }.apply(refinedType) + val refinedType = refinements.foldLeft(ref: Type): (parent, refinement) => RefinedType(parent, refinement._1, TypeAlias(refinement._2)) - val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, refinedType, span) + + val recType = RecType.closeOver ( recType => + mapRecTermRefReferences(recType, refinedType) + ) + + val refiningSym = newSym(InlineBinderName.fresh(), Synthetic, recType, span) refiningSym.termRef def unapply(refiningRef: TermRef)(using Context): Option[TermRef] = @@ -387,7 +406,9 @@ class Inliner(val call: tpd.Tree)(using Context): val refiningRef = OpaqueProxy(ref, cls, call.span) val refiningSym = refiningRef.symbol.asTerm val refinedType = refiningRef.info - val refiningDef = ValDef(refiningSym, tpd.ref(ref).cast(refinedType), inferred = true).withSpan(span) + val refiningDef = addProxiesForRecurrentOpaques( + ValDef(refiningSym, tpd.ref(ref).cast(refinedType), inferred = true).withSpan(span) + ) inlining.println(i"add opaque alias proxy $refiningDef for $ref in $tp") bindingsBuf += refiningDef opaqueProxies += ((ref, refiningSym.termRef)) @@ -407,6 +428,27 @@ class Inliner(val call: tpd.Tree)(using Context): } ) + /** Transforms proxies that reference other opaque types, like for: + * object Obj1 { opaque type A = Int } + * object Obj2 { opaque type B = A } + * and proxy$1 of type Obj2.type{type B = Obj1.A} + * creates proxy$2 of type Obj1.type{type A = Int} + * and transforms proxy$1 into Obj2.type{type B = proxy$2.A} + */ + private def addProxiesForRecurrentOpaques(binding: ValDef)(using Context): ValDef = + def fixRefinedTypes(ref: Type): Unit = + ref match + case recType: RecType => fixRefinedTypes(recType.underlying) + case RefinedType(parent, name, info) => + addOpaqueProxies(info.widen, binding.span, true) + fixRefinedTypes(parent) + case _ => + fixRefinedTypes(binding.symbol.info) + binding.symbol.info = mapOpaques.typeMap(binding.symbol.info) + mapOpaques.transform(binding).asInstanceOf[ValDef] + .showing(i"transformed this binding exposing opaque aliases: $result", inlining) + end addProxiesForRecurrentOpaques + /** If `binding` contains TermRefs that refer to objects with opaque * type aliases, add proxy definitions that expose these aliases * and substitute such TermRefs with theproxies. Example from pos/opaque-inline1.scala: diff --git a/tests/pos/22359a.scala b/tests/pos/22359a.scala new file mode 100644 index 000000000000..a3b9ef63257a --- /dev/null +++ b/tests/pos/22359a.scala @@ -0,0 +1,15 @@ +opaque type NT[N <: Tuple, V <: Tuple] = V +opaque type System = NT[Tuple1["wires"], Tuple1[Any]] + +extension [N <: Tuple, V <: Tuple] (x: NT[N, V]) { + inline def apply(n: Int): Any = + x.productElement(n) +} + +extension (system: System) { + inline def foo = + system.apply(0) +} + +val simulation: System = ??? +val _ = simulation.foo diff --git a/tests/pos/22359b.scala b/tests/pos/22359b.scala new file mode 100644 index 000000000000..f6b7cbb462c1 --- /dev/null +++ b/tests/pos/22359b.scala @@ -0,0 +1,17 @@ +object Obj2: + opaque type NT[N <: Tuple, V <: Tuple] = V + + extension [N <: Tuple, V <: Tuple] (x: NT[N, V]) { + inline def apply(n: Int): Any = + x.productElement(n) + } + +object Obj: + opaque type System = Obj2.NT[Tuple1["wires"], Tuple1[Any]] + + extension (system: System) { + inline def foo = system.apply(0) + } +import Obj._ +val simulation: System = ??? +val _ = simulation.foo diff --git a/tests/pos/i17243.scala b/tests/pos/i17243.scala new file mode 100644 index 000000000000..3d42495b26b0 --- /dev/null +++ b/tests/pos/i17243.scala @@ -0,0 +1,17 @@ +object Opaque: + opaque type A = Int + + val va: A = 1 + + inline def a(x: A) = + x + 1 + +object Opaque2: + opaque type B = Opaque.A + + val vb: B = Opaque.va + + inline def b(x: B) = Opaque.a(x) + +@main def Test() = + print(Opaque2.b(Opaque2.vb))