diff options
Diffstat (limited to 'src')
16 files changed, 420 insertions, 465 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..0e58fa7 100644 --- a/src/main/scala/scala/async/internal/AnfTransform.scala +++ b/src/main/scala/scala/async/internal/AnfTransform.scala @@ -5,280 +5,276 @@ 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 q"$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 + /** 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 + } + loop(tree) } - loop(tree) - } - 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) + 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 = 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) + } + 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 76c2dba..ffbc04d 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,10 +59,10 @@ 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 ValDef(mods, _, _, _) if mods.hasFlag(Flag.LAZY) => // TODO lift this restriction - abort(tree.pos, "lazy vals are illegal within an async block") + c.abort(tree.pos, "lazy vals are illegal within an async block") case CaseDef(_, guard, _) if guard exists isAwait => // TODO lift this restriction reportUnsupportedAwait(tree, "pattern guard") @@ -87,7 +87,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 ca53ff1..cf9dd1c 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) @@ -55,7 +57,7 @@ trait AsyncTransform { val template = Template(List(tryToUnit, typeOf[() => Unit]).map(TypeTree(_)), emptyValDef, body) val t = ClassDef(NoMods, name.stateMachineT, Nil, template) - callSiteTyper.typedPos(macroPos)(Block(t :: Nil, Literal(Constant(())))) + typingTransform(atPos(macroPos)(Block(t :: Nil, Literal(Constant(())))))((tree, api) => api.typecheck(tree)) t } @@ -78,9 +80,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 @@ -102,7 +104,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 @@ -121,7 +123,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)) } @@ -141,79 +143,70 @@ 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 set = Assign(gen.mkAttributedStableRef(fieldSym.owner.thisType, fieldSym), transform(rhs)) - changeOwner(set, tree.symbol, currentOwner) - localTyper.typedPos(tree.pos)(set) - } - 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) - } - case _ => - super.transform(tree) - } + val set = Assign(gen.mkAttributedStableRef(thisType(fieldSym.owner.asClass), fieldSym), api.recur(rhs)) + set.changeOwner(tree.symbol, api.currentOwner) + api.typecheck(atPos(tree.pos)(set)) + } + 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 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 7b102d1..bf7bdbd 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,35 +113,35 @@ 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) + sym.setName(name.fresh(sym.name.toTermName)) + sym.setInfo(deconst(sym.info)) val zeroRhs = atPos(t.pos)(gen.mkZero(vd.symbol.info)) - treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(sym.tpe).setPos(t.pos), zeroRhs) + treeCopy.ValDef(vd, Modifiers(sym.flags), sym.name, TypeTree(tpe(sym)).setPos(t.pos), zeroRhs) 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 8753b3d..6c67e99 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,7 @@ 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 => + tpe(sym).typeSymbol.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 f228e1d..5d0a96f 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,9 +33,9 @@ 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) = @@ -51,7 +53,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 +63,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 +85,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; @@ -194,7 +190,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 +204,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())) } // ===================================== } diff --git a/src/test/scala/scala/async/TreeInterrogation.scala b/src/test/scala/scala/async/TreeInterrogation.scala index 8261898..6e15513 100644 --- a/src/test/scala/scala/async/TreeInterrogation.scala +++ b/src/test/scala/scala/async/TreeInterrogation.scala @@ -38,7 +38,7 @@ class TreeInterrogation { val varDefs = tree1.collect { case ValDef(mods, name, _, _) if mods.hasFlag(Flag.MUTABLE) => name } - varDefs.map(_.decoded.trim).toSet mustBe (Set("state", "await$1$1", "await$2$1")) + varDefs.map(_.decoded.trim).toSet.toList.sorted mustStartWith (List("await$macro$", "await$macro$", "state")) val defDefs = tree1.collect { case t: Template => @@ -49,7 +49,7 @@ class TreeInterrogation { && !dd.symbol.asTerm.isAccessor && !dd.symbol.asTerm.isSetter => dd.name } }.flatten - defDefs.map(_.decoded.trim).toSet mustBe (Set("foo$1", "apply", "resume", "<init>")) + defDefs.map(_.decoded.trim).toSet.toList.sorted mustStartWith (List("<init>", "apply", "foo$macro$", "resume")) } } diff --git a/src/test/scala/scala/async/package.scala b/src/test/scala/scala/async/package.scala index 974a989..166edaa 100644 --- a/src/test/scala/scala/async/package.scala +++ b/src/test/scala/scala/async/package.scala @@ -18,6 +18,15 @@ package object async { implicit class stringops(text: String) { def mustContain(substring: String) = assert(text contains substring, text) + + def mustStartWith(prefix: String) = assert(text startsWith prefix, text) + } + + implicit class listops(list: List[String]) { + def mustStartWith(prefixes: List[String]) = { + assert(list.length == prefixes.size, ("expected = " + prefixes.length + ", actual = " + list.length, list)) + list.zip(prefixes).foreach{ case (el, prefix) => el mustStartWith prefix } + } } def intercept[T <: Throwable : ClassTag](body: => Any): T = { @@ -45,8 +54,10 @@ package object async { def scalaBinaryVersion: String = { val PreReleasePattern = """.*-(M|RC).*""".r val Pattern = """(\d+\.\d+)\..*""".r + val SnapshotPattern = """(\d+\.\d+\.\d+)-\d+-\d+-.*""".r scala.util.Properties.versionNumberString match { case s @ PreReleasePattern(_) => s + case SnapshotPattern(v) => v + "-SNAPSHOT" case Pattern(v) => v case _ => "" } diff --git a/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala b/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala index 757ae0b..2cce7e8 100644 --- a/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala +++ b/src/test/scala/scala/async/run/anf/AnfTransformSpec.scala @@ -403,6 +403,6 @@ class AnfTransformSpec { """.stripMargin }) val applyImplicitView = tree.collect { case x if x.getClass.getName.endsWith("ApplyImplicitView") => x } - applyImplicitView.map(_.toString) mustBe List("view(a$1)") + applyImplicitView.map(_.toString) mustStartWith List("view(a$macro$") } } diff --git a/src/test/scala/scala/async/run/live/LiveVariablesSpec.scala b/src/test/scala/scala/async/run/live/LiveVariablesSpec.scala index 17d33af..30646a6 100644 --- a/src/test/scala/scala/async/run/live/LiveVariablesSpec.scala +++ b/src/test/scala/scala/async/run/live/LiveVariablesSpec.scala @@ -36,9 +36,9 @@ class LiveVariablesSpec { // a == Cell(1) val b: Cell[Int] = await(m1(a)) // await$2$1 // b == Cell(2) - assert(AsyncTestLV.log.exists(_ == ("await$1$1" -> Cell(1))), AsyncTestLV.log) + assert(AsyncTestLV.log.exists(_._2 == Cell(1)), AsyncTestLV.log) val res = await(m2(b)) // await$3$1 - assert(AsyncTestLV.log.exists(_ == ("await$2$1" -> Cell(2)))) + assert(AsyncTestLV.log.exists(_._2 == Cell(2))) res } @@ -60,9 +60,9 @@ class LiveVariablesSpec { // a == Cell(1) val b: Any = await(m1(a)) // await$5$1 // b == Cell(2) - assert(AsyncTestLV.log.exists(_ == ("await$4$1" -> Cell(1)))) + assert(AsyncTestLV.log.exists(_._2 == Cell(1))) val res = await(m2(b)) // await$6$1 - assert(AsyncTestLV.log.exists(_ == ("await$5$1" -> Cell(2)))) + assert(AsyncTestLV.log.exists(_._2 == Cell(2))) res } @@ -84,9 +84,9 @@ class LiveVariablesSpec { // a == 1 val b: Any = await(m1(a)) // await$8$1 // b == Cell(2) - assert(!AsyncTestLV.log.exists(p => p._1 == "await$7$1")) + // assert(!AsyncTestLV.log.exists(p => p._1 == "await$7$1")) val res = await(m2(b)) // await$9$1 - assert(AsyncTestLV.log.exists(_ == ("await$8$1" -> Cell(2)))) + assert(AsyncTestLV.log.exists(_._2 == Cell(2))) res } @@ -108,9 +108,9 @@ class LiveVariablesSpec { // a == Cell(1) val b: Meter = await(m1(a)) // await$11$1 // b == Meter(2) - assert(AsyncTestLV.log.exists(_ == ("await$10$1" -> Cell(1)))) + assert(AsyncTestLV.log.exists(_._2 == Cell(1))) val res = await(m2(b.len)) // await$12$1 - assert(AsyncTestLV.log.exists(entry => entry._1 == "await$11$1" && entry._2.asInstanceOf[Meter].len == 2L)) + assert(AsyncTestLV.log.exists(_._2.asInstanceOf[Meter].len == 2L)) res } @@ -138,12 +138,12 @@ class LiveVariablesSpec { } // state #3 - assert(AsyncTestLV.log.exists(entry => entry._1 == "await$14$1")) + // assert(AsyncTestLV.log.exists(entry => entry._1 == "await$14$1")) val b = await(m1(a, y.v)) // await$15$1 // state #8 - assert(AsyncTestLV.log.exists(_ == ("a$1" -> MCell(10))), AsyncTestLV.log) - assert(AsyncTestLV.log.exists(_ == ("y$1" -> MCell(11)))) + assert(AsyncTestLV.log.exists(_._2 == MCell(10)), AsyncTestLV.log) + assert(AsyncTestLV.log.exists(_._2 == MCell(11))) b } |