@@ -556,18 +556,92 @@ 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
578
+ .find((from, to) => from.symbol == ref.symbol && from =:= ref)
579
+ .map(_._2)
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 )
571
645
572
646
/** Very similar to TreeInfo.isPureExpr, but with the following inliner-only exceptions:
573
647
* - synthetic case class apply methods, when the case class constructor is empty, are
@@ -666,12 +740,25 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
666
740
case _ =>
667
741
}
668
742
743
+ private val registerTypes = new TypeTraverser :
744
+ override def stopAt = StopAt .Package
745
+ // Only register ThisType prefixes that see opaques. No need to register the others
746
+ // since they are static prefixes.
747
+ def registerStaticPrefix (t : Type ): Unit = t match
748
+ case t : ThisType if t.cls.seesOpaques => registerType(t)
749
+ case t : NamedType => registerStaticPrefix(t.prefix)
750
+ case _ =>
751
+ override def traverse (t : Type ) = t match
752
+ case t : NamedType if t.currentSymbol.isStatic =>
753
+ registerStaticPrefix(t.prefix)
754
+ case t =>
755
+ registerType(t)
756
+ traverseChildren(t)
757
+
669
758
/** Register type of leaf node */
670
- private def registerLeaf (tree : Tree ): Unit = tree match {
671
- case _ : This | _ : Ident | _ : TypeTree =>
672
- tree.typeOpt.foreachPart(registerType, StopAt .Static )
759
+ private def registerLeaf (tree : Tree ): Unit = tree match
760
+ case _ : This | _ : Ident | _ : TypeTree => registerTypes.traverse(tree.typeOpt)
673
761
case _ =>
674
- }
675
762
676
763
/** Make `tree` part of inlined expansion. This means its owner has to be changed
677
764
* from its `originalOwner`, and, if it comes from outside the inlined method
@@ -797,6 +884,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
797
884
val inliner = new InlinerMap (
798
885
typeMap =
799
886
new DeepTypeMap {
887
+ override def stopAt =
888
+ if opaqueProxies.isEmpty then StopAt .Static else StopAt .Package
800
889
def apply (t : Type ) = t match {
801
890
case t : ThisType => thisProxy.getOrElse(t.cls, t)
802
891
case t : TypeRef => paramProxy.getOrElse(t, mapOver(t))
@@ -843,7 +932,16 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(using Context) {
843
932
844
933
// Apply inliner to `rhsToInline`, split off any implicit bindings from result, and
845
934
// make them part of `bindingsBuf`. The expansion is then the tree that remains.
846
- val expansion = inliner.transform(rhsToInline)
935
+ val expansion0 = inliner.transform(rhsToInline)
936
+ val expansion =
937
+ if opaqueProxies.nonEmpty && ! inlinedMethod.is(Transparent ) then
938
+ expansion0.cast(call.tpe)(using ctx.withSource(expansion0.source))
939
+ // the cast makes sure that the sealing with the declared type
940
+ // is type correct. Without it we might get problems since the
941
+ // expression's type is the opaque alias but the call's type is
942
+ // the opaque type itself. An example is in pos/opaque-inline1.scala.
943
+ else
944
+ expansion0
847
945
848
946
def issueError () = callValueArgss match {
849
947
case (msgArg :: Nil ) :: Nil =>
0 commit comments