From 0aa3f4cecfff4a2c883a5b814c551f399dc425e1 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 14 Apr 2014 14:42:05 +0200 Subject: New phase: Nullarify Eliminates ExprTypes and PolyTypes over value types. --- src/dotty/tools/dotc/Compiler.scala | 11 +- src/dotty/tools/dotc/core/Definitions.scala | 5 +- src/dotty/tools/dotc/transform/Erasure.scala | 2 +- src/dotty/tools/dotc/transform/LazyVals.scala | 1 - src/dotty/tools/dotc/transform/Nullarify.scala | 148 +++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 6 deletions(-) create mode 100644 src/dotty/tools/dotc/transform/Nullarify.scala (limited to 'src') diff --git a/src/dotty/tools/dotc/Compiler.scala b/src/dotty/tools/dotc/Compiler.scala index 2ba75c56b..457238da4 100644 --- a/src/dotty/tools/dotc/Compiler.scala +++ b/src/dotty/tools/dotc/Compiler.scala @@ -20,9 +20,14 @@ class Compiler { def phases: List[List[Phase]] = List( List(new FrontEnd), - List(new LazyValsCreateCompanionObjects, new TailRec), //force separataion between lazyVals and LVCreateCO - List(new PatternMatcher, new LazyValTranformContext().transformer, - new Splitter, new TypeTestsCasts, new InterceptedMethods), + List(new LazyValsCreateCompanionObjects, + new TailRec), //force separataion between lazyVals and LVCreateCO + List(new PatternMatcher, + new LazyValTranformContext().transformer, + new Splitter), + List(new Nullarify, + new TypeTestsCasts, + new InterceptedMethods), List(new Erasure), List(new UncurryTreeTransform) ) diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 53e8b4d2c..f1c69027e 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -341,6 +341,8 @@ class Definitions { lazy val AbstractFunctionClass = mkArityArray("scala.runtime.AbstractFunction", MaxFunctionArity, 0) lazy val FunctionClass = mkArityArray("scala.Function", MaxFunctionArity, 0) + lazy val Function0_apply = FunctionClass(0).requiredMethod(nme.apply) + lazy val TupleClass = mkArityArray("scala.Tuple", MaxTupleArity, 2) lazy val ProductNClass = mkArityArray("scala.Product", MaxTupleArity, 2) @@ -357,6 +359,7 @@ class Definitions { lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf, Object_asInstanceOf) lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf, Object_isInstanceOf) + lazy val typeTestsOrCasts = asInstanceOfMethods ++ isInstanceOfMethods lazy val RootImports = List[Symbol](JavaLangPackageVal, ScalaPackageVal, ScalaPredefModule, DottyPredefModule) @@ -440,7 +443,7 @@ class Definitions { LongClass, FloatClass, DoubleClass) - + lazy val ScalaValueClasses: collection.Set[Symbol] = ScalaNumericValueClasses + UnitClass + BooleanClass lazy val ScalaBoxedClasses = ScalaValueClasses map boxedClass diff --git a/src/dotty/tools/dotc/transform/Erasure.scala b/src/dotty/tools/dotc/transform/Erasure.scala index d0c51c8b9..562a30682 100644 --- a/src/dotty/tools/dotc/transform/Erasure.scala +++ b/src/dotty/tools/dotc/transform/Erasure.scala @@ -28,7 +28,7 @@ class Erasure extends Phase with DenotTransformer { override def name: String = "erasure" /** List of names of phases that should precede this phase */ - override def runsAfter: Set[String] = Set("typeTestsCasts", "intercepted", "splitter") + override def runsAfter: Set[String] = Set("typeTestsCasts", "intercepted", "splitter", "nullarify") def transform(ref: SingleDenotation)(implicit ctx: Context): SingleDenotation = ref match { case ref: SymDenotation => diff --git a/src/dotty/tools/dotc/transform/LazyVals.scala b/src/dotty/tools/dotc/transform/LazyVals.scala index cb5fb79cd..840ca0cdb 100644 --- a/src/dotty/tools/dotc/transform/LazyVals.scala +++ b/src/dotty/tools/dotc/transform/LazyVals.scala @@ -68,7 +68,6 @@ class LazyValTranformContext { override def runsAfterGroupsOf: Set[String] = Set("lazyValsModules") /** List of names of phases that should have finished their processing of all compilation units * before this phase starts */ - override def runsAfter: Set[String] = Set("lazyValsModules") /** List of names of phases that should have finished processing of tree * before this phase starts processing same tree */ diff --git a/src/dotty/tools/dotc/transform/Nullarify.scala b/src/dotty/tools/dotc/transform/Nullarify.scala new file mode 100644 index 000000000..8ca41c07f --- /dev/null +++ b/src/dotty/tools/dotc/transform/Nullarify.scala @@ -0,0 +1,148 @@ +package dotty.tools.dotc +package transform + +import TreeTransforms._ +import core.DenotTransformers._ +import core.Symbols._ +import core.Contexts._ +import core.Types._ +import core.Flags._ +import core.Decorators._ +import core.StdNames.nme +import ast.Trees._ + +/** This phase eliminates ExprTypes `=> T` and PolyTypes over value types `[X]T`. + * They are expressed in terms of nullary method or function types. More precisely: + * + * For types: + * + * => T ==> () => T if T is the type of a parameter + * ==> ()T otherwise + * [X]T ==> [X]()T + * + * For definitions: + * + * def f: R ==> def f(): R + * def f[X]: R ==> def f[X](): R + * (x: => T) ==> (x: () => T) + * + * For terms: + * + * f ==> f() if f had type => T and is not a parameter + * x ==> x.apply() if x is a parameter that had type => T + * e.apply() ==> e if e.apply() is an argument to a call-by-name parameter + * expr ==> () => expr if other expr is an argument to a call-by-name parameter + * + */ +class Nullarify extends TreeTransform with InfoTransformer { + import ast.tpd._ + + override def name: String = "nullarify" + + override def runsAfterGroupsOf: Set[String] = Set("splitter") + // assumes idents and selects have symbols; interferes with splitter distribution + // that's why it's "after group". + + override def transformApply(tree: Apply)(implicit ctx: Context, info: TransformerInfo): Tree = + ctx.traceIndented(s"transforming ${tree.show} at phase ${ctx.phase}", show = true) { + + def transformArg(arg: Tree, formal: Type): Tree = formal match { + case _: ExprType => + arg match { + case Apply(Select(qual, nme.apply), Nil) => qual + case _ => + val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic, + MethodType(Nil, Nil, arg.tpe.widen)) + Closure(meth, _ => arg) + } + case _ => + arg + } + + // Compute the method type tree had before this phase is run. + // This is needed to find out which parameters are by-name. + val funType = tree.fun.symbol.info match { + case info: PolyType => info.resultType + case info => info + } + def methType(info: Type, tree: Tree): Type = tree match { + case Apply(fn, args) => methType(info.resultType, fn) + case _ => info + } + val MethodType(_, formals) = methType(funType, tree.fun) + + val args1 = tree.args.zipWithConserve(formals)(transformArg) + cpy.Apply(tree, tree.fun, args1) withType nullarify(tree.tpe) + } + + /** Insert () or .apply() if the term refers to something that was converted to a + * nullary method. Also, transform its type. + */ + def insertParens(tree: Tree)(implicit ctx: Context): Tree = { + val tp1 = transformInfo(tree.tpe, tree.symbol) + val tree1 = tree.withType(tp1) + val origType = tree.tpe.widenSingleton + def result(implicit ctx: Context) = { + tp1.widen match { + case MethodType(Nil, _) if origType.widenExpr.isInstanceOf[ValueType] => + Apply(tree1, Nil) + case _ => + origType match { + case _: ExprType => // it's a by-name parameter + Apply(Select(tree1, defn.Function0_apply), Nil) + case _ => + tree1 + } + } + } + result(ctx.withPhase(ctx.phase.next)) + } + + override def transformIdent(tree: Ident)(implicit ctx: Context, info: TransformerInfo): Tree = + insertParens(tree) + + override def transformSelect(tree: Select)(implicit ctx: Context, info: TransformerInfo): Tree = + insertParens(tree) + + override def transformTypeApply(tree: TypeApply)(implicit ctx: Context, info: TransformerInfo): Tree = + insertParens(tree) + + override def transformDefDef(tree: DefDef)(implicit ctx: Context, info: TransformerInfo): Tree = { + val DefDef(mods, name, tparams, vparamss, tpt, rhs) = tree + val vparamss1 = + if (vparamss.isEmpty) Nil :: Nil + else vparamss nestedMap { vparam => + val tp = vparam.tpt.tpe + val tp1 = nullarifyParam(tp) + if (tp eq tp1) vparam + else cpy.ValDef(vparam, vparam.mods, vparam.name, vparam.tpt.withType(tp1), vparam.rhs) + } + cpy.DefDef(tree, mods, name, tparams, vparamss1, tpt, rhs) + } + + def nullarify(tp: Type)(implicit ctx: Context): Type = tp match { + case ExprType(rt) => + MethodType(Nil, Nil, rt) + case pt: PolyType => + val rt = pt.resultType match { + case mt: MethodType => nullarify(mt) + case rt => MethodType(Nil, Nil, rt) + } + pt.derivedPolyType(pt.paramNames, pt.paramBounds, rt) + case mt: MethodType => + mt.derivedMethodType(mt.paramNames, mt.paramTypes mapConserve nullarifyParam, + nullarify(mt.resultType)) + case _ => + tp + } + + def nullarifyParam(tp: Type)(implicit ctx: Context) = tp match { + case ExprType(rt) => defn.FunctionType(Nil, rt) + case _ => tp + } + + def transformInfo(tp: Type, sym: Symbol)(implicit ctx: Context): Type = + if (defn.typeTestsOrCasts contains sym) tp + else if (sym is Param) nullarifyParam(tp) + else nullarify(tp) +} \ No newline at end of file -- cgit v1.2.3