From 4b265ca213d066897db3a2333a3ac1420e8a480f Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 22 Jul 2013 15:05:39 +0200 Subject: Added eta-expansion. Also: made RepeatedParamTypes aliases of their underlying types, in order to avoid any incompatibilities. --- src/dotty/tools/dotc/ast/CheckTrees.scala | 4 +- src/dotty/tools/dotc/ast/TypedTrees.scala | 19 ++-- src/dotty/tools/dotc/core/Definitions.scala | 21 ++-- src/dotty/tools/dotc/core/Types.scala | 9 +- src/dotty/tools/dotc/core/pickling/UnPickler.scala | 2 +- src/dotty/tools/dotc/typer/Applications.scala | 34 +------ src/dotty/tools/dotc/typer/EtaExpansion.scala | 109 +++++++++++++++++++++ src/dotty/tools/dotc/typer/Typer.scala | 7 +- 8 files changed, 149 insertions(+), 56 deletions(-) create mode 100644 src/dotty/tools/dotc/typer/EtaExpansion.scala (limited to 'src/dotty/tools') diff --git a/src/dotty/tools/dotc/ast/CheckTrees.scala b/src/dotty/tools/dotc/ast/CheckTrees.scala index 4790f231b..670dd3730 100644 --- a/src/dotty/tools/dotc/ast/CheckTrees.scala +++ b/src/dotty/tools/dotc/ast/CheckTrees.scala @@ -35,8 +35,6 @@ object CheckTrees { arg match { case NamedArg(argName, _) => check(argName == name) - case SeqLiteral(_, _) => - check(formal.isRepeatedParam) case _ => check(arg.isValue) } @@ -187,7 +185,7 @@ object CheckTrees { case nme.unapplySeq => // args need to be wrapped in (...: _*) check(args.length == 1) - check(args.head.tpe.isRepeatedParam) + check(args.head.isInstanceOf[tpd.SeqLiteral]) case nme.unapply => val rtp = funtpe.resultType val rsym = rtp.dealias.typeSymbol diff --git a/src/dotty/tools/dotc/ast/TypedTrees.scala b/src/dotty/tools/dotc/ast/TypedTrees.scala index cce905d92..122de7d43 100644 --- a/src/dotty/tools/dotc/ast/TypedTrees.scala +++ b/src/dotty/tools/dotc/ast/TypedTrees.scala @@ -15,7 +15,7 @@ object tpd extends Trees.Instance[Type] { sym.annotations map (_.tree)) def Ident(tp: NamedType)(implicit ctx: Context): Ident = - Trees.Ident(tp.name).withType(tp).checked + Trees.Ident(tp.name).withType(tp.underlyingIfRepeated).checked def Select(pre: Tree, tp: NamedType)(implicit ctx: Context): Select = Trees.Select(pre, tp.name).withType(tp).checked @@ -104,16 +104,18 @@ object tpd extends Trees.Instance[Type] { * where the closure's type is the target type of the expression (FunctionN, unless * otherwise specified). */ - def Closure(meth: TermSymbol, body: Tree, target: Type = NoType)(implicit ctx: Context): Block = { + def Closure(meth: TermSymbol, bodyFn: List[Tree] => Tree, target: Type = NoType)(implicit ctx: Context): Block = { val funtpe = if (target.exists) target else meth.info match { case mt @ MethodType(_, formals) => assert(!mt.isDependent) - defn.FunctionType(formals, mt.resultType) + val formals1 = formals mapConserve (_.underlyingIfRepeated) + defn.FunctionType(formals1, mt.resultType) } + val rhsFn: List[List[Tree]] => Tree = { case args :: Nil => bodyFn(args) } Block( - DefDef(meth, body) :: Nil, + DefDef(meth, rhsFn) :: Nil, Trees.Closure(Nil, Ident(TermRef.withSym(NoPrefix, meth)))).withType(funtpe).checked } @@ -186,7 +188,10 @@ object tpd extends Trees.Instance[Type] { def ValDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): ValDef = Trees.ValDef(Modifiers(sym), sym.name, TypeTree(sym.info), rhs).withType(refType(sym)).checked - def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = { + def DefDef(sym: TermSymbol, rhs: Tree = EmptyTree)(implicit ctx: Context): DefDef = + DefDef(sym, Function.const(rhs) _) + + def DefDef(sym: TermSymbol, rhsFn: List[List[Tree]] => Tree)(implicit ctx: Context): DefDef = { val (tparams, mtp) = sym.info match { case tp: PolyType => @@ -205,10 +210,10 @@ object tpd extends Trees.Instance[Type] { case tp => (Nil, tp) } val (vparamss, rtp) = valueParamss(mtp) - + val argss = vparamss map (_ map (vparam => Ident(vparam.symRef))) Trees.DefDef( Modifiers(sym), sym.name, tparams map TypeDef, - vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhs) + vparamss map (_ map (ValDef(_))), TypeTree(rtp), rhsFn(argss)) .withType(refType(sym)).checked } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 757240c43..6bd27590c 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -46,6 +46,9 @@ class Definitions(implicit ctx: Context) { private def newMethod(cls: ClassSymbol, name: TermName, info: Type, flags: FlagSet = EmptyFlags): TermSymbol = ctx.newSymbol(cls, name.encode, flags | Method, info).entered.asTerm + private def newAliasType(name: TypeName, tpe: Type, flags: FlagSet = EmptyFlags): TypeSymbol = + ctx.newSymbol(ScalaPackageClass, name, flags, tpe).entered.asType + private def newPolyMethod(cls: ClassSymbol, name: TermName, typeParamCount: Int, resultTypeFn: PolyType => Type, flags: FlagSet = EmptyFlags) = { val tparamNames = tpnme.syntheticTypeParamNames(typeParamCount) @@ -174,8 +177,9 @@ class Definitions(implicit ctx: Context) { lazy val ByNameParamClass = specialPolyClass(tpnme.BYNAME_PARAM_CLASS, Covariant, AnyType) lazy val EqualsPatternClass = specialPolyClass(tpnme.EQUALS_PATTERN, EmptyFlags, AnyType) - lazy val JavaRepeatedParamClass = specialPolyClass(tpnme.JAVA_REPEATED_PARAM_CLASS, Contravariant, AnyRefType, ArrayType) - lazy val RepeatedParamClass = specialPolyClass(tpnme.REPEATED_PARAM_CLASS, Covariant, AnyRefType, SeqType) + + lazy val RepeatedParamAlias = newAliasType(tpnme.REPEATED_PARAM_CLASS, SeqType) + lazy val JavaRepeatedParamAlias = newAliasType(tpnme.JAVA_REPEATED_PARAM_CLASS, ArrayType) // fundamental reference classes lazy val StringClass = requiredClass("java.lang.String") @@ -245,8 +249,8 @@ class Definitions(implicit ctx: Context) { def DoubleType: Type = DoubleClass.typeConstructor def PairType: Type = PairClass.typeConstructor def StringType: Type = StringClass.typeConstructor - def JavaRepeatedParamType = JavaRepeatedParamClass.typeConstructor - def RepeatedParamType = RepeatedParamClass.typeConstructor + def RepeatedParamType = RepeatedParamAlias.typeConstructor + def JavaRepeatedParamType = JavaRepeatedParamAlias.typeConstructor def ThrowableType = ThrowableClass.typeConstructor def OptionType = OptionClass.typeConstructor @@ -285,7 +289,8 @@ class Definitions(implicit ctx: Context) { lazy val FunctionClasses: Set[Symbol] = FunctionClass.toSet lazy val TupleClasses: Set[Symbol] = TupleClass.toSet - lazy val RepeatedParamClasses: Set[Symbol] = Set(RepeatedParamClass, JavaRepeatedParamClass) + + lazy val RepeatedParamAliases: Set[Symbol] = Set(RepeatedParamAlias, JavaRepeatedParamAlias) /** Modules whose members are in the default namespace */ lazy val UnqualifiedModules: Set[TermSymbol] = Set(PredefModule, ScalaPackageVal, JavaLangPackageVal) @@ -298,7 +303,7 @@ class Definitions(implicit ctx: Context) { lazy val asInstanceOfMethods = Set[Symbol](Any_asInstanceOf, Object_asInstanceOf) lazy val isInstanceOfMethods = Set[Symbol](Any_isInstanceOf, Object_isInstanceOf) - // ----- Higher kinds machinery ------------------------------------------ + // ----- Higher kinds machinery ------------------------------------------ private var _hkTraits: Set[Symbol] = Set() @@ -422,8 +427,8 @@ class Definitions(implicit ctx: Context) { /** Lists core classes that don't have underlying bytecode, but are synthesized on-the-fly in every reflection universe */ lazy val syntheticCoreClasses = List( AnyRefAlias, - RepeatedParamClass, - JavaRepeatedParamClass, + RepeatedParamAlias, + JavaRepeatedParamAlias, ByNameParamClass, AnyClass, AnyValClass, diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index ef06c37f7..26886ed1d 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -153,7 +153,7 @@ object Types { final def occursIn(that: Type): Boolean = that.existsPart(this == _) def isRepeatedParam(implicit ctx: Context): Boolean = - defn.RepeatedParamClasses contains typeSymbol + defn.RepeatedParamAliases contains typeSymbol // ----- Higher-order combinators ----------------------------------- @@ -544,6 +544,13 @@ object Types { final def objToAny(implicit ctx: Context) = if (isClassType(defn.ObjectClass) && !ctx.phase.erasedTypes) defn.AnyType else this + /** If this is repeated parameter type, its underlying type, + * else the type itself. + */ + def underlyingIfRepeated(implicit ctx: Context): Type = + if (isRepeatedParam) dealias else this + + // ----- Access to parts -------------------------------------------- /** The normalized prefix of this type is: diff --git a/src/dotty/tools/dotc/core/pickling/UnPickler.scala b/src/dotty/tools/dotc/core/pickling/UnPickler.scala index 24259211a..3b9ca7ba5 100644 --- a/src/dotty/tools/dotc/core/pickling/UnPickler.scala +++ b/src/dotty/tools/dotc/core/pickling/UnPickler.scala @@ -991,7 +991,7 @@ class UnPickler(bytes: Array[Byte], classRoot: ClassDenotation, moduleClassRoot: val vparams = until(end, readValDefRef) val applyType = MethodType(vparams map (_.name), vparams map (_.tpt.tpe), body.tpe) val applyMeth = cctx.newSymbol(symbol.owner, nme.apply, Method, applyType) - Closure(applyMeth, body.changeOwner(symbol, applyMeth), tpe) + Closure(applyMeth, Function.const(body.changeOwner(symbol, applyMeth)), tpe) case ASSIGNtree => val lhs = readTreeRef() diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index cb397a692..7cc35387e 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -8,7 +8,6 @@ import util.Positions._ import Trees.Untyped import Mode.ImplicitsDisabled import Contexts._ -import Types._ import Flags._ import Denotations._ import NameOps._ @@ -19,6 +18,7 @@ import Names._ import StdNames._ import Constants._ import Inferencing._ +import EtaExpansion._ import collection.mutable import language.implicitConversions @@ -70,38 +70,6 @@ trait Applications extends Compatibility{ self: Typer => private def state(implicit ctx: Context) = ctx.typerState - def lift(defs: mutable.ListBuffer[tpd.Tree], expr: tpd.Tree, prefix: String = "")(implicit ctx: Context): tpd.Tree = - if (TreeInfo.isIdempotentExpr(expr)) expr - else { - val name = ctx.freshName(prefix).toTermName - val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe, coord = positionCoord(expr.pos)) - defs += tpd.ValDef(sym, expr) - tpd.Ident(sym.symRef) - } - - def liftArgs(defs: mutable.ListBuffer[tpd.Tree], methType: Type, args: List[tpd.Tree])(implicit ctx: Context) = { - val paramPrefixes = methType match { - case MethodType(paramNames, _) => paramNames map (_.toString) - case _ => args map (_ => "") - } - for ((arg, prefix) <- args zip paramPrefixes) yield lift(defs, arg, prefix) - } - - def liftApp(defs: mutable.ListBuffer[tpd.Tree], tree: tpd.Tree)(implicit ctx: Context): tpd.Tree = tree match { - case Apply(fn, args) => - tree.derivedApply(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) - case TypeApply(fn, targs) => - tree.derivedTypeApply(liftApp(defs, fn), targs) - case Select(pre, name) => - tree.derivedSelect(lift(defs, pre), name) - case Ident(name) => - lift(defs, tree) - case Block(stats, expr) => - liftApp(defs ++= stats, expr) - case _ => - tree - } - /** * @param Arg the type of arguments, could be tpd.Tree, untpd.Tree, or Type * @param methRef the reference to the method of the application diff --git a/src/dotty/tools/dotc/typer/EtaExpansion.scala b/src/dotty/tools/dotc/typer/EtaExpansion.scala new file mode 100644 index 000000000..dffe8aca9 --- /dev/null +++ b/src/dotty/tools/dotc/typer/EtaExpansion.scala @@ -0,0 +1,109 @@ +package dotty.tools +package dotc +package typer + +import core._ +import ast.{Trees, untpd, tpd, TreeInfo} +import Contexts._ +import Types._ +import Flags._ +import NameOps._ +import Symbols._ +import Decorators._ +import Names._ +import StdNames._ +import Trees._ +import util.Positions._ +import collection.mutable + +object EtaExpansion { + + import tpd._ + + def lift(defs: mutable.ListBuffer[Tree], expr: Tree, prefix: String = "")(implicit ctx: Context): Tree = + if (TreeInfo.isIdempotentExpr(expr)) expr + else { + val name = ctx.freshName(prefix).toTermName + val sym = ctx.newSymbol(ctx.owner, name, EmptyFlags, expr.tpe, coord = positionCoord(expr.pos)) + defs += ValDef(sym, expr) + Ident(sym.symRef) + } + + def liftArgs(defs: mutable.ListBuffer[Tree], methType: Type, args: List[Tree])(implicit ctx: Context) = { + def toPrefix(name: Name) = if (name contains '$') "" else name.toString + def isByName(tp: Type) = tp.typeSymbol == defn.ByNameParamClass + val paramInfos = methType match { + case MethodType(paramNames, paramTypes) => + (paramNames, paramTypes).zipped map ((name, tp) => + (toPrefix(name), isByName(tp))) + case _ => + args map Function.const(("", false)) + } + for ((arg, (prefix, isByName)) <- args zip paramInfos) + yield + if (isByName) arg else lift(defs, arg, prefix) + } + + def liftApp(defs: mutable.ListBuffer[Tree], tree: Tree)(implicit ctx: Context): Tree = tree match { + case Trees.Apply(fn, args) => + tree.derivedApply(liftApp(defs, fn), liftArgs(defs, fn.tpe, args)) + case Trees.TypeApply(fn, targs) => + tree.derivedTypeApply(liftApp(defs, fn), targs) + case Trees.Select(pre, name) => + tree.derivedSelect(lift(defs, pre), name) + case Trees.Ident(name) => + lift(defs, tree) + case Trees.Block(stats, expr) => + liftApp(defs ++= stats, expr) + case _ => + tree + } + + /**

+ * Expand partial function applications of type `type`. + *

+   *  p.f(es_1)...(es_n)
+   *     ==>  {
+   *            private synthetic val eta$f   = p.f   // if p is not stable
+   *            ...
+   *            private synthetic val eta$e_i = e_i    // if e_i is not stable
+   *            ...
+   *            (ps_1 => ... => ps_m => eta$f([es_1])...([es_m])(ps_1)...(ps_m))
+   *          }
+ *

+ * tree is already attributed + *

+ def etaExpandUntyped(tree: Tree)(implicit ctx: Context): untpd.Tree = { // kept as a reserve for now + def expand(tree: Tree): untpd.Tree = tree.tpe match { + case mt @ MethodType(paramNames, paramTypes) if !mt.isImplicit => + val paramsArgs: List[(untpd.ValDef, untpd.Tree)] = + (paramNames, paramTypes).zipped.map { (name, tp) => + val droppedStarTpe = defn.underlyingOfRepeated(tp) + val param = Trees.ValDef( + Trees.Modifiers(Param), name, + untpd.TypedSplice(TypeTree(droppedStarTpe)), untpd.EmptyTree) + var arg: untpd.Tree = Trees.Ident(name) + if (defn.isRepeatedParam(tp)) + arg = Trees.Typed(arg, Trees.Ident(tpnme.WILDCARD_STAR)) + (param, arg) + } + val (params, args) = paramsArgs.unzip + untpd.Function(params, Trees.Apply(untpd.TypedSplice(tree), args)) + } + + val defs = new mutable.ListBuffer[Tree] + val tree1 = liftApp(defs, tree) + Trees.Block(defs.toList map untpd.TypedSplice, expand(tree1)) + } + */ + + def etaExpand(tree: Tree, tpe: MethodType)(implicit ctx: Context): Tree = { + def expand(tree: Tree): Tree = { + val meth = ctx.newSymbol(ctx.owner, nme.ANON_FUN, Synthetic, tpe, coord = tree.pos) + Closure(meth, Apply(tree, _)) + } + val defs = new mutable.ListBuffer[Tree] + val tree1 = liftApp(defs, tree) + Block(defs.toList, expand(tree1)) + } +} \ No newline at end of file diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 2734807a6..a82cf1eda 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -18,6 +18,7 @@ import Names._ import NameOps._ import Flags._ import Decorators._ +import EtaExpansion.etaExpand import util.Positions._ import util.SourcePosition import collection.mutable @@ -237,7 +238,7 @@ class Typer extends Namer with Applications with Implicits { ctx.error(s"not found: $name", tree.pos) ErrorType } - tree.withType(ownType) + tree.withType(ownType.underlyingIfRepeated) } def typedSelect(tree: untpd.Select, pt: Type)(implicit ctx: Context): Tree = { @@ -310,6 +311,7 @@ class Typer extends Namer with Applications with Implicits { val tpt1 = typedType(tpt) val rhs1 = typedExpr(rhs, tpt1.tpe) ddef.withType(sym.symRef).derivedDefDef(mods1, name, tparams1, vparamss1, tpt1, rhs1) + //todo: make sure dependent method types do not depend on implicits or by-name params } def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(implicit ctx: Context): TypeDef = { @@ -455,7 +457,6 @@ class Typer extends Namer with Applications with Implicits { def summarize(tpe: Type): String = ??? - /** * (-1) For expressions with annotated types, let AnnotationCheckers decide what to do * (0) Convert expressions with constant types to literals (unless in interactive/scaladoc mode) @@ -542,7 +543,7 @@ class Typer extends Namer with Applications with Implicits { adapt(tpd.Apply(tree, args), pt) case tp: MethodType => if (defn.isFunctionType(pt) && !tree.symbol.isConstructor) - ??? // etaExpand() + etaExpand(tree, tp) else if (tp.paramTypes.isEmpty) adapt(tpd.Apply(tree, Nil), pt) else -- cgit v1.2.3