@@ -556,18 +556,93 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
556
556
ref(lastSelf).outerSelect(lastLevel - level, selfSym.info)
557
557
else
558
558
inlineCallPrefix
559
- val binding = ValDef (selfSym.asTerm, QuoteUtils .changeOwnerOfTree(rhs, selfSym)).withSpan(selfSym.span)
559
+ val binding = accountForOpaques(
560
+ ValDef (selfSym.asTerm, QuoteUtils .changeOwnerOfTree(rhs, selfSym)).withSpan(selfSym.span))
560
561
bindingsBuf += binding
561
562
inlining.println(i " proxy at $level: $selfSym = ${bindingsBuf.last}" )
562
563
lastSelf = selfSym
563
564
lastLevel = level
564
565
}
565
566
}
566
567
568
+ /** A list of pairs between TermRefs appearing in thisProxy bindings that
569
+ * refer to objects with opaque type aliases and local proxy symbols
570
+ * that contain refined versions of these TermRefs where the aliases
571
+ * are exposed.
572
+ */
573
+ private val opaqueProxies = new mutable.ListBuffer [(TermRef , TermRef )]
574
+
575
+ /** Map first halfs of opaqueProxies pairs to second halfs, using =:= as equality */
576
+ def mapRef (ref : TermRef ): Option [TermRef ] =
577
+ opaqueProxies.collectFirst {
578
+ case (from, to) if from.symbol == ref.symbol && from =:= ref => to
579
+ }
580
+
581
+ /** If `binding` contains TermRefs that refer to objects with opaque
582
+ * type aliases, add proxy definitions that expose these aliases
583
+ * and substitute such TermRefs with theproxies. Example from pos/opaque-inline1.scala:
584
+ *
585
+ * object refined:
586
+ * opaque type Positive = Int
587
+ * inline def Positive(value: Int): Positive = f(value)
588
+ * def f(x: Positive): Positive = x
589
+ * def run: Unit = { val x = 9; val nine = refined.Positive(x) }
590
+ *
591
+ * This generates the following proxies:
592
+ *
593
+ * val $proxy1: refined.type{type Positive = Int} =
594
+ * refined.$asInstanceOf$[refined.type{type Positive = Int}]
595
+ * val refined$_this: ($proxy1 : refined.type{Positive = Int}) =
596
+ * $proxy1
597
+ *
598
+ * and every reference to `refined` in the inlined expression is replaced by
599
+ * `refined_$this`.
600
+ */
601
+ def accountForOpaques (binding : ValDef )(using Context ): ValDef =
602
+ binding.symbol.info.foreachPart {
603
+ case ref : TermRef =>
604
+ for cls <- ref.widen.classSymbols do
605
+ if cls.containsOpaques && mapRef(ref).isEmpty then
606
+ def openOpaqueAliases (selfType : Type ): List [(Name , Type )] = selfType match
607
+ case RefinedType (parent, rname, TypeAlias (alias)) =>
608
+ val opaq = cls.info.member(rname).symbol
609
+ if opaq.isOpaqueAlias then
610
+ (rname, alias.stripLazyRef.asSeenFrom(ref, cls))
611
+ :: openOpaqueAliases(parent)
612
+ else Nil
613
+ case _ =>
614
+ Nil
615
+ val refinements = openOpaqueAliases(cls.givenSelfType)
616
+ val refinedType = refinements.foldLeft(ref : Type ) ((parent, refinement) =>
617
+ RefinedType (parent, refinement._1, TypeAlias (refinement._2))
618
+ )
619
+ val refiningSym = newSym(InlineBinderName .fresh(), Synthetic , refinedType).asTerm
620
+ val refiningDef = ValDef (refiningSym, tpd.ref(ref).cast(refinedType)).withSpan(binding.span)
621
+ inlining.println(i " add opaque alias proxy $refiningDef" )
622
+ bindingsBuf += refiningDef
623
+ opaqueProxies += ((ref, refiningSym.termRef))
624
+ case _ =>
625
+ }
626
+ if opaqueProxies.isEmpty then binding
627
+ else
628
+ val mapType = new TypeMap :
629
+ override def stopAt = StopAt .Package
630
+ def apply (t : Type ) = mapOver {
631
+ t match
632
+ case ref : TermRef => mapRef(ref).getOrElse(ref)
633
+ case _ => t
634
+ }
635
+ binding.symbol.info = mapType(binding.symbol.info)
636
+ val mapTree = TreeTypeMap (typeMap = mapType)
637
+ mapTree.transform(binding).asInstanceOf [ValDef ]
638
+ .showing(i " transformed this binding exposing opaque aliases: $result" , inlining)
639
+ end accountForOpaques
640
+
567
641
private def canElideThis (tpe : ThisType ): Boolean =
568
- inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls) ||
569
- tpe.cls.isContainedIn(inlinedMethod) ||
570
- tpe.cls.is(Package )
642
+ inlineCallPrefix.tpe == tpe && ctx.owner.isContainedIn(tpe.cls)
643
+ || tpe.cls.isContainedIn(inlinedMethod)
644
+ || tpe.cls.is(Package )
645
+ || tpe.cls.isStaticOwner && ! (tpe.cls.seesOpaques && inlinedMethod.isContainedIn(tpe.cls))
571
646
572
647
/** Very similar to TreeInfo.isPureExpr, but with the following inliner-only exceptions:
573
648
* - synthetic case class apply methods, when the case class constructor is empty, are
@@ -666,12 +741,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
666
741
case _ =>
667
742
}
668
743
744
+ private val registerTypes = new TypeTraverser :
745
+ override def stopAt = StopAt .Package
746
+ override def traverse (t : Type ) =
747
+ registerType(t)
748
+ traverseChildren(t)
749
+
669
750
/** Register type of leaf node */
670
- private def registerLeaf (tree : Tree ): Unit = tree match {
671
- case _ : This | _ : Ident | _ : TypeTree =>
672
- tree.typeOpt.foreachPart(registerType, stopAtStatic = true )
751
+ private def registerLeaf (tree : Tree ): Unit = tree match
752
+ case _ : This | _ : Ident | _ : TypeTree => registerTypes.traverse(tree.typeOpt)
673
753
case _ =>
674
- }
675
754
676
755
/** Make `tree` part of inlined expansion. This means its owner has to be changed
677
756
* from its `originalOwner`, and, if it comes from outside the inlined method
@@ -797,6 +876,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
797
876
val inliner = new InlinerMap (
798
877
typeMap =
799
878
new DeepTypeMap {
879
+ override def stopAt =
880
+ if opaqueProxies.isEmpty then StopAt .Static else StopAt .Package
800
881
def apply (t : Type ) = t match {
801
882
case t : ThisType => thisProxy.getOrElse(t.cls, t)
802
883
case t : TypeRef => paramProxy.getOrElse(t, mapOver(t))
@@ -915,7 +996,17 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
915
996
916
997
// Take care that only argument bindings go into `bindings`, since positions are
917
998
// different for bindings from arguments and bindings from body.
918
- tpd.Inlined (call, finalBindings, finalExpansion)
999
+ val res = tpd.Inlined (call, finalBindings, finalExpansion)
1000
+ if opaqueProxies.isEmpty then res
1001
+ else
1002
+ val target =
1003
+ if inlinedMethod.is(Transparent ) then call.tpe & res.tpe
1004
+ else call.tpe
1005
+ res.ensureConforms(target)
1006
+ // Make sure that the sealing with the declared type
1007
+ // is type correct. Without it we might get problems since the
1008
+ // expression's type is the opaque alias but the call's type is
1009
+ // the opaque type itself. An example is in pos/opaque-inline1.scala.
919
1010
}
920
1011
}
921
1012
0 commit comments