diff --git a/compiler/src/dotty/tools/dotc/core/Flags.scala b/compiler/src/dotty/tools/dotc/core/Flags.scala index f6f949cf6e32..8d894bdfa034 100644 --- a/compiler/src/dotty/tools/dotc/core/Flags.scala +++ b/compiler/src/dotty/tools/dotc/core/Flags.scala @@ -560,6 +560,9 @@ object Flags { /** A transparent method */ final val TransparentMethod = allOf(Transparent, Method) + /** A transparent implicit method */ + final val TransparentImplicitMethod = allOf(Transparent, Implicit, Method) + /** A transparent parameter */ final val TransparentParam = allOf(Transparent, Param) diff --git a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala index 9990d1d9ea8f..6797e57ea15d 100644 --- a/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala +++ b/compiler/src/dotty/tools/dotc/printing/RefinedPrinter.scala @@ -165,7 +165,7 @@ class RefinedPrinter(_ctx: Context) extends PlainPrinter(_ctx) { val leftArg = if (isRightAssoc && isInfixType(l)) "(" ~ argText(l) ~ ")" else argText(l) val rightArg = if (!isRightAssoc && isInfixType(r)) "(" ~ argText(r) ~ ")" else argText(r) - leftArg ~ " " ~ toTextLocal(op) ~ " " ~ rightArg + leftArg ~ " " ~ simpleNameString(op.classSymbol) ~ " " ~ rightArg } homogenize(tp) match { diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala index 516ded001526..8731515f06cd 100644 --- a/compiler/src/dotty/tools/dotc/typer/Inliner.scala +++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala @@ -85,10 +85,49 @@ object Inliner { * @return An `Inlined` node that refers to the original call and the inlined bindings * and body that replace it. */ - def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = tree match { - case Block(stats, expr) => - cpy.Block(tree)(stats, inlineCall(expr, pt)) - case _ if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) => + def inlineCall(tree: Tree, pt: Type)(implicit ctx: Context): Tree = { + + /** Set the position of all trees logically contained in the expansion of + * inlined call `call` to the position of `call`. This transform is necessary + * when lifting bindings from the expansion to the outside of the call. + */ + def liftFromInlined(call: Tree) = new TreeMap { + override def transform(t: Tree)(implicit ctx: Context) = { + t match { + case Inlined(t, Nil, expr) if t.isEmpty => expr + case _ => super.transform(t.withPos(call.pos)) + } + } + } + + val bindings = new mutable.ListBuffer[Tree] + + /** Lift bindings around inline call or in its function part to + * the `bindings` buffer. This is done as an optimization to keep + * inline call expansions smaller. + */ + def liftBindings(tree: Tree, liftPos: Tree => Tree): Tree = tree match { + case Block(stats, expr) => + bindings ++= stats.map(liftPos) + liftBindings(expr, liftPos) + case Inlined(call, stats, expr) => + bindings ++= stats.map(liftPos) + val lifter = liftFromInlined(call) + cpy.Inlined(tree)(call, Nil, liftBindings(expr, liftFromInlined(call).transform(_))) + case Apply(fn, args) => + cpy.Apply(tree)(liftBindings(fn, liftPos), args) + case TypeApply(fn, args) => + cpy.TypeApply(tree)(liftBindings(fn, liftPos), args) + case Select(qual, name) => + cpy.Select(tree)(liftBindings(qual, liftPos), name) + case _ => + tree + } + + val tree1 = liftBindings(tree, identity) + if (bindings.nonEmpty) + cpy.Block(tree)(bindings.toList, inlineCall(tree1, pt)) + else if (enclosingInlineds.length < ctx.settings.XmaxInlines.value) { val body = bodyToInline(tree.symbol) // can typecheck the tree and thereby produce errors if (ctx.reporter.hasErrors) tree else { @@ -97,7 +136,8 @@ object Inliner { else ctx.fresh.setProperty(InlineBindings, newMutableSymbolMap[Tree]) new Inliner(tree, body)(inlinerCtx).inlined(pt) } - case _ => + } + else errorTree( tree, 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) { val argInPlace = if (trailing.isEmpty) arg else letBindUnless(TreeInfo.Pure, arg)(seq(trailing, _)) - seq(prefix, seq(leading, argInPlace)) + val fullArg = seq(prefix, seq(leading, argInPlace)) + new TreeTypeMap().transform(fullArg) // make sure local bindings in argument have fresh symbols .reporting(res => i"projecting $tree -> $res", inlining) } else tree + case Block(stats, expr) if stats.forall(isPureBinding) => + cpy.Block(tree)(stats, reduceProjection(expr)) case _ => tree } } @@ -793,6 +836,14 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { } countRefs.traverse(tree) for (binding <- bindings) countRefs.traverse(binding) + + def retain(boundSym: Symbol) = { + refCount.get(boundSym) match { + case Some(x) => x > 1 || x == 1 && !boundSym.is(Method) + case none => true + } + } && !boundSym.is(TransparentImplicitMethod) + val inlineBindings = new TreeMap { override def transform(t: Tree)(implicit ctx: Context) = t match { case t: RefTree => @@ -812,11 +863,8 @@ class Inliner(call: tpd.Tree, rhsToInline: tpd.Tree)(implicit ctx: Context) { super.transform(t) } } - def retain(binding: MemberDef) = refCount.get(binding.symbol) match { - case Some(x) => x > 1 || x == 1 && !binding.symbol.is(Method) - case none => true - } - val retained = bindings.filterConserve(retain) + + val retained = bindings.filterConserve(binding => retain(binding.symbol)) if (retained `eq` bindings) { (bindings, tree) } diff --git a/compiler/src/dotty/tools/dotc/typer/PrepareTransparent.scala b/compiler/src/dotty/tools/dotc/typer/PrepareTransparent.scala index 927ce6f4f053..9219853fa309 100644 --- a/compiler/src/dotty/tools/dotc/typer/PrepareTransparent.scala +++ b/compiler/src/dotty/tools/dotc/typer/PrepareTransparent.scala @@ -287,7 +287,7 @@ object PrepareTransparent { !isLocalOrParam(tree.symbol, inlineMethod) && !implicitRefTypes.contains(tree.tpe) => if (tree.existsSubTree(t => isLocal(tree.symbol, inlineMethod))) - ctx.warning("implicit reference $tree is dropped at inline site because it refers to local symbol(s)", tree.pos) + ctx.warning(i"implicit reference $tree is dropped at inline site because it refers to local symbol(s)", tree.pos) else { implicitRefTypes += tree.tpe implicitRefs += tree @@ -417,11 +417,23 @@ object PrepareTransparent { val localImplicit = iref.symbol.asTerm.copy( owner = inlineMethod, name = UniqueInlineName.fresh(iref.symbol.name.asTermName), - flags = Implicit | Method | Stable, + flags = Implicit | Method | Stable | iref.symbol.flags & (Transparent | Erased), info = iref.tpe.widen.ensureMethodic, coord = inlineMethod.pos).asTerm - polyDefDef(localImplicit, tps => vrefss => + val idef = polyDefDef(localImplicit, tps => vrefss => iref.appliedToTypes(tps).appliedToArgss(vrefss)) + if (localImplicit.is(Transparent)) { + // produce a Body annotation for inlining + def untype(tree: Tree): untpd.Tree = tree match { + case Apply(fn, args) => untpd.cpy.Apply(tree)(untype(fn), args) + case TypeApply(fn, args) => untpd.cpy.TypeApply(tree)(untype(fn), args) + case _ => untpd.TypedSplice(tree) + } + val inlineBody = tpd.UntypedSplice(untype(idef.rhs)).withType(idef.rhs.tpe) + inlining.println(i"body annot for $idef: $inlineBody") + localImplicit.addAnnotation(ConcreteBodyAnnotation(inlineBody)) + } + idef } val untpdSplice = tpd.UntypedSplice(addRefs.transform(original)).withType(typed.tpe) seq(implicitBindings, untpdSplice) diff --git a/tests/run/Tuple.scala b/tests/run/Tuple.scala new file mode 100644 index 000000000000..34eadf2b45bc --- /dev/null +++ b/tests/run/Tuple.scala @@ -0,0 +1,38 @@ +import annotation.showAsInfix + +sealed trait Tuple + +object Tuple { + object Empty extends Tuple + + type Empty = Empty.type + + @showAsInfix + final case class *: [H, T <: Tuple](hd: H, tl: T) extends Tuple + + class HListDeco(val xs: Tuple) extends AnyVal { + transparent def *: [H] (x: H): Tuple = Tuple.*:.apply(x, xs) + + transparent def size: Int = Tuple.size(xs) + } + + transparent def size(xs: Tuple): Int = xs match { + case Empty => 0 + case _ *: xs1 => size(xs1) + 1 + } + + transparent implicit def hlistDeco(xs: Tuple): HListDeco = new HListDeco(xs) + + transparent def apply(): Tuple = Empty + transparent def apply(x1: Any): Tuple = x1 *: Empty + transparent def apply(x1: Any, x2: Any) = x1 *: x2 *: Empty + + val xs0 = Tuple() + val xs1 = Tuple(2) + val xs2 = Tuple(2, "a") + val s0 = xs0.size + val s1 = xs1.size + val s2 = xs2.size +} + +object Test extends App \ No newline at end of file