aboutsummaryrefslogtreecommitdiff
path: root/compiler/src/dotty/tools/dotc/typer/Inliner.scala
diff options
context:
space:
mode:
authorFelix Mulder <felix.mulder@gmail.com>2016-11-02 11:08:28 +0100
committerGuillaume Martres <smarter@ubuntu.com>2016-11-22 01:35:07 +0100
commit8a61ff432543a29234193cd1f7c14abd3f3d31a0 (patch)
treea8147561d307af862c295cfc8100d271063bb0dd /compiler/src/dotty/tools/dotc/typer/Inliner.scala
parent6a455fe6da5ff9c741d91279a2dc6fe2fb1b472f (diff)
downloaddotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.gz
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.tar.bz2
dotty-8a61ff432543a29234193cd1f7c14abd3f3d31a0.zip
Move compiler and compiler tests to compiler dir
Diffstat (limited to 'compiler/src/dotty/tools/dotc/typer/Inliner.scala')
-rw-r--r--compiler/src/dotty/tools/dotc/typer/Inliner.scala539
1 files changed, 539 insertions, 0 deletions
diff --git a/compiler/src/dotty/tools/dotc/typer/Inliner.scala b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
new file mode 100644
index 000000000..3931fcaf4
--- /dev/null
+++ b/compiler/src/dotty/tools/dotc/typer/Inliner.scala
@@ -0,0 +1,539 @@
+package dotty.tools
+package dotc
+package typer
+
+import dotty.tools.dotc.ast.Trees.NamedArg
+import dotty.tools.dotc.ast.{Trees, untpd, tpd, TreeTypeMap}
+import Trees._
+import core._
+import Flags._
+import Symbols._
+import Types._
+import Decorators._
+import Constants._
+import StdNames.nme
+import Contexts.Context
+import Names.{Name, TermName}
+import NameOps._
+import SymDenotations.SymDenotation
+import Annotations._
+import transform.ExplicitOuter
+import Inferencing.fullyDefinedType
+import config.Printers.inlining
+import ErrorReporting.errorTree
+import collection.mutable
+import transform.TypeUtils._
+
+object Inliner {
+ import tpd._
+
+ /** Adds accessors accessors for all non-public term members accessed
+ * from `tree`. Non-public type members are currently left as they are.
+ * This means that references to a private type will lead to typing failures
+ * on the code when it is inlined. Less than ideal, but hard to do better (see below).
+ *
+ * @return If there are accessors generated, a thicket consisting of the rewritten `tree`
+ * and all accessors, otherwise the original tree.
+ */
+ private def makeInlineable(tree: Tree)(implicit ctx: Context) = {
+
+ /** A tree map which inserts accessors for all non-public term members accessed
+ * from inlined code. Accesors are collected in the `accessors` buffer.
+ */
+ object addAccessors extends TreeMap {
+ val inlineMethod = ctx.owner
+ val accessors = new mutable.ListBuffer[MemberDef]
+
+ /** A definition needs an accessor if it is private, protected, or qualified private */
+ def needsAccessor(sym: Symbol)(implicit ctx: Context) =
+ sym.is(AccessFlags) || sym.privateWithin.exists
+
+ /** The name of the next accessor to be generated */
+ def accessorName(implicit ctx: Context) =
+ ctx.freshNames.newName(inlineMethod.name.asTermName.inlineAccessorName.toString)
+
+ /** A fresh accessor symbol.
+ *
+ * @param tree The tree representing the original access to the non-public member
+ * @param accessorInfo The type of the accessor
+ */
+ def accessorSymbol(tree: Tree, accessorInfo: Type)(implicit ctx: Context): Symbol =
+ ctx.newSymbol(
+ owner = inlineMethod.owner,
+ name = if (tree.isTerm) accessorName.toTermName else accessorName.toTypeName,
+ flags = if (tree.isTerm) Synthetic | Method else Synthetic,
+ info = accessorInfo,
+ coord = tree.pos).entered
+
+ /** Add an accessor to a non-public method and replace the original access with a
+ * call to the accessor.
+ *
+ * @param tree The original access to the non-public symbol
+ * @param refPart The part that refers to the method or field of the original access
+ * @param targs All type arguments passed in the access, if any
+ * @param argss All value arguments passed in the access, if any
+ * @param accessedType The type of the accessed method or field, as seen from the access site.
+ * @param rhs A function that builds the right-hand side of the accessor,
+ * given a reference to the accessed symbol and any type and
+ * value arguments the need to be integrated.
+ * @return The call to the accessor method that replaces the original access.
+ */
+ def addAccessor(tree: Tree, refPart: Tree, targs: List[Tree], argss: List[List[Tree]],
+ accessedType: Type, rhs: (Tree, List[Type], List[List[Tree]]) => Tree)(implicit ctx: Context): Tree = {
+ val qual = qualifier(refPart)
+ def refIsLocal = qual match {
+ case qual: This => qual.symbol == refPart.symbol.owner
+ case _ => false
+ }
+ val (accessorDef, accessorRef) =
+ if (refPart.symbol.isStatic || refIsLocal) {
+ // Easy case: Reference to a static symbol or a symbol referenced via `this.`
+ val accessorType = accessedType.ensureMethodic
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(refPart, tps, argss))
+ val accessorRef = ref(accessor).appliedToTypeTrees(targs).appliedToArgss(argss)
+ (accessorDef, accessorRef)
+ } else {
+ // Hard case: Reference needs to go via a dynamic prefix
+ inlining.println(i"adding inline accessor for $tree -> (${qual.tpe}, $refPart: ${refPart.getClass}, [$targs%, %], ($argss%, %))")
+
+ // Need to dealias in order to catch all possible references to abstracted over types in
+ // substitutions
+ val dealiasMap = new TypeMap {
+ def apply(t: Type) = mapOver(t.dealias)
+ }
+
+ val qualType = dealiasMap(qual.tpe.widen)
+
+ // Add qualifier type as leading method argument to argument `tp`
+ def addQualType(tp: Type): Type = tp match {
+ case tp: PolyType => tp.derivedPolyType(tp.paramNames, tp.paramBounds, addQualType(tp.resultType))
+ case tp: ExprType => addQualType(tp.resultType)
+ case tp => MethodType(qualType :: Nil, tp)
+ }
+
+ // The types that are local to the inlined method, and that therefore have
+ // to be abstracted out in the accessor, which is external to the inlined method
+ val localRefs = qualType.namedPartsWith(_.symbol.isContainedIn(inlineMethod)).toList
+
+ // Abstract accessed type over local refs
+ def abstractQualType(mtpe: Type): Type =
+ if (localRefs.isEmpty) mtpe
+ else mtpe.LambdaAbstract(localRefs.map(_.symbol)).asInstanceOf[PolyType].flatten
+
+ val accessorType = abstractQualType(addQualType(dealiasMap(accessedType)))
+ val accessor = accessorSymbol(tree, accessorType).asTerm
+
+ val accessorDef = polyDefDef(accessor, tps => argss =>
+ rhs(argss.head.head.select(refPart.symbol), tps.drop(localRefs.length), argss.tail))
+
+ val accessorRef = ref(accessor)
+ .appliedToTypeTrees(localRefs.map(TypeTree(_)) ++ targs)
+ .appliedToArgss((qual :: Nil) :: argss)
+ (accessorDef, accessorRef)
+ }
+ accessors += accessorDef
+ inlining.println(i"added inline accessor: $accessorDef")
+ accessorRef
+ }
+
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = super.transform {
+ tree match {
+ case _: Apply | _: TypeApply | _: RefTree if needsAccessor(tree.symbol) =>
+ if (tree.isTerm) {
+ val (methPart, targs, argss) = decomposeCall(tree)
+ addAccessor(tree, methPart, targs, argss,
+ accessedType = methPart.tpe.widen,
+ rhs = (qual, tps, argss) => qual.appliedToTypes(tps).appliedToArgss(argss))
+ } else {
+ // TODO: Handle references to non-public types.
+ // This is quite tricky, as such types can appear anywhere, including as parts
+ // of types of other things. For the moment we do nothing and complain
+ // at the implicit expansion site if there's a reference to an inaccessible type.
+ // Draft code (incomplete):
+ //
+ // val accessor = accessorSymbol(tree, TypeAlias(tree.tpe)).asType
+ // myAccessors += TypeDef(accessor)
+ // ref(accessor)
+ //
+ tree
+ }
+ case Assign(lhs: RefTree, rhs) if needsAccessor(lhs.symbol) =>
+ addAccessor(tree, lhs, Nil, (rhs :: Nil) :: Nil,
+ accessedType = MethodType(rhs.tpe.widen :: Nil, defn.UnitType),
+ rhs = (lhs, tps, argss) => lhs.becomes(argss.head.head))
+ case _ => tree
+ }
+ }
+ }
+
+ val tree1 = addAccessors.transform(tree)
+ flatTree(tree1 :: addAccessors.accessors.toList)
+ }
+
+ /** Register inline info for given inline method `sym`.
+ *
+ * @param sym The symbol denotatioon of the inline method for which info is registered
+ * @param treeExpr A function that computes the tree to be inlined, given a context
+ * This tree may still refer to non-public members.
+ * @param ctx The context to use for evaluating `treeExpr`. It needs
+ * to have the inlined method as owner.
+ */
+ def registerInlineInfo(
+ sym: SymDenotation, treeExpr: Context => Tree)(implicit ctx: Context): Unit = {
+ sym.unforcedAnnotation(defn.BodyAnnot) match {
+ case Some(ann: ConcreteBodyAnnotation) =>
+ case Some(ann: LazyBodyAnnotation) if ann.isEvaluated =>
+ case _ =>
+ if (!ctx.isAfterTyper) {
+ val inlineCtx = ctx
+ sym.updateAnnotation(LazyBodyAnnotation { _ =>
+ implicit val ctx: Context = inlineCtx
+ ctx.withNoError(treeExpr(ctx))(makeInlineable)
+ })
+ }
+ }
+ }
+
+ /** `sym` has an inline method with a known body to inline (note: definitions coming
+ * from Scala2x class files might be `@inline`, but still lack that body.
+ */
+ def hasBodyToInline(sym: SymDenotation)(implicit ctx: Context): Boolean =
+ sym.isInlineMethod && sym.hasAnnotation(defn.BodyAnnot)
+
+ private def bodyAndAccessors(sym: SymDenotation)(implicit ctx: Context): (Tree, List[MemberDef]) =
+ sym.unforcedAnnotation(defn.BodyAnnot).get.tree match {
+ case Thicket(body :: accessors) => (body, accessors.asInstanceOf[List[MemberDef]])
+ case body => (body, Nil)
+ }
+
+ /** The body to inline for method `sym`.
+ * @pre hasBodyToInline(sym)
+ */
+ def bodyToInline(sym: SymDenotation)(implicit ctx: Context): Tree =
+ bodyAndAccessors(sym)._1
+
+ /** The accessors to non-public members needed by the inlinable body of `sym`.
+ * These accessors are dropped as a side effect of calling this method.
+ * @pre hasBodyToInline(sym)
+ */
+ def removeInlineAccessors(sym: SymDenotation)(implicit ctx: Context): List[MemberDef] = {
+ val (body, accessors) = bodyAndAccessors(sym)
+ if (accessors.nonEmpty) sym.updateAnnotation(ConcreteBodyAnnotation(body))
+ accessors
+ }
+
+ /** Try to inline a call to a `@inline` method. Fail with error if the maximal
+ * inline depth is exceeded.
+ *
+ * @param tree The call to inline
+ * @param pt The expected type of the call.
+ * @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 =
+ if (enclosingInlineds.length < ctx.settings.xmaxInlines.value)
+ new Inliner(tree, bodyToInline(tree.symbol)).inlined(pt)
+ else errorTree(
+ tree,
+ i"""|Maximal number of successive inlines (${ctx.settings.xmaxInlines.value}) exceeded,
+ |Maybe this is caused by a recursive inline method?
+ |You can use -Xmax:inlines to change the limit."""
+ )
+
+ /** Replace `Inlined` node by a block that contains its bindings and expansion */
+ def dropInlined(inlined: tpd.Inlined)(implicit ctx: Context): Tree = {
+ val reposition = new TreeMap {
+ override def transform(tree: Tree)(implicit ctx: Context): Tree = {
+ super.transform(tree).withPos(inlined.call.pos)
+ }
+ }
+ tpd.seq(inlined.bindings, reposition.transform(inlined.expansion))
+ }
+
+ /** The qualifier part of a Select or Ident.
+ * For an Ident, this is the `This` of the current class. (TODO: use elsewhere as well?)
+ */
+ private def qualifier(tree: Tree)(implicit ctx: Context) = tree match {
+ case Select(qual, _) => qual
+ case _ => This(ctx.owner.enclosingClass.asClass)
+ }
+}
+
+/** Produces an inlined version of `call` via its `inlined` method.
+ *
+ * @param call The original call to a `@inline` method
+ * @param rhs The body of the inline method that replaces the call.
+ */
+class Inliner(call: tpd.Tree, rhs: tpd.Tree)(implicit ctx: Context) {
+ import tpd._
+ import Inliner._
+
+ private val (methPart, targs, argss) = decomposeCall(call)
+ private val meth = methPart.symbol
+ private val prefix = qualifier(methPart)
+
+ // Make sure all type arguments to the call are fully determined
+ for (targ <- targs) fullyDefinedType(targ.tpe, "inlined type argument", targ.pos)
+
+ /** A map from parameter names of the inline method to references of the actual arguments.
+ * For a type argument this is the full argument type.
+ * For a value argument, it is a reference to either the argument value
+ * (if the argument is a pure expression of singleton type), or to `val` or `def` acting
+ * as a proxy (if the argument is something else).
+ */
+ private val paramBinding = new mutable.HashMap[Name, Type]
+
+ /** A map from references to (type and value) parameters of the inline method
+ * to their corresponding argument or proxy references, as given by `paramBinding`.
+ */
+ private val paramProxy = new mutable.HashMap[Type, Type]
+
+ /** A map from the classes of (direct and outer) this references in `rhs`
+ * to references of their proxies.
+ * Note that we can't index by the ThisType itself since there are several
+ * possible forms to express what is logicaly the same ThisType. E.g.
+ *
+ * ThisType(TypeRef(ThisType(p), cls))
+ *
+ * vs
+ *
+ * ThisType(TypeRef(TermRef(ThisType(<root>), p), cls))
+ *
+ * These are different (wrt ==) types but represent logically the same key
+ */
+ private val thisProxy = new mutable.HashMap[ClassSymbol, TermRef]
+
+ /** A buffer for bindings that define proxies for actual arguments */
+ val bindingsBuf = new mutable.ListBuffer[ValOrDefDef]
+
+ computeParamBindings(meth.info, targs, argss)
+
+ private def newSym(name: Name, flags: FlagSet, info: Type): Symbol =
+ ctx.newSymbol(ctx.owner, name, flags, info, coord = call.pos)
+
+ /** Populate `paramBinding` and `bindingsBuf` by matching parameters with
+ * corresponding arguments. `bindingbuf` will be further extended later by
+ * proxies to this-references.
+ */
+ private def computeParamBindings(tp: Type, targs: List[Tree], argss: List[List[Tree]]): Unit = tp match {
+ case tp: PolyType =>
+ (tp.paramNames, targs).zipped.foreach { (name, arg) =>
+ paramBinding(name) = arg.tpe.stripTypeVar
+ }
+ computeParamBindings(tp.resultType, Nil, argss)
+ case tp: MethodType =>
+ (tp.paramNames, tp.paramTypes, argss.head).zipped.foreach { (name, paramtp, arg) =>
+ def isByName = paramtp.dealias.isInstanceOf[ExprType]
+ paramBinding(name) = arg.tpe.stripAnnots.stripTypeVar match {
+ case argtpe: SingletonType if isByName || isIdempotentExpr(arg) => argtpe
+ case argtpe =>
+ val inlineFlag = if (paramtp.hasAnnotation(defn.InlineParamAnnot)) Inline else EmptyFlags
+ val (bindingFlags, bindingType) =
+ if (isByName) (inlineFlag | Method, ExprType(argtpe.widen))
+ else (inlineFlag, argtpe.widen)
+ val boundSym = newSym(name, bindingFlags, bindingType).asTerm
+ val binding =
+ if (isByName) DefDef(boundSym, arg.changeOwner(ctx.owner, boundSym))
+ else ValDef(boundSym, arg)
+ bindingsBuf += binding
+ boundSym.termRef
+ }
+ }
+ computeParamBindings(tp.resultType, targs, argss.tail)
+ case _ =>
+ assert(targs.isEmpty)
+ assert(argss.isEmpty)
+ }
+
+ /** Populate `thisProxy` and `paramProxy` as follows:
+ *
+ * 1a. If given type refers to a static this, thisProxy binds it to corresponding global reference,
+ * 1b. If given type refers to an instance this, create a proxy symbol and bind the thistype to
+ * refer to the proxy. The proxy is not yet entered in `bindingsBuf` that will come later.
+ * 2. If given type refers to a parameter, make `paramProxy` refer to the entry stored
+ * in `paramNames` under the parameter's name. This roundabout way to bind parameter
+ * references to proxies is done because we not known a priori what the parameter
+ * references of a method are (we only know the method's type, but that contains PolyParams
+ * and MethodParams, not TypeRefs or TermRefs.
+ */
+ private def registerType(tpe: Type): Unit = tpe match {
+ case tpe: ThisType
+ if !ctx.owner.isContainedIn(tpe.cls) && !tpe.cls.is(Package) &&
+ !thisProxy.contains(tpe.cls) =>
+ if (tpe.cls.isStaticOwner)
+ thisProxy(tpe.cls) = tpe.cls.sourceModule.termRef
+ else {
+ val proxyName = s"${tpe.cls.name}_this".toTermName
+ val proxyType = tpe.asSeenFrom(prefix.tpe, meth.owner)
+ thisProxy(tpe.cls) = newSym(proxyName, EmptyFlags, proxyType).termRef
+ registerType(meth.owner.thisType) // make sure we have a base from which to outer-select
+ }
+ case tpe: NamedType
+ if tpe.symbol.is(Param) && tpe.symbol.owner == meth &&
+ !paramProxy.contains(tpe) =>
+ paramProxy(tpe) = paramBinding(tpe.name)
+ case _ =>
+ }
+
+ /** Register type of leaf node */
+ private def registerLeaf(tree: Tree): Unit = tree match {
+ case _: This | _: Ident | _: TypeTree =>
+ tree.tpe.foreachPart(registerType, stopAtStatic = true)
+ case _ =>
+ }
+
+ /** The Inlined node representing the inlined call */
+ def inlined(pt: Type) = {
+ // make sure prefix is executed if it is impure
+ if (!isIdempotentExpr(prefix)) registerType(meth.owner.thisType)
+
+ // Register types of all leaves of inlined body so that the `paramProxy` and `thisProxy` maps are defined.
+ rhs.foreachSubTree(registerLeaf)
+
+ // The class that the this-proxy `selfSym` represents
+ def classOf(selfSym: Symbol) = selfSym.info.widen.classSymbol
+
+ // The name of the outer selector that computes the rhs of `selfSym`
+ def outerSelector(selfSym: Symbol): TermName = classOf(selfSym).name.toTermName ++ nme.OUTER_SELECT
+
+ // The total nesting depth of the class represented by `selfSym`.
+ def outerLevel(selfSym: Symbol): Int = classOf(selfSym).ownersIterator.length
+
+ // All needed this-proxies, sorted by nesting depth of the classes they represent (innermost first)
+ val accessedSelfSyms = thisProxy.values.toList.map(_.symbol).sortBy(-outerLevel(_))
+
+ // Compute val-definitions for all this-proxies and append them to `bindingsBuf`
+ var lastSelf: Symbol = NoSymbol
+ for (selfSym <- accessedSelfSyms) {
+ val rhs =
+ if (!lastSelf.exists)
+ prefix
+ else
+ untpd.Select(ref(lastSelf), outerSelector(selfSym)).withType(selfSym.info)
+ bindingsBuf += ValDef(selfSym.asTerm, rhs)
+ lastSelf = selfSym
+ }
+
+ // The type map to apply to the inlined tree. This maps references to this-types
+ // and parameters to type references of their arguments or proxies.
+ val typeMap = new TypeMap {
+ def apply(t: Type) = t match {
+ case t: ThisType => thisProxy.getOrElse(t.cls, t)
+ case t: TypeRef => paramProxy.getOrElse(t, mapOver(t))
+ case t: SingletonType => paramProxy.getOrElse(t, mapOver(t))
+ case t => mapOver(t)
+ }
+ }
+
+ // The tree map to apply to the inlined tree. This maps references to this-types
+ // and parameters to references of their arguments or their proxies.
+ def treeMap(tree: Tree) = {
+ tree match {
+ case _: This =>
+ tree.tpe match {
+ case thistpe: ThisType =>
+ thisProxy.get(thistpe.cls) match {
+ case Some(t) => ref(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _ => tree
+ }
+ case _: Ident =>
+ paramProxy.get(tree.tpe) match {
+ case Some(t: SingletonType) if tree.isTerm => singleton(t).withPos(tree.pos)
+ case Some(t) if tree.isType => TypeTree(t).withPos(tree.pos)
+ case None => tree
+ }
+ case _ => tree
+ }}
+
+ // The complete translation maps referenves to this and parameters to
+ // corresponding arguments or proxies on the type and term level. It also changes
+ // the owner from the inlined method to the current owner.
+ val inliner = new TreeTypeMap(typeMap, treeMap, meth :: Nil, ctx.owner :: Nil)
+
+ val expansion = inliner(rhs.withPos(call.pos))
+ ctx.traceIndented(i"inlining $call\n, BINDINGS =\n${bindingsBuf.toList}%\n%\nEXPANSION =\n$expansion", inlining, show = true) {
+
+ // The final expansion runs a typing pass over the inlined tree. See InlineTyper for details.
+ val expansion1 = InlineTyper.typed(expansion, pt)(inlineContext(call))
+
+ /** Does given definition bind a closure that will be inlined? */
+ def bindsDeadClosure(defn: ValOrDefDef) = Ident(defn.symbol.termRef) match {
+ case InlineableClosure(_) => !InlineTyper.retainedClosures.contains(defn.symbol)
+ case _ => false
+ }
+
+ /** All bindings in `bindingsBuf` except bindings of inlineable closures */
+ val bindings = bindingsBuf.toList.filterNot(bindsDeadClosure).map(_.withPos(call.pos))
+
+ tpd.Inlined(call, bindings, expansion1)
+ }
+ }
+
+ /** An extractor for references to closure arguments that refer to `@inline` methods */
+ private object InlineableClosure {
+ lazy val paramProxies = paramProxy.values.toSet
+ def unapply(tree: Ident)(implicit ctx: Context): Option[Tree] =
+ if (paramProxies.contains(tree.tpe)) {
+ bindingsBuf.find(_.name == tree.name) match {
+ case Some(ddef: ValDef) if ddef.symbol.is(Inline) =>
+ ddef.rhs match {
+ case closure(_, meth, _) => Some(meth)
+ case _ => None
+ }
+ case _ => None
+ }
+ } else None
+ }
+
+ /** A typer for inlined code. Its purpose is:
+ * 1. Implement constant folding over inlined code
+ * 2. Selectively expand ifs with constant conditions
+ * 3. Inline arguments that are inlineable closures
+ * 4. Make sure inlined code is type-correct.
+ * 5. Make sure that the tree's typing is idempotent (so that future -Ycheck passes succeed)
+ */
+ private object InlineTyper extends ReTyper {
+
+ var retainedClosures = Set[Symbol]()
+
+ override def typedIdent(tree: untpd.Ident, pt: Type)(implicit ctx: Context) = {
+ val tree1 = super.typedIdent(tree, pt)
+ tree1 match {
+ case InlineableClosure(_) => retainedClosures += tree.symbol
+ case _ =>
+ }
+ tree1
+ }
+
+ override def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = {
+ val res = super.typedSelect(tree, pt)
+ ensureAccessible(res.tpe, tree.qualifier.isInstanceOf[untpd.Super], tree.pos)
+ res
+ }
+
+ override def typedIf(tree: untpd.If, pt: Type)(implicit ctx: Context) = {
+ val cond1 = typed(tree.cond, defn.BooleanType)
+ cond1.tpe.widenTermRefExpr match {
+ case ConstantType(Constant(condVal: Boolean)) =>
+ val selected = typed(if (condVal) tree.thenp else tree.elsep, pt)
+ if (isIdempotentExpr(cond1)) selected
+ else Block(cond1 :: Nil, selected)
+ case _ =>
+ val if1 = untpd.cpy.If(tree)(cond = untpd.TypedSplice(cond1))
+ super.typedIf(if1, pt)
+ }
+ }
+
+ override def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context) = tree.asInstanceOf[tpd.Tree] match {
+ case Apply(Select(InlineableClosure(fn), nme.apply), args) =>
+ inlining.println(i"reducing $tree with closure $fn")
+ typed(fn.appliedToArgs(args), pt)
+ case _ =>
+ super.typedApply(tree, pt)
+ }
+ }
+}