diff options
Diffstat (limited to 'src/main')
12 files changed, 459 insertions, 453 deletions
diff --git a/src/main/scala/scala/async/Async.scala b/src/main/scala/scala/async/Async.scala index 17a63a4..bacf70a 100644 --- a/src/main/scala/scala/async/Async.scala +++ b/src/main/scala/scala/async/Async.scala @@ -6,7 +6,7 @@ package scala.async import scala.language.experimental.macros import scala.concurrent.{Future, ExecutionContext} -import scala.reflect.internal.annotations.compileTimeOnly +import scala.annotation.compileTimeOnly /** * Async blocks provide a direct means to work with [[scala.concurrent.Future]]. diff --git a/src/main/scala/scala/async/internal/AnfTransform.scala b/src/main/scala/scala/async/internal/AnfTransform.scala index d879c7b..e54eb54 100644 --- a/src/main/scala/scala/async/internal/AnfTransform.scala +++ b/src/main/scala/scala/async/internal/AnfTransform.scala @@ -5,280 +5,262 @@ package scala.async.internal -import scala.tools.nsc.Global import scala.Predef._ +import scala.reflect.internal.util.Collections.map2 private[async] trait AnfTransform { self: AsyncMacro => - import global._ - import reflect.internal.Flags._ + import c.universe._ + import Flag._ + import c.internal._ + import decorators._ def anfTransform(tree: Tree): Block = { // Must prepend the () for issue #31. - val block = callSiteTyper.typedPos(tree.pos)(Block(List(Literal(Constant(()))), tree)).setType(tree.tpe) + val block = c.typecheck(atPos(tree.pos)(Block(List(Literal(Constant(()))), tree))).setType(tree.tpe) - new SelectiveAnfTransform().transform(block) - } - - sealed abstract class AnfMode - - case object Anf extends AnfMode - - case object Linearizing extends AnfMode + sealed abstract class AnfMode + case object Anf extends AnfMode + case object Linearizing extends AnfMode - final class SelectiveAnfTransform extends MacroTypingTransformer { var mode: AnfMode = Anf - - def blockToList(tree: Tree): List[Tree] = tree match { - case Block(stats, expr) => stats :+ expr - case t => t :: Nil - } - - def listToBlock(trees: List[Tree]): Block = trees match { - case trees @ (init :+ last) => - val pos = trees.map(_.pos).reduceLeft(_ union _) - Block(init, last).setType(last.tpe).setPos(pos) - } - - override def transform(tree: Tree): Block = { - def anfLinearize: Block = { - val trees: List[Tree] = mode match { - case Anf => anf._transformToList(tree) - case Linearizing => linearize._transformToList(tree) - } - listToBlock(trees) - } - tree match { - case _: ValDef | _: DefDef | _: Function | _: ClassDef | _: TypeDef => - atOwner(tree.symbol)(anfLinearize) - case _: ModuleDef => - atOwner(tree.symbol.moduleClass orElse tree.symbol)(anfLinearize) - case _ => - anfLinearize + typingTransform(block)((tree, api) => { + def blockToList(tree: Tree): List[Tree] = tree match { + case Block(stats, expr) => stats :+ expr + case t => t :: Nil } - } - private object linearize { - def transformToList(tree: Tree): List[Tree] = { - mode = Linearizing; blockToList(transform(tree)) + def listToBlock(trees: List[Tree]): Block = trees match { + case trees @ (init :+ last) => + val pos = trees.map(_.pos).reduceLeft(_ union _) + Block(init, last).setType(last.tpe).setPos(pos) } - def transformToBlock(tree: Tree): Block = listToBlock(transformToList(tree)) - - def _transformToList(tree: Tree): List[Tree] = trace(tree) { - val stats :+ expr = anf.transformToList(tree) - def statsExprUnit = - stats :+ expr :+ localTyper.typedPos(expr.pos)(Literal(Constant(()))) - expr match { - case Apply(fun, args) if isAwait(fun) => - val valDef = defineVal(name.await, expr, tree.pos) - stats :+ valDef :+ gen.mkAttributedStableRef(valDef.symbol).setType(tree.tpe).setPos(tree.pos) - - case If(cond, thenp, elsep) => - // if type of if-else is Unit don't introduce assignment, - // but add Unit value to bring it into form expected by async transform - if (expr.tpe =:= definitions.UnitTpe) { + object linearize { + def transformToList(tree: Tree): List[Tree] = { + mode = Linearizing; blockToList(api.recur(tree)) + } + + def transformToBlock(tree: Tree): Block = listToBlock(transformToList(tree)) + + def _transformToList(tree: Tree): List[Tree] = trace(tree) { + val stats :+ expr = anf.transformToList(tree) + def statsExprUnit = + stats :+ expr :+ api.typecheck(atPos(expr.pos)(Literal(Constant(())))) + expr match { + case Apply(fun, args) if isAwait(fun) => + val valDef = defineVal(name.await, expr, tree.pos) + stats :+ valDef :+ gen.mkAttributedStableRef(valDef.symbol).setType(tree.tpe).setPos(tree.pos) + + case If(cond, thenp, elsep) => + // if type of if-else is Unit don't introduce assignment, + // but add Unit value to bring it into form expected by async transform + if (expr.tpe =:= definitions.UnitTpe) { + statsExprUnit + } else { + val varDef = defineVar(name.ifRes, expr.tpe, tree.pos) + def branchWithAssign(orig: Tree) = api.typecheck(atPos(orig.pos) { + def cast(t: Tree) = mkAttributedCastPreservingAnnotations(t, tpe(varDef.symbol)) + orig match { + case Block(thenStats, thenExpr) => Block(thenStats, Assign(Ident(varDef.symbol), cast(thenExpr))) + case _ => Assign(Ident(varDef.symbol), cast(orig)) + } + }) + val ifWithAssign = treeCopy.If(tree, cond, branchWithAssign(thenp), branchWithAssign(elsep)).setType(definitions.UnitTpe) + stats :+ varDef :+ ifWithAssign :+ gen.mkAttributedStableRef(varDef.symbol).setType(tree.tpe).setPos(tree.pos) + } + case LabelDef(name, params, rhs) => statsExprUnit - } else { - val varDef = defineVar(name.ifRes, expr.tpe, tree.pos) - def branchWithAssign(orig: Tree) = localTyper.typedPos(orig.pos) { - def cast(t: Tree) = mkAttributedCastPreservingAnnotations(t, varDef.symbol.tpe) - orig match { - case Block(thenStats, thenExpr) => Block(thenStats, Assign(Ident(varDef.symbol), cast(thenExpr))) - case _ => Assign(Ident(varDef.symbol), cast(orig)) + + case Match(scrut, cases) => + // if type of match is Unit don't introduce assignment, + // but add Unit value to bring it into form expected by async transform + if (expr.tpe =:= definitions.UnitTpe) { + statsExprUnit + } + else { + val varDef = defineVar(name.matchRes, expr.tpe, tree.pos) + def typedAssign(lhs: Tree) = + api.typecheck(atPos(lhs.pos)(Assign(Ident(varDef.symbol), mkAttributedCastPreservingAnnotations(lhs, tpe(varDef.symbol))))) + val casesWithAssign = cases map { + case cd@CaseDef(pat, guard, body) => + val newBody = body match { + case b@Block(caseStats, caseExpr) => treeCopy.Block(b, caseStats, typedAssign(caseExpr)).setType(definitions.UnitTpe) + case _ => typedAssign(body) + } + treeCopy.CaseDef(cd, pat, guard, newBody).setType(definitions.UnitTpe) } + val matchWithAssign = treeCopy.Match(tree, scrut, casesWithAssign).setType(definitions.UnitTpe) + require(matchWithAssign.tpe != null, matchWithAssign) + stats :+ varDef :+ matchWithAssign :+ gen.mkAttributedStableRef(varDef.symbol).setPos(tree.pos).setType(tree.tpe) } - val ifWithAssign = treeCopy.If(tree, cond, branchWithAssign(thenp), branchWithAssign(elsep)).setType(definitions.UnitTpe) - stats :+ varDef :+ ifWithAssign :+ gen.mkAttributedStableRef(varDef.symbol).setType(tree.tpe).setPos(tree.pos) - } - case LabelDef(name, params, rhs) => - statsExprUnit + case _ => + stats :+ expr + } + } - case Match(scrut, cases) => - // if type of match is Unit don't introduce assignment, - // but add Unit value to bring it into form expected by async transform - if (expr.tpe =:= definitions.UnitTpe) { - statsExprUnit - } - else { - val varDef = defineVar(name.matchRes, expr.tpe, tree.pos) - def typedAssign(lhs: Tree) = - localTyper.typedPos(lhs.pos)(Assign(Ident(varDef.symbol), mkAttributedCastPreservingAnnotations(lhs, varDef.symbol.tpe))) - val casesWithAssign = cases map { - case cd@CaseDef(pat, guard, body) => - val newBody = body match { - case b@Block(caseStats, caseExpr) => treeCopy.Block(b, caseStats, typedAssign(caseExpr)).setType(definitions.UnitTpe) - case _ => typedAssign(body) - } - treeCopy.CaseDef(cd, pat, guard, newBody).setType(definitions.UnitTpe) - } - val matchWithAssign = treeCopy.Match(tree, scrut, casesWithAssign).setType(definitions.UnitTpe) - require(matchWithAssign.tpe != null, matchWithAssign) - stats :+ varDef :+ matchWithAssign :+ gen.mkAttributedStableRef(varDef.symbol).setPos(tree.pos).setType(tree.tpe) - } - case _ => - stats :+ expr + private def defineVar(prefix: String, tp: Type, pos: Position): ValDef = { + val sym = api.currentOwner.newTermSymbol(name.fresh(prefix), pos, MUTABLE | SYNTHETIC).setInfo(uncheckedBounds(tp)) + valDef(sym, gen.mkZero(uncheckedBounds(tp))).setType(NoType).setPos(pos) } } - private def defineVar(prefix: String, tp: Type, pos: Position): ValDef = { - val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, MUTABLE | SYNTHETIC).setInfo(uncheckedBounds(tp)) - ValDef(sym, gen.mkZero(uncheckedBounds(tp))).setType(NoType).setPos(pos) - } - } - - private object trace { - private var indent = -1 - - private def indentString = " " * indent - - def apply[T](args: Any)(t: => T): T = { - def prefix = mode.toString.toLowerCase - indent += 1 - def oneLine(s: Any) = s.toString.replaceAll("""\n""", "\\\\n").take(127) - try { - AsyncUtils.trace(s"${indentString}$prefix(${oneLine(args)})") - val result = t - AsyncUtils.trace(s"${indentString}= ${oneLine(result)}") - result - } finally { - indent -= 1 + object trace { + private var indent = -1 + + private def indentString = " " * indent + + def apply[T](args: Any)(t: => T): T = { + def prefix = mode.toString.toLowerCase + indent += 1 + def oneLine(s: Any) = s.toString.replaceAll("""\n""", "\\\\n").take(127) + try { + AsyncUtils.trace(s"${indentString}$prefix(${oneLine(args)})") + val result = t + AsyncUtils.trace(s"${indentString}= ${oneLine(result)}") + result + } finally { + indent -= 1 + } } } - } - private def defineVal(prefix: String, lhs: Tree, pos: Position): ValDef = { - val sym = currOwner.newTermSymbol(name.fresh(prefix), pos, SYNTHETIC).setInfo(uncheckedBounds(lhs.tpe)) - changeOwner(lhs, currentOwner, sym) - ValDef(sym, changeOwner(lhs, currentOwner, sym)).setType(NoType).setPos(pos) - } - - private object anf { - def transformToList(tree: Tree): List[Tree] = { - mode = Anf; blockToList(transform(tree)) + def defineVal(prefix: String, lhs: Tree, pos: Position): ValDef = { + val sym = api.currentOwner.newTermSymbol(name.fresh(prefix), pos, SYNTHETIC).setInfo(uncheckedBounds(lhs.tpe)) + lhs.changeOwner(api.currentOwner, sym) + valDef(sym, lhs.changeOwner(api.currentOwner, sym)).setType(NoType).setPos(pos) } - def _transformToList(tree: Tree): List[Tree] = trace(tree) { - val containsAwait = tree exists isAwait - if (!containsAwait) { - tree match { - case Block(stats, expr) => - // avoids nested block in `while(await(false)) ...`. - // TODO I think `containsAwait` really should return true if the code contains a label jump to an enclosing - // while/doWhile and there is an await *anywhere* inside that construct. - stats :+ expr - case _ => List(tree) - } - } else tree match { - case Select(qual, sel) => - val stats :+ expr = linearize.transformToList(qual) - stats :+ treeCopy.Select(tree, expr, sel) - - case Throw(expr) => - val stats :+ expr1 = linearize.transformToList(expr) - stats :+ treeCopy.Throw(tree, expr1) - - case Typed(expr, tpt) => - val stats :+ expr1 = linearize.transformToList(expr) - stats :+ treeCopy.Typed(tree, expr1, tpt) - - case treeInfo.Applied(fun, targs, argss) if argss.nonEmpty => - // we can assume that no await call appears in a by-name argument position, - // this has already been checked. - val funStats :+ simpleFun = linearize.transformToList(fun) - val (argStatss, argExprss): (List[List[List[Tree]]], List[List[Tree]]) = - mapArgumentss[List[Tree]](fun, argss) { - case Arg(expr, byName, _) if byName /*|| isPure(expr) TODO */ => (Nil, expr) - case Arg(expr, _, argName) => - linearize.transformToList(expr) match { - case stats :+ expr1 => - val valDef = defineVal(argName, expr1, expr1.pos) - require(valDef.tpe != null, valDef) - val stats1 = stats :+ valDef - (stats1, atPos(tree.pos.makeTransparent)(gen.stabilize(gen.mkAttributedIdent(valDef.symbol)))) - } - } + object anf { + def transformToList(tree: Tree): List[Tree] = { + mode = Anf; blockToList(api.recur(tree)) + } - def copyApplied(tree: Tree, depth: Int): Tree = { - tree match { - case TypeApply(_, targs) => treeCopy.TypeApply(tree, simpleFun, targs) - case _ if depth == 0 => simpleFun - case Apply(fun, args) => - val newTypedArgs = map2(args.map(_.pos), argExprss(depth - 1))((pos, arg) => localTyper.typedPos(pos)(arg)) - treeCopy.Apply(tree, copyApplied(fun, depth - 1), newTypedArgs) - } + def _transformToList(tree: Tree): List[Tree] = trace(tree) { + val containsAwait = tree exists isAwait + if (!containsAwait) { + tree match { + case Block(stats, expr) => + // avoids nested block in `while(await(false)) ...`. + // TODO I think `containsAwait` really should return true if the code contains a label jump to an enclosing + // while/doWhile and there is an await *anywhere* inside that construct. + stats :+ expr + case _ => List(tree) } + } else tree match { + case Select(qual, sel) => + val stats :+ expr = linearize.transformToList(qual) + stats :+ treeCopy.Select(tree, expr, sel) + + case Throw(expr) => + val stats :+ expr1 = linearize.transformToList(expr) + stats :+ treeCopy.Throw(tree, expr1) + + case Typed(expr, tpt) => + val stats :+ expr1 = linearize.transformToList(expr) + stats :+ treeCopy.Typed(tree, expr1, tpt) + + case Applied(fun, targs, argss) if argss.nonEmpty => + // we can assume that no await call appears in a by-name argument position, + // this has already been checked. + val funStats :+ simpleFun = linearize.transformToList(fun) + val (argStatss, argExprss): (List[List[List[Tree]]], List[List[Tree]]) = + mapArgumentss[List[Tree]](fun, argss) { + case Arg(expr, byName, _) if byName /*|| isPure(expr) TODO */ => (Nil, expr) + case Arg(expr, _, argName) => + linearize.transformToList(expr) match { + case stats :+ expr1 => + val valDef = defineVal(argName, expr1, expr1.pos) + require(valDef.tpe != null, valDef) + val stats1 = stats :+ valDef + (stats1, atPos(tree.pos.makeTransparent)(gen.stabilize(gen.mkAttributedIdent(valDef.symbol)))) + } + } + def copyApplied(tree: Tree, depth: Int): Tree = { + tree match { + case TypeApply(_, targs) => treeCopy.TypeApply(tree, simpleFun, targs) + case _ if depth == 0 => simpleFun + case Apply(fun, args) => + val newTypedArgs = map2(args.map(_.pos), argExprss(depth - 1))((pos, arg) => api.typecheck(atPos(pos)(arg))) + treeCopy.Apply(tree, copyApplied(fun, depth - 1), newTypedArgs) + } + } - /** The depth of the nested applies: e.g. Apply(Apply(Apply(_, _), _), _) - * has depth 3. Continues through type applications (without counting them.) - */ - def applyDepth: Int = { - def loop(tree: Tree): Int = tree match { - case Apply(fn, _) => 1 + loop(fn) - case TypeApply(fn, _) => loop(fn) - case AppliedTypeTree(fn, _) => loop(fn) - case _ => 0 + val typedNewApply = copyApplied(tree, argss.length) + + funStats ++ argStatss.flatten.flatten :+ typedNewApply + + case Block(stats, expr) => + (stats :+ expr).flatMap(linearize.transformToList) + + case ValDef(mods, name, tpt, rhs) => + if (rhs exists isAwait) { + val stats :+ expr = api.atOwner(api.currentOwner.owner)(linearize.transformToList(rhs)) + stats.foreach(_.changeOwner(api.currentOwner, api.currentOwner.owner)) + stats :+ treeCopy.ValDef(tree, mods, name, tpt, expr) + } else List(tree) + + case Assign(lhs, rhs) => + val stats :+ expr = linearize.transformToList(rhs) + stats :+ treeCopy.Assign(tree, lhs, expr) + + case If(cond, thenp, elsep) => + val condStats :+ condExpr = linearize.transformToList(cond) + val thenBlock = linearize.transformToBlock(thenp) + val elseBlock = linearize.transformToBlock(elsep) + condStats :+ treeCopy.If(tree, condExpr, thenBlock, elseBlock) + + case Match(scrut, cases) => + val scrutStats :+ scrutExpr = linearize.transformToList(scrut) + val caseDefs = cases map { + case CaseDef(pat, guard, body) => + // extract local variables for all names bound in `pat`, and rewrite `body` + // to refer to these. + // TODO we can move this into ExprBuilder once we get rid of `AsyncDefinitionUseAnalyzer`. + val block = linearize.transformToBlock(body) + val (valDefs, mappings) = (pat collect { + case b@Bind(name, _) => + val vd = defineVal(name.toTermName + AnfTransform.this.name.bindSuffix, gen.mkAttributedStableRef(b.symbol).setPos(b.pos), b.pos) + (vd, (b.symbol, vd.symbol)) + }).unzip + val (from, to) = mappings.unzip + val b@Block(stats1, expr1) = block.substituteSymbols(from, to).asInstanceOf[Block] + val newBlock = treeCopy.Block(b, valDefs ++ stats1, expr1) + treeCopy.CaseDef(tree, pat, guard, newBlock) } - loop(tree) - } + scrutStats :+ treeCopy.Match(tree, scrutExpr, caseDefs) - val typedNewApply = copyApplied(tree, applyDepth) - - funStats ++ argStatss.flatten.flatten :+ typedNewApply - - case Block(stats, expr) => - (stats :+ expr).flatMap(linearize.transformToList) - - case ValDef(mods, name, tpt, rhs) => - if (rhs exists isAwait) { - val stats :+ expr = atOwner(currOwner.owner)(linearize.transformToList(rhs)) - stats.foreach(changeOwner(_, currOwner, currOwner.owner)) - stats :+ treeCopy.ValDef(tree, mods, name, tpt, expr) - } else List(tree) - - case Assign(lhs, rhs) => - val stats :+ expr = linearize.transformToList(rhs) - stats :+ treeCopy.Assign(tree, lhs, expr) - - case If(cond, thenp, elsep) => - val condStats :+ condExpr = linearize.transformToList(cond) - val thenBlock = linearize.transformToBlock(thenp) - val elseBlock = linearize.transformToBlock(elsep) - condStats :+ treeCopy.If(tree, condExpr, thenBlock, elseBlock) - - case Match(scrut, cases) => - val scrutStats :+ scrutExpr = linearize.transformToList(scrut) - val caseDefs = cases map { - case CaseDef(pat, guard, body) => - // extract local variables for all names bound in `pat`, and rewrite `body` - // to refer to these. - // TODO we can move this into ExprBuilder once we get rid of `AsyncDefinitionUseAnalyzer`. - val block = linearize.transformToBlock(body) - val (valDefs, mappings) = (pat collect { - case b@Bind(name, _) => - val vd = defineVal(name.toTermName + AnfTransform.this.name.bindSuffix, gen.mkAttributedStableRef(b.symbol).setPos(b.pos), b.pos) - (vd, (b.symbol, vd.symbol)) - }).unzip - val (from, to) = mappings.unzip - val b@Block(stats1, expr1) = block.substituteSymbols(from, to).asInstanceOf[Block] - val newBlock = treeCopy.Block(b, valDefs ++ stats1, expr1) - treeCopy.CaseDef(tree, pat, guard, newBlock) - } - scrutStats :+ treeCopy.Match(tree, scrutExpr, caseDefs) + case LabelDef(name, params, rhs) => + List(LabelDef(name, params, Block(linearize.transformToList(rhs), Literal(Constant(())))).setSymbol(tree.symbol)) - case LabelDef(name, params, rhs) => - List(LabelDef(name, params, Block(linearize.transformToList(rhs), Literal(Constant(())))).setSymbol(tree.symbol)) + case TypeApply(fun, targs) => + val funStats :+ simpleFun = linearize.transformToList(fun) + funStats :+ treeCopy.TypeApply(tree, simpleFun, targs) - case TypeApply(fun, targs) => - val funStats :+ simpleFun = linearize.transformToList(fun) - funStats :+ treeCopy.TypeApply(tree, simpleFun, targs) + case _ => + List(tree) + } + } + } - case _ => - List(tree) + def anfLinearize(tree: Tree): Block = { + val trees: List[Tree] = mode match { + case Anf => anf._transformToList(tree) + case Linearizing => linearize._transformToList(tree) } + listToBlock(trees) + } + + tree match { + case _: ValDef | _: DefDef | _: Function | _: ClassDef | _: TypeDef => + api.atOwner(tree.symbol)(anfLinearize(tree)) + case _: ModuleDef => + api.atOwner(tree.symbol.asModule.moduleClass orElse tree.symbol)(anfLinearize(tree)) + case _ => + anfLinearize(tree) } - } + }).asInstanceOf[Block] } } diff --git a/src/main/scala/scala/async/internal/AsyncAnalysis.scala b/src/main/scala/scala/async/internal/AsyncAnalysis.scala index 15ddf48..6540bdb 100644 --- a/src/main/scala/scala/async/internal/AsyncAnalysis.scala +++ b/src/main/scala/scala/async/internal/AsyncAnalysis.scala @@ -10,7 +10,7 @@ import scala.collection.mutable trait AsyncAnalysis { self: AsyncMacro => - import global._ + import c.universe._ /** * Analyze the contents of an `async` block in order to: @@ -21,14 +21,14 @@ trait AsyncAnalysis { def reportUnsupportedAwaits(tree: Tree): Unit = { val analyzer = new UnsupportedAwaitAnalyzer analyzer.traverse(tree) - analyzer.hasUnsupportedAwaits + // analyzer.hasUnsupportedAwaits // XB: not used?! } private class UnsupportedAwaitAnalyzer extends AsyncTraverser { var hasUnsupportedAwaits = false override def nestedClass(classDef: ClassDef) { - val kind = if (classDef.symbol.isTrait) "trait" else "class" + val kind = if (classDef.symbol.asClass.isTrait) "trait" else "class" reportUnsupportedAwait(classDef, s"nested ${kind}") } @@ -59,7 +59,7 @@ trait AsyncAnalysis { reportUnsupportedAwait(tree, "try/catch") super.traverse(tree) case Return(_) => - abort(tree.pos, "return is illegal within a async block") + c.abort(tree.pos, "return is illegal within a async block") case DefDef(mods, _, _, _, _, _) if mods.hasFlag(Flag.LAZY) && containsAwait => reportUnsupportedAwait(tree, "lazy val initializer") case CaseDef(_, guard, _) if guard exists isAwait => @@ -86,7 +86,7 @@ trait AsyncAnalysis { private def reportError(pos: Position, msg: String) { hasUnsupportedAwaits = true - abort(pos, msg) + c.abort(pos, msg) } } } diff --git a/src/main/scala/scala/async/internal/AsyncBase.scala b/src/main/scala/scala/async/internal/AsyncBase.scala index ebedcbd..7464c42 100644 --- a/src/main/scala/scala/async/internal/AsyncBase.scala +++ b/src/main/scala/scala/async/internal/AsyncBase.scala @@ -42,34 +42,22 @@ abstract class AsyncBase { def asyncImpl[T: c.WeakTypeTag](c: Context) (body: c.Expr[T]) (execContext: c.Expr[futureSystem.ExecContext]): c.Expr[futureSystem.Fut[T]] = { - import c.universe._ + import c.universe._, c.internal._, decorators._ val asyncMacro = AsyncMacro(c, self) - val isPresentationCompiler = asyncMacro.global.forInteractive - - val code = asyncMacro.asyncTransform[T]( - body.tree.asInstanceOf[asyncMacro.global.Tree], - execContext.tree.asInstanceOf[asyncMacro.global.Tree] - )(implicitly[c.WeakTypeTag[T]].asInstanceOf[asyncMacro.global.WeakTypeTag[T]]).asInstanceOf[Tree] - + val code = asyncMacro.asyncTransform[T](body.tree, execContext.tree)(c.weakTypeTag[T]) AsyncUtils.vprintln(s"async state machine transform expands to:\n ${code}") - val result = if (isPresentationCompiler) { - asyncMacro.suppressExpansion() - c.macroApplication - } else { - // Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges - for (t <- code) - t.pos = t.pos.makeTransparent - code - } - c.Expr[futureSystem.Fut[T]](result) + + // Mark range positions for synthetic code as transparent to allow some wiggle room for overlapping ranges + for (t <- code) t.setPos(t.pos.makeTransparent) + c.Expr[futureSystem.Fut[T]](code) } protected[async] def awaitMethod(u: Universe)(asyncMacroSymbol: u.Symbol): u.Symbol = { import u._ asyncMacroSymbol.owner.typeSignature.member(newTermName("await")) - } - + } + protected[async] def nullOut(u: Universe)(name: u.Expr[String], v: u.Expr[Any]): u.Expr[Unit] = u.reify { () } } diff --git a/src/main/scala/scala/async/internal/AsyncId.scala b/src/main/scala/scala/async/internal/AsyncId.scala index f9d8c0b..6b4dbce 100644 --- a/src/main/scala/scala/async/internal/AsyncId.scala +++ b/src/main/scala/scala/async/internal/AsyncId.scala @@ -7,7 +7,6 @@ package scala.async.internal import language.experimental.macros import scala.reflect.macros.Context import scala.reflect.api.Universe -import scala.reflect.internal.SymbolTable object AsyncId extends AsyncBase { lazy val futureSystem = IdentityFutureSystem @@ -52,12 +51,11 @@ object IdentityFutureSystem extends FutureSystem { type ExecContext = Unit type Tryy[A] = scala.util.Try[A] - def mkOps(c: SymbolTable): Ops {val universe: c.type} = new Ops { - val universe: c.type = c + def mkOps(c0: Context): Ops {val c: c0.type} = new Ops { + val c: c0.type = c0 + import c.universe._ - import universe._ - - def execContext: Expr[ExecContext] = Expr[Unit](Literal(Constant(()))) + def execContext: Expr[ExecContext] = c.Expr[Unit](Literal(Constant(()))) def promType[A: WeakTypeTag]: Type = weakTypeOf[Prom[A]] def tryType[A: WeakTypeTag]: Type = weakTypeOf[scala.util.Try[A]] @@ -76,12 +74,12 @@ object IdentityFutureSystem extends FutureSystem { def onComplete[A, U](future: Expr[Fut[A]], fun: Expr[Tryy[A] => U], execContext: Expr[ExecContext]): Expr[Unit] = reify { fun.splice.apply(util.Success(future.splice)) - Expr[Unit](Literal(Constant(()))).splice + c.Expr[Unit](Literal(Constant(()))).splice } def completeProm[A](prom: Expr[Prom[A]], value: Expr[Tryy[A]]): Expr[Unit] = reify { prom.splice.a = value.splice.get - Expr[Unit](Literal(Constant(()))).splice + c.Expr[Unit](Literal(Constant(()))).splice } def tryyIsFailure[A](tryy: Expr[Tryy[A]]): Expr[Boolean] = reify { diff --git a/src/main/scala/scala/async/internal/AsyncMacro.scala b/src/main/scala/scala/async/internal/AsyncMacro.scala index e3400b4..e969f9b 100644 --- a/src/main/scala/scala/async/internal/AsyncMacro.scala +++ b/src/main/scala/scala/async/internal/AsyncMacro.scala @@ -1,51 +1,25 @@ package scala.async.internal -import scala.tools.nsc.Global -import scala.tools.nsc.transform.TypingTransformers - object AsyncMacro { - def apply(c: reflect.macros.Context, base: AsyncBase): AsyncMacro = { + def apply(c0: reflect.macros.Context, base: AsyncBase): AsyncMacro { val c: c0.type } = { import language.reflectiveCalls - val powerContext = c.asInstanceOf[c.type { val universe: Global; val callsiteTyper: universe.analyzer.Typer }] - new AsyncMacro { - val global: powerContext.universe.type = powerContext.universe - val callSiteTyper: global.analyzer.Typer = powerContext.callsiteTyper - val macroApplication: global.Tree = c.macroApplication.asInstanceOf[global.Tree] + new AsyncMacro { self => + val c: c0.type = c0 // This member is required by `AsyncTransform`: - val asyncBase: AsyncBase = base + val asyncBase: AsyncBase = base // These members are required by `ExprBuilder`: - val futureSystem: FutureSystem = base.futureSystem - val futureSystemOps: futureSystem.Ops {val universe: global.type} = futureSystem.mkOps(global) + val futureSystem: FutureSystem = base.futureSystem + val futureSystemOps: futureSystem.Ops {val c: self.c.type} = futureSystem.mkOps(c) } } } private[async] trait AsyncMacro - extends TypingTransformers - with AnfTransform with TransformUtils with Lifter + extends AnfTransform with TransformUtils with Lifter with ExprBuilder with AsyncTransform with AsyncAnalysis with LiveVariables { - val global: Global - val callSiteTyper: global.analyzer.Typer - val macroApplication: global.Tree - - lazy val macroPos = macroApplication.pos.makeTransparent - def atMacroPos(t: global.Tree) = global.atPos(macroPos)(t) - - def suppressExpansion() { - // Have your cake : Scala IDE sees original trees and hyperlinking, etc within async blocks "Just Works" - // Eat it too : (domain specific errors like "unsupported use of await" - // - // TODO remove this once we unsupport 2.10.x, scalac 2.11 does this automatically. - - import global.Tree - type Suppress = { def suppressMacroExpansion(a: Tree): Tree } - try { - global.asInstanceOf[Suppress].suppressMacroExpansion(macroApplication) - } catch { - case _: NoSuchMethodException => - global.analyzer.asInstanceOf[Suppress].suppressMacroExpansion(macroApplication) - } - } + val c: scala.reflect.macros.Context + lazy val macroPos = c.macroApplication.pos.makeTransparent + def atMacroPos(t: c.Tree) = c.universe.atPos(macroPos)(t) } diff --git a/src/main/scala/scala/async/internal/AsyncTransform.scala b/src/main/scala/scala/async/internal/AsyncTransform.scala index a24a823..66cfb87 100644 --- a/src/main/scala/scala/async/internal/AsyncTransform.scala +++ b/src/main/scala/scala/async/internal/AsyncTransform.scala @@ -3,7 +3,9 @@ package scala.async.internal trait AsyncTransform { self: AsyncMacro => - import global._ + import c.universe._ + import c.internal._ + import decorators._ val asyncBase: AsyncBase @@ -13,7 +15,7 @@ trait AsyncTransform { // We annotate the type of the whole expression as `T @uncheckedBounds` so as not to introduce // warnings about non-conformant LUBs. See SI-7694 // This implicit propagates the annotated type in the type tag. - implicit val uncheckedBoundsResultTag: WeakTypeTag[T] = WeakTypeTag[T](rootMirror, FixedMirrorTypeCreator(rootMirror, uncheckedBounds(resultType.tpe))) + implicit val uncheckedBoundsResultTag: WeakTypeTag[T] = c.WeakTypeTag[T](uncheckedBounds(resultType.tpe)) reportUnsupportedAwaits(body) @@ -77,9 +79,9 @@ trait AsyncTransform { val fieldSym = fld.symbol Block( List( - asyncBase.nullOut(global)(Expr[String](Literal(Constant(fieldSym.name.toString))), Expr[Any](Ident(fieldSym))).tree + asyncBase.nullOut(c.universe)(c.Expr[String](Literal(Constant(fieldSym.name.toString))), c.Expr[Any](Ident(fieldSym))).tree ), - Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), gen.mkZero(fieldSym.info)) + Assign(gen.mkAttributedStableRef(thisType(fieldSym.owner), fieldSym), gen.mkZero(fieldSym.info)) ) } val asyncState = asyncBlock.asyncStates.find(_.state == state).get @@ -101,7 +103,7 @@ trait AsyncTransform { ValDef(NoMods, name.stateMachine, TypeTree(), Apply(Select(New(Ident(stateMachine.symbol)), nme.CONSTRUCTOR), Nil)), futureSystemOps.spawn(Apply(selectStateMachine(name.apply), Nil), selectStateMachine(name.execContext)) ), - futureSystemOps.promiseToFuture(Expr[futureSystem.Prom[T]](selectStateMachine(name.result))).tree) + futureSystemOps.promiseToFuture(c.Expr[futureSystem.Prom[T]](selectStateMachine(name.result))).tree) } val isSimple = asyncBlock.asyncStates.size == 1 @@ -120,7 +122,7 @@ trait AsyncTransform { } AsyncUtils.vprintln(s"In file '$location':") - AsyncUtils.vprintln(s"${macroApplication}") + AsyncUtils.vprintln(s"${c.macroApplication}") AsyncUtils.vprintln(s"ANF transform expands to:\n $anfTree") states foreach (s => AsyncUtils.vprintln(s)) } @@ -140,89 +142,81 @@ trait AsyncTransform { liftedSyms.foreach { sym => if (sym != null) { - sym.owner = stateMachineClass + sym.setOwner(stateMachineClass) if (sym.isModule) - sym.moduleClass.owner = stateMachineClass + sym.asModule.moduleClass.setOwner(stateMachineClass) } } // Replace the ValDefs in the splicee with Assigns to the corresponding lifted // fields. Similarly, replace references to them with references to the field. // // This transform will only be run on the RHS of `def foo`. - class UseFields extends MacroTypingTransformer { - override def transform(tree: Tree): Tree = tree match { - case _ if currentOwner == stateMachineClass => - super.transform(tree) - case ValDef(_, _, _, rhs) if liftedSyms(tree.symbol) => - atOwner(currentOwner) { - val fieldSym = tree.symbol - val lhs = atPos(tree.pos) { - gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym) - } - val assign = treeCopy.Assign(tree, lhs, transform(rhs)).setType(definitions.UnitTpe) - changeOwner(assign, tree.symbol, currentOwner) - assign - } - case _: DefTree if liftedSyms(tree.symbol) => - EmptyTree - case Ident(name) if liftedSyms(tree.symbol) => + val useFields: (Tree, TypingTransformApi) => Tree = (tree, api) => tree match { + case _ if api.currentOwner == stateMachineClass => + api.default(tree) + case ValDef(_, _, _, rhs) if liftedSyms(tree.symbol) => + api.atOwner(api.currentOwner) { val fieldSym = tree.symbol - atPos(tree.pos) { - gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym).setType(tree.tpe) + val lhs = atPos(tree.pos) { + gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym) } - case _ => - super.transform(tree) - } + treeCopy.Assign(tree, lhs, api.recur(rhs)).setType(definitions.UnitTpe).changeOwner(fieldSym, api.currentOwner) + } + case _: DefTree if liftedSyms(tree.symbol) => + EmptyTree + case Ident(name) if liftedSyms(tree.symbol) => + val fieldSym = tree.symbol + atPos(tree.pos) { + gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym).setType(tree.tpe) + } + case _ => + api.default(tree) } val liftablesUseFields = liftables.map { case vd: ValDef => vd - case x => - val useField = new UseFields() - //.substituteSymbols(fromSyms, toSyms) - useField.atOwner(stateMachineClass)(useField.transform(x)) + case x => typingTransform(x, stateMachineClass)(useFields) } - tree.children.foreach { - t => - new ChangeOwnerAndModuleClassTraverser(callSiteTyper.context.owner, tree.symbol).traverse(t) - } + tree.children.foreach(_.changeOwner(enclosingOwner, tree.symbol)) val treeSubst = tree /* Fixes up DefDef: use lifted fields in `body` */ - def fixup(dd: DefDef, body: Tree, ctx: analyzer.Context): Tree = { + def fixup(dd: DefDef, body: Tree, api: TypingTransformApi): Tree = { val spliceeAnfFixedOwnerSyms = body - val useField = new UseFields() - val newRhs = useField.atOwner(dd.symbol)(useField.transform(spliceeAnfFixedOwnerSyms)) - val typer = global.analyzer.newTyper(ctx.make(dd, dd.symbol)) - treeCopy.DefDef(dd, dd.mods, dd.name, dd.tparams, dd.vparamss, dd.tpt, typer.typed(newRhs)) + val newRhs = typingTransform(spliceeAnfFixedOwnerSyms, dd.symbol)(useFields) + val newRhsTyped = api.atOwner(dd, dd.symbol)(api.typecheck(newRhs)) + treeCopy.DefDef(dd, dd.mods, dd.name, dd.tparams, dd.vparamss, dd.tpt, newRhsTyped) } liftablesUseFields.foreach(t => if (t.symbol != null) stateMachineClass.info.decls.enter(t.symbol)) val result0 = transformAt(treeSubst) { case t@Template(parents, self, stats) => - (ctx: analyzer.Context) => { + (api: TypingTransformApi) => { treeCopy.Template(t, parents, self, liftablesUseFields ++ stats) } } val result = transformAt(result0) { case dd@DefDef(_, name.apply, _, List(List(_)), _, _) if dd.symbol.owner == stateMachineClass => - (ctx: analyzer.Context) => - val typedTree = fixup(dd, changeOwner(applyBody, callSiteTyper.context.owner, dd.symbol), ctx) + (api: TypingTransformApi) => + val typedTree = fixup(dd, applyBody.changeOwner(enclosingOwner, dd.symbol), api) typedTree case dd@DefDef(_, name.resume, _, _, _, _) if dd.symbol.owner == stateMachineClass => - (ctx: analyzer.Context) => - val changed = changeOwner(resumeBody, callSiteTyper.context.owner, dd.symbol) - val res = fixup(dd, changed, ctx) + (api: TypingTransformApi) => + val changed = resumeBody.changeOwner(enclosingOwner, dd.symbol) + val res = fixup(dd, changed, api) res } result } def typecheckClassDef(cd: ClassDef): ClassDef = { - val Block(cd1 :: Nil, _) = callSiteTyper.typedPos(macroPos)(Block(cd :: Nil, Literal(Constant(())))) + val Block(cd1 :: Nil, _) = typingTransform(atPos(macroPos)(Block(cd :: Nil, Literal(Constant(())))))( + (tree, api) => + api.typecheck(tree) + ) cd1.asInstanceOf[ClassDef] } } diff --git a/src/main/scala/scala/async/internal/ExprBuilder.scala b/src/main/scala/scala/async/internal/ExprBuilder.scala index 5314ae0..2e31347 100644 --- a/src/main/scala/scala/async/internal/ExprBuilder.scala +++ b/src/main/scala/scala/async/internal/ExprBuilder.scala @@ -13,11 +13,12 @@ import scala.reflect.api trait ExprBuilder { builder: AsyncMacro => - import global._ + import c.universe._ import defn._ + import c.internal._ val futureSystem: FutureSystem - val futureSystemOps: futureSystem.Ops { val universe: global.type } + val futureSystemOps: futureSystem.Ops { val c: builder.c.type } val stateAssigner = new StateAssigner val labelDefStates = collection.mutable.Map[Symbol, Int]() @@ -80,8 +81,8 @@ trait ExprBuilder { List(nextState) override def mkHandlerCaseForState: CaseDef = { - val callOnComplete = futureSystemOps.onComplete(Expr(awaitable.expr), - Expr(This(tpnme.EMPTY)), Expr(Ident(name.execContext))).tree + val callOnComplete = futureSystemOps.onComplete(c.Expr(awaitable.expr), + c.Expr(This(tpnme.EMPTY)), c.Expr(Ident(name.execContext))).tree mkHandlerCase(state, stats :+ callOnComplete) } @@ -89,7 +90,7 @@ trait ExprBuilder { val tryGetTree = Assign( Ident(awaitable.resultName), - TypeApply(Select(futureSystemOps.tryyGet[T](Expr[futureSystem.Tryy[T]](Ident(symLookup.applyTrParam))).tree, newTermName("asInstanceOf")), List(TypeTree(awaitable.resultType))) + TypeApply(Select(futureSystemOps.tryyGet[T](c.Expr[futureSystem.Tryy[T]](Ident(symLookup.applyTrParam))).tree, newTermName("asInstanceOf")), List(TypeTree(awaitable.resultType))) ) /* if (tr.isFailure) @@ -101,10 +102,10 @@ trait ExprBuilder { * } */ val ifIsFailureTree = - If(futureSystemOps.tryyIsFailure(Expr[futureSystem.Tryy[T]](Ident(symLookup.applyTrParam))).tree, + If(futureSystemOps.tryyIsFailure(c.Expr[futureSystem.Tryy[T]](Ident(symLookup.applyTrParam))).tree, futureSystemOps.completeProm[T]( - Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), - Expr[futureSystem.Tryy[T]]( + c.Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), + c.Expr[futureSystem.Tryy[T]]( TypeApply(Select(Ident(symLookup.applyTrParam), newTermName("asInstanceOf")), List(TypeTree(futureSystemOps.tryType[T]))))).tree, Block(List(tryGetTree, mkStateTree(nextState, symLookup)), mkResumeApply(symLookup)) @@ -213,7 +214,7 @@ trait ExprBuilder { def checkForUnsupportedAwait(tree: Tree) = if (tree exists { case Apply(fun, _) if isAwait(fun) => true case _ => false - }) abort(tree.pos, "await must not be used in this position") + }) c.abort(tree.pos, "await must not be used in this position") def nestedBlockBuilder(nestedTree: Tree, startState: Int, endState: Int) = { val (nestedStats, nestedExpr) = statsAndExpr(nestedTree) @@ -327,9 +328,9 @@ trait ExprBuilder { def mkCombinedHandlerCases[T: WeakTypeTag]: List[CaseDef] = { val caseForLastState: CaseDef = { val lastState = asyncStates.last - val lastStateBody = Expr[T](lastState.body) + val lastStateBody = c.Expr[T](lastState.body) val rhs = futureSystemOps.completeProm( - Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryySuccess[T](lastStateBody)) + c.Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryySuccess[T](lastStateBody)) mkHandlerCase(lastState.state, rhs.tree) } asyncStates.toList match { @@ -370,9 +371,9 @@ trait ExprBuilder { CaseDef( Bind(name.t, Ident(nme.WILDCARD)), Apply(Ident(defn.NonFatalClass), List(Ident(name.t))), { - val t = Expr[Throwable](Ident(name.t)) + val t = c.Expr[Throwable](Ident(name.t)) futureSystemOps.completeProm[T]( - Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryyFailure[T](t)).tree + c.Expr[futureSystem.Prom[T]](symLookup.memberRef(name.result)), futureSystemOps.tryyFailure[T](t)).tree })), EmptyTree)) /** diff --git a/src/main/scala/scala/async/internal/FutureSystem.scala b/src/main/scala/scala/async/internal/FutureSystem.scala index 46c0bcf..1b1ffc3 100644 --- a/src/main/scala/scala/async/internal/FutureSystem.scala +++ b/src/main/scala/scala/async/internal/FutureSystem.scala @@ -4,7 +4,7 @@ package scala.async.internal import scala.language.higherKinds -import scala.reflect.internal.SymbolTable +import scala.reflect.macros.Context /** * An abstraction over a future system. @@ -27,10 +27,8 @@ trait FutureSystem { type Tryy[T] trait Ops { - val universe: reflect.internal.SymbolTable - - import universe._ - def Expr[T: WeakTypeTag](tree: Tree): Expr[T] = universe.Expr[T](rootMirror, universe.FixedMirrorTreeCreator(rootMirror, tree)) + val c: Context + import c.universe._ def promType[A: WeakTypeTag]: Type def tryType[A: WeakTypeTag]: Type @@ -53,7 +51,7 @@ trait FutureSystem { def completeProm[A](prom: Expr[Prom[A]], value: Expr[Tryy[A]]): Expr[Unit] def spawn(tree: Tree, execContext: Tree): Tree = - future(Expr[Unit](tree))(Expr[ExecContext](execContext)).tree + future(c.Expr[Unit](tree))(c.Expr[ExecContext](execContext)).tree def tryyIsFailure[A](tryy: Expr[Tryy[A]]): Expr[Boolean] @@ -65,7 +63,7 @@ trait FutureSystem { def postAnfTransform(tree: Block): Block = tree } - def mkOps(c: SymbolTable): Ops { val universe: c.type } + def mkOps(c0: Context): Ops { val c: c0.type } } object ScalaConcurrentFutureSystem extends FutureSystem { @@ -77,10 +75,9 @@ object ScalaConcurrentFutureSystem extends FutureSystem { type ExecContext = ExecutionContext type Tryy[A] = scala.util.Try[A] - def mkOps(c: SymbolTable): Ops {val universe: c.type} = new Ops { - val universe: c.type = c - - import universe._ + def mkOps(c0: Context): Ops {val c: c0.type} = new Ops { + val c: c0.type = c0 + import c.universe._ def promType[A: WeakTypeTag]: Type = weakTypeOf[Promise[A]] def tryType[A: WeakTypeTag]: Type = weakTypeOf[scala.util.Try[A]] @@ -105,7 +102,7 @@ object ScalaConcurrentFutureSystem extends FutureSystem { def completeProm[A](prom: Expr[Prom[A]], value: Expr[scala.util.Try[A]]): Expr[Unit] = reify { prom.splice.complete(value.splice) - Expr[Unit](Literal(Constant(()))).splice + c.Expr[Unit](Literal(Constant(()))).splice } def tryyIsFailure[A](tryy: Expr[scala.util.Try[A]]): Expr[Boolean] = reify { diff --git a/src/main/scala/scala/async/internal/Lifter.scala b/src/main/scala/scala/async/internal/Lifter.scala index 403eae9..4242a8e 100644 --- a/src/main/scala/scala/async/internal/Lifter.scala +++ b/src/main/scala/scala/async/internal/Lifter.scala @@ -2,8 +2,10 @@ package scala.async.internal trait Lifter { self: AsyncMacro => - import scala.reflect.internal.Flags._ - import global._ + import c.universe._ + import Flag._ + import c.internal._ + import decorators._ /** * Identify which DefTrees are used (including transitively) which are declared @@ -88,7 +90,7 @@ trait Lifter { // Only mark transitive references of defs, modules and classes. The RHS of lifted vals/vars // stays in its original location, so things that it refers to need not be lifted. - if (!(sym.isVal || sym.isVar)) + if (!(sym.isTerm && (sym.asTerm.isVal || sym.asTerm.isVar))) defSymToReferenced(sym).foreach(sym2 => markForLift(sym2)) } } @@ -111,34 +113,34 @@ trait Lifter { val treeLifted = t match { case vd@ValDef(_, _, tpt, rhs) => sym.setFlag(MUTABLE | STABLE | PRIVATE | LOCAL) - sym.name = name.fresh(sym.name.toTermName) - sym.modifyInfo(_.deconst) - treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(sym.tpe).setPos(t.pos), EmptyTree) + sym.setName(name.fresh(sym.name.toTermName)) + sym.setInfo(deconst(sym.info)) + treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), EmptyTree) case dd@DefDef(_, _, tparams, vparamss, tpt, rhs) => - sym.name = this.name.fresh(sym.name.toTermName) + sym.setName(this.name.fresh(sym.name.toTermName)) sym.setFlag(PRIVATE | LOCAL) // Was `DefDef(sym, rhs)`, but this ran afoul of `ToughTypeSpec.nestedMethodWithInconsistencyTreeAndInfoParamSymbols` // due to the handling of type parameter skolems in `thisMethodType` in `Namers` treeCopy.DefDef(dd, Modifiers(sym.flags), sym.name, tparams, vparamss, tpt, rhs) case cd@ClassDef(_, _, tparams, impl) => - sym.name = newTypeName(name.fresh(sym.name.toString).toString) + sym.setName(newTypeName(name.fresh(sym.name.toString).toString)) companionship.companionOf(cd.symbol) match { case NoSymbol => case moduleSymbol => - moduleSymbol.name = sym.name.toTermName - moduleSymbol.moduleClass.name = moduleSymbol.name.toTypeName + moduleSymbol.setName(sym.name.toTermName) + moduleSymbol.asModule.moduleClass.setName(moduleSymbol.name.toTypeName) } treeCopy.ClassDef(cd, Modifiers(sym.flags), sym.name, tparams, impl) case md@ModuleDef(_, _, impl) => companionship.companionOf(md.symbol) match { case NoSymbol => - sym.name = name.fresh(sym.name.toTermName) - sym.moduleClass.name = sym.name.toTypeName + sym.setName(name.fresh(sym.name.toTermName)) + sym.asModule.moduleClass.setName(sym.name.toTypeName) case classSymbol => // will be renamed by `case ClassDef` above. } treeCopy.ModuleDef(md, Modifiers(sym.flags), sym.name, impl) case td@TypeDef(_, _, tparams, rhs) => - sym.name = newTypeName(name.fresh(sym.name.toString).toString) + sym.setName(newTypeName(name.fresh(sym.name.toString).toString)) treeCopy.TypeDef(td, Modifiers(sym.flags), sym.name, tparams, rhs) } atPos(t.pos)(treeLifted) diff --git a/src/main/scala/scala/async/internal/LiveVariables.scala b/src/main/scala/scala/async/internal/LiveVariables.scala index 23063ba..5b49398 100644 --- a/src/main/scala/scala/async/internal/LiveVariables.scala +++ b/src/main/scala/scala/async/internal/LiveVariables.scala @@ -1,10 +1,9 @@ package scala.async.internal -import reflect.internal.Flags._ - trait LiveVariables { self: AsyncMacro => - import global._ + import c.universe._ + import Flag._ /** * Returns for a given state a list of fields (as trees) that should be nulled out @@ -56,7 +55,8 @@ trait LiveVariables { // determine which fields should be live also at the end (will not be nulled out) val noNull: Set[Symbol] = liftedSyms.filter { sym => - sym.tpe.typeSymbol.isPrimitiveValueClass || liftables.exists { tree => + val typeSym = tpe(sym).typeSymbol + (typeSym.isClass && typeSym.asClass.isPrimitive) || liftables.exists { tree => !liftedSyms.contains(tree.symbol) && tree.exists(_.symbol == sym) } } diff --git a/src/main/scala/scala/async/internal/TransformUtils.scala b/src/main/scala/scala/async/internal/TransformUtils.scala index bef52f1..665dead 100644 --- a/src/main/scala/scala/async/internal/TransformUtils.scala +++ b/src/main/scala/scala/async/internal/TransformUtils.scala @@ -5,7 +5,7 @@ package scala.async.internal import scala.reflect.macros.Context import reflect.ClassTag -import scala.reflect.macros.runtime.AbortMacroException +import scala.collection.immutable.ListMap /** * Utilities used in both `ExprBuilder` and `AnfTransform`. @@ -13,7 +13,9 @@ import scala.reflect.macros.runtime.AbortMacroException private[async] trait TransformUtils { self: AsyncMacro => - import global._ + import c.universe._ + import c.internal._ + import decorators._ object name { val resume = newTermName("resume") @@ -31,14 +33,82 @@ private[async] trait TransformUtils { val tr = newTermName("tr") val t = newTermName("throwable") - def fresh(name: TermName): TermName = newTermName(fresh(name.toString)) + def fresh(name: TermName): TermName = c.freshName(name) - def fresh(name: String): String = currentUnit.freshTermName("" + name + "$").toString + def fresh(name: String): String = c.freshName(name) } def isAwait(fun: Tree) = fun.symbol == defn.Async_await + // Copy pasted from TreeInfo in the compiler. + // Using a quasiquote pattern like `case q"$fun[..$targs](...$args)" => is not + // sufficient since https://github.com/scala/scala/pull/3656 as it doesn't match + // constructor invocations. + class Applied(val tree: Tree) { + /** The tree stripped of the possibly nested applications. + * The original tree if it's not an application. + */ + def callee: Tree = { + def loop(tree: Tree): Tree = tree match { + case Apply(fn, _) => loop(fn) + case tree => tree + } + loop(tree) + } + + /** The `callee` unwrapped from type applications. + * The original `callee` if it's not a type application. + */ + def core: Tree = callee match { + case TypeApply(fn, _) => fn + case AppliedTypeTree(fn, _) => fn + case tree => tree + } + + /** The type arguments of the `callee`. + * `Nil` if the `callee` is not a type application. + */ + def targs: List[Tree] = callee match { + case TypeApply(_, args) => args + case AppliedTypeTree(_, args) => args + case _ => Nil + } + + /** (Possibly multiple lists of) value arguments of an application. + * `Nil` if the `callee` is not an application. + */ + def argss: List[List[Tree]] = { + def loop(tree: Tree): List[List[Tree]] = tree match { + case Apply(fn, args) => loop(fn) :+ args + case _ => Nil + } + loop(tree) + } + } + + /** Returns a wrapper that knows how to destructure and analyze applications. + */ + def dissectApplied(tree: Tree) = new Applied(tree) + + /** Destructures applications into important subparts described in `Applied` class, + * namely into: core, targs and argss (in the specified order). + * + * Trees which are not applications are also accepted. Their callee and core will + * be equal to the input, while targs and argss will be Nil. + * + * The provided extractors don't expose all the API of the `Applied` class. + * For advanced use, call `dissectApplied` explicitly and use its methods instead of pattern matching. + */ + object Applied { + def apply(tree: Tree): Applied = new Applied(tree) + + def unapply(applied: Applied): Option[(Tree, List[Tree], List[List[Tree]])] = + Some((applied.core, applied.targs, applied.argss)) + + def unapply(tree: Tree): Option[(Tree, List[Tree], List[List[Tree]])] = + unapply(dissectApplied(tree)) + } private lazy val Boolean_ShortCircuits: Set[Symbol] = { import definitions.BooleanClass def BooleanTermMember(name: String) = BooleanClass.typeSignature.member(newTermName(name).encodedName) @@ -51,7 +121,7 @@ private[async] trait TransformUtils { if (Boolean_ShortCircuits contains fun.symbol) (i, j) => true else { val paramss = fun.tpe.paramss - val byNamess = paramss.map(_.map(_.isByNameParam)) + val byNamess = paramss.map(_.map(_.asTerm.isByNameParam)) (i, j) => util.Try(byNamess(i)(j)).getOrElse(false) } } @@ -61,11 +131,9 @@ private[async] trait TransformUtils { (i, j) => util.Try(namess(i)(j)).getOrElse(s"arg_${i}_${j}") } - def Expr[A: WeakTypeTag](t: Tree) = global.Expr[A](rootMirror, new FixedMirrorTreeCreator(rootMirror, t)) - object defn { def mkList_apply[A](args: List[Expr[A]]): Expr[List[A]] = { - Expr(Apply(Ident(definitions.List_apply), args.map(_.tree))) + c.Expr(Apply(Ident(definitions.List_apply), args.map(_.tree))) } def mkList_contains[A](self: Expr[List[A]])(elem: Expr[Any]) = reify { @@ -85,11 +153,7 @@ private[async] trait TransformUtils { } val NonFatalClass = rootMirror.staticModule("scala.util.control.NonFatal") - val Async_await = asyncBase.awaitMethod(global)(macroApplication.symbol).ensuring(_ != NoSymbol) - } - - def isSafeToInline(tree: Tree) = { - treeInfo.isExprSafeToInline(tree) + val Async_await = asyncBase.awaitMethod(c.universe)(c.macroApplication.symbol).ensuring(_ != NoSymbol) } // `while(await(x))` ... or `do { await(x); ... } while(...)` contain an `If` that loops; @@ -100,11 +164,17 @@ private[async] trait TransformUtils { case ld: LabelDef => ld.symbol }.toSet t.exists { - case rt: RefTree => rt.symbol != null && rt.symbol.isLabel && !(labelDefs contains rt.symbol) + case rt: RefTree => rt.symbol != null && isLabel(rt.symbol) && !(labelDefs contains rt.symbol) case _ => false } } + private def isLabel(sym: Symbol): Boolean = { + val LABEL = 1L << 17 // not in the public reflection API. + (internal.flags(sym).asInstanceOf[Long] & LABEL) != 0L + } + + /** Map a list of arguments to: * - A list of argument Trees * - A list of auxillary results. @@ -194,7 +264,7 @@ private[async] trait TransformUtils { case dd: DefDef => nestedMethod(dd) case fun: Function => function(fun) case m@Match(EmptyTree, _) => patMatFunction(m) // Pattern matching anonymous function under -Xoldpatmat of after `restorePatternMatchingFunctions` - case treeInfo.Applied(fun, targs, argss) if argss.nonEmpty => + case q"$fun[..$targs](...$argss)" if argss.nonEmpty => val isInByName = isByName(fun) for ((args, i) <- argss.zipWithIndex) { for ((arg, j) <- args.zipWithIndex) { @@ -208,64 +278,64 @@ private[async] trait TransformUtils { } } - def abort(pos: Position, msg: String) = throw new AbortMacroException(pos, msg) - - abstract class MacroTypingTransformer extends TypingTransformer(callSiteTyper.context.unit) { - currentOwner = callSiteTyper.context.owner - curTree = EmptyTree - - def currOwner: Symbol = currentOwner - - localTyper = global.analyzer.newTyper(callSiteTyper.context.make(unit = callSiteTyper.context.unit)) + def transformAt(tree: Tree)(f: PartialFunction[Tree, (TypingTransformApi => Tree)]) = { + typingTransform(tree)((tree, api) => { + if (f.isDefinedAt(tree)) f(tree)(api) + else api.default(tree) + }) } - def transformAt(tree: Tree)(f: PartialFunction[Tree, (analyzer.Context => Tree)]) = { - object trans extends MacroTypingTransformer { - override def transform(tree: Tree): Tree = { - if (f.isDefinedAt(tree)) { - f(tree)(localTyper.context) - } else super.transform(tree) - } + def toMultiMap[A, B](as: Iterable[(A, B)]): Map[A, List[B]] = + as.toList.groupBy(_._1).mapValues(_.map(_._2).toList).toMap + + // Attributed version of `TreeGen#mkCastPreservingAnnotations` + def mkAttributedCastPreservingAnnotations(tree: Tree, tp: Type): Tree = { + atPos(tree.pos) { + val casted = c.typecheck(gen.mkCast(tree, uncheckedBounds(withoutAnnotations(tp)).dealias)) + Typed(casted, TypeTree(tp)).setType(tp) } - trans.transform(tree) } - def changeOwner(tree: Tree, oldOwner: Symbol, newOwner: Symbol): tree.type = { - new ChangeOwnerAndModuleClassTraverser(oldOwner, newOwner).traverse(tree) - tree + def deconst(tp: Type): Type = tp match { + case AnnotatedType(anns, underlying) => annotatedType(anns, deconst(underlying)) + case ExistentialType(quants, underlying) => existentialType(quants, deconst(underlying)) + case ConstantType(value) => deconst(value.tpe) + case _ => tp } - class ChangeOwnerAndModuleClassTraverser(oldowner: Symbol, newowner: Symbol) - extends ChangeOwnerTraverser(oldowner, newowner) { + def withAnnotation(tp: Type, ann: Annotation): Type = withAnnotations(tp, List(ann)) - override def traverse(tree: Tree) { - tree match { - case _: DefTree => change(tree.symbol.moduleClass) - case _ => - } - super.traverse(tree) - } + def withAnnotations(tp: Type, anns: List[Annotation]): Type = tp match { + case AnnotatedType(existingAnns, underlying) => annotatedType(anns ::: existingAnns, underlying) + case ExistentialType(quants, underlying) => existentialType(quants, withAnnotations(underlying, anns)) + case _ => annotatedType(anns, tp) } - def toMultiMap[A, B](as: Iterable[(A, B)]): Map[A, List[B]] = - as.toList.groupBy(_._1).mapValues(_.map(_._2).toList).toMap + def withoutAnnotations(tp: Type): Type = tp match { + case AnnotatedType(anns, underlying) => withoutAnnotations(underlying) + case ExistentialType(quants, underlying) => existentialType(quants, withoutAnnotations(underlying)) + case _ => tp + } - // Attributed version of `TreeGen#mkCastPreservingAnnotations` - def mkAttributedCastPreservingAnnotations(tree: Tree, tp: Type): Tree = { - atPos(tree.pos) { - val casted = gen.mkAttributedCast(tree, uncheckedBounds(tp.withoutAnnotations).dealias) - Typed(casted, TypeTree(tp)).setType(tp) - } + def tpe(sym: Symbol): Type = { + if (sym.isType) sym.asType.toType + else sym.info + } + + def thisType(sym: Symbol): Type = { + if (sym.isClass) sym.asClass.thisPrefix + else NoPrefix } // ===================================== // Copy/Pasted from Scala 2.10.3. See SI-7694. private lazy val UncheckedBoundsClass = { - global.rootMirror.getClassIfDefined("scala.reflect.internal.annotations.uncheckedBounds") + try c.mirror.staticClass("scala.reflect.internal.annotations.uncheckedBounds") + catch { case _: ScalaReflectionException => NoSymbol } } final def uncheckedBounds(tp: Type): Type = { if (tp.typeArgs.isEmpty || UncheckedBoundsClass == NoSymbol) tp - else tp.withAnnotation(AnnotationInfo marker UncheckedBoundsClass.tpe) + else withAnnotation(tp, Annotation(UncheckedBoundsClass.asType.toType, Nil, ListMap())) } // ===================================== } |