Skip to content

Commit 416d85c

Browse files
Merge pull request #4881 from dotty-staging/fix-transparent-1
Fixes to transparent functions
2 parents a4a611c + 8b941d6 commit 416d85c

File tree

5 files changed

+116
-15
lines changed

5 files changed

+116
-15
lines changed

compiler/src/dotty/tools/dotc/core/Flags.scala

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -560,6 +560,9 @@ object Flags {
560560
/** A transparent method */
561561
final val TransparentMethod = allOf(Transparent, Method)
562562

563+
/** A transparent implicit method */
564+
final val TransparentImplicitMethod = allOf(Transparent, Implicit, Method)
565+
563566
/** A transparent parameter */
564567
final val TransparentParam = allOf(Transparent, Param)
565568

compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) {
165165
val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ argText(l) ~ ")" else argText(l)
166166
val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ argText(r) ~ ")" else argText(r)
167167

168-
leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg
168+
leftArg ~ " " ~ simpleNameString(op.classSymbol) ~ " " ~ rightArg
169169
}
170170

171171
homogenize(tp) match {

compiler/src/dotty/tools/dotc/typer/Inliner.scala

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,49 @@ object Inliner {
8585
* @return An `Inlined` node that refers to the original call and the inlined bindings
8686
* and body that replace it.
8787
*/
88-
def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree match {
89-
case Block(stats, expr) =>
90-
cpy.Block(tree)(stats, inlineCall(expr, pt))
91-
case _ if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) =>
88+
def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = {
89+
90+
/** Set the position of all trees logically contained in the expansion of
91+
* inlined call `call` to the position of `call`. This transform is necessary
92+
* when lifting bindings from the expansion to the outside of the call.
93+
*/
94+
def liftFromInlined(call: Tree) = new TreeMap {
95+
override def transform(t: Tree)(implicit ctx: Context) = {
96+
t match {
97+
case Inlined(t, Nil, expr) if t.isEmpty => expr
98+
case _ => super.transform(t.withPos(call.pos))
99+
}
100+
}
101+
}
102+
103+
val bindings = new mutable.ListBuffer[Tree]
104+
105+
/** Lift bindings around inline call or in its function part to
106+
* the `bindings` buffer. This is done as an optimization to keep
107+
* inline call expansions smaller.
108+
*/
109+
def liftBindings(tree: Tree, liftPos: Tree => Tree): Tree = tree match {
110+
case Block(stats, expr) =>
111+
bindings ++= stats.map(liftPos)
112+
liftBindings(expr, liftPos)
113+
case Inlined(call, stats, expr) =>
114+
bindings ++= stats.map(liftPos)
115+
val lifter = liftFromInlined(call)
116+
cpy.Inlined(tree)(call, Nil, liftBindings(expr, liftFromInlined(call).transform(_)))
117+
case Apply(fn, args) =>
118+
cpy.Apply(tree)(liftBindings(fn, liftPos), args)
119+
case TypeApply(fn, args) =>
120+
cpy.TypeApply(tree)(liftBindings(fn, liftPos), args)
121+
case Select(qual, name) =>
122+
cpy.Select(tree)(liftBindings(qual, liftPos), name)
123+
case _ =>
124+
tree
125+
}
126+
127+
val tree1 = liftBindings(tree, identity)
128+
if (bindings.nonEmpty)
129+
cpy.Block(tree)(bindings.toList, inlineCall(tree1, pt))
130+
else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) {
92131
val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors
93132
if (ctx.reporter.hasErrors) tree
94133
else {
@@ -97,7 +136,8 @@ object Inliner {
97136
else ctx.fresh.setProperty(InlineBindings, newMutableSymbolMap[Tree])
98137
new Inliner(tree, body)(inlinerCtx).inlined(pt)
99138
}
100-
case _ =>
139+
}
140+
else
101141
errorTree(
102142
tree,
103143
i"""|Maximal number of successive inlines (${ctx.settings.XmaxInlines.value}) exceeded,
@@ -469,10 +509,13 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
469509
val argInPlace =
470510
if (trailing.isEmpty) arg
471511
else letBindUnless(TreeInfo.Pure, arg)(seq(trailing, _))
472-
seq(prefix, seq(leading, argInPlace))
512+
val fullArg = seq(prefix, seq(leading, argInPlace))
513+
new TreeTypeMap().transform(fullArg) // make sure local bindings in argument have fresh symbols
473514
.reporting(res => i"projecting $tree -> $res", inlining)
474515
}
475516
else tree
517+
case Block(stats, expr) if stats.forall(isPureBinding) =>
518+
cpy.Block(tree)(stats, reduceProjection(expr))
476519
case _ => tree
477520
}
478521
}
@@ -793,6 +836,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
793836
}
794837
countRefs.traverse(tree)
795838
for (binding <- bindings) countRefs.traverse(binding)
839+
840+
def retain(boundSym: Symbol) = {
841+
refCount.get(boundSym) match {
842+
case Some(x) => x > 1 || x == 1 && !boundSym.is(Method)
843+
case none => true
844+
}
845+
} && !boundSym.is(TransparentImplicitMethod)
846+
796847
val inlineBindings = new TreeMap {
797848
override def transform(t: Tree)(implicit ctx: Context) = t match {
798849
case t: RefTree =>
@@ -812,11 +863,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) {
812863
super.transform(t)
813864
}
814865
}
815-
def retain(binding: MemberDef) = refCount.get(binding.symbol) match {
816-
case Some(x) => x > 1 || x == 1 && !binding.symbol.is(Method)
817-
case none => true
818-
}
819-
val retained = bindings.filterConserve(retain)
866+
867+
val retained = bindings.filterConserve(binding => retain(binding.symbol))
820868
if (retained `eq` bindings) {
821869
(bindings, tree)
822870
}

compiler/src/dotty/tools/dotc/typer/PrepareTransparent.scala

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ object PrepareTransparent {
287287
!isLocalOrParam(tree.symbol, inlineMethod) &&
288288
!implicitRefTypes.contains(tree.tpe) =>
289289
if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod)))
290-
ctx.warning("implicit reference $tree is dropped at inline site because it refers to local symbol(s)", tree.pos)
290+
ctx.warning(i"implicit reference $tree is dropped at inline site because it refers to local symbol(s)", tree.pos)
291291
else {
292292
implicitRefTypes += tree.tpe
293293
implicitRefs += tree
@@ -417,11 +417,23 @@ object PrepareTransparent {
417417
val localImplicit = iref.symbol.asTerm.copy(
418418
owner = inlineMethod,
419419
name = UniqueInlineName.fresh(iref.symbol.name.asTermName),
420-
flags = Implicit | Method | Stable,
420+
flags = Implicit | Method | Stable | iref.symbol.flags & (Transparent | Erased),
421421
info = iref.tpe.widen.ensureMethodic,
422422
coord = inlineMethod.pos).asTerm
423-
polyDefDef(localImplicit, tps => vrefss =>
423+
val idef = polyDefDef(localImplicit, tps => vrefss =>
424424
iref.appliedToTypes(tps).appliedToArgss(vrefss))
425+
if (localImplicit.is(Transparent)) {
426+
// produce a Body annotation for inlining
427+
def untype(tree: Tree): untpd.Tree = tree match {
428+
case Apply(fn, args) => untpd.cpy.Apply(tree)(untype(fn), args)
429+
case TypeApply(fn, args) => untpd.cpy.TypeApply(tree)(untype(fn), args)
430+
case _ => untpd.TypedSplice(tree)
431+
}
432+
val inlineBody = tpd.UntypedSplice(untype(idef.rhs)).withType(idef.rhs.tpe)
433+
inlining.println(i"body annot for $idef: $inlineBody")
434+
localImplicit.addAnnotation(ConcreteBodyAnnotation(inlineBody))
435+
}
436+
idef
425437
}
426438
val untpdSplice = tpd.UntypedSplice(addRefs.transform(original)).withType(typed.tpe)
427439
seq(implicitBindings, untpdSplice)

tests/run/Tuple.scala

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import annotation.showAsInfix
2+
3+
sealed trait Tuple
4+
5+
object Tuple {
6+
object Empty extends Tuple
7+
8+
type Empty = Empty.type
9+
10+
@showAsInfix
11+
final case class *: [H, T <: Tuple](hd: H, tl: T) extends Tuple
12+
13+
class HListDeco(val xs: Tuple) extends AnyVal {
14+
transparent def *: [H] (x: H): Tuple = Tuple.*:.apply(x, xs)
15+
16+
transparent def size: Int = Tuple.size(xs)
17+
}
18+
19+
transparent def size(xs: Tuple): Int = xs match {
20+
case Empty => 0
21+
case _ *: xs1 => size(xs1) + 1
22+
}
23+
24+
transparent implicit def hlistDeco(xs: Tuple): HListDeco = new HListDeco(xs)
25+
26+
transparent def apply(): Tuple = Empty
27+
transparent def apply(x1: Any): Tuple = x1 *: Empty
28+
transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty
29+
30+
val xs0 = Tuple()
31+
val xs1 = Tuple(2)
32+
val xs2 = Tuple(2, "a")
33+
val s0 = xs0.size
34+
val s1 = xs1.size
35+
val s2 = xs2.size
36+
}
37+
38+
object Test extends App

0 commit comments

Comments
 (0)