From 574a148fd561a793ee522c2be18ee02214236d80 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 16 Mar 2014 12:39:56 +0100 Subject: Added auto-tupling. Auto-tupling should satisfy the following spec. 1. An application `f(args)` where `f` is a non-overloaded method which has a single, non-repeated parameter as its first parameter list and where args consists of two or more arguments is expanded to `f((args))`. 2. A constructor pattern `C(args)` where `C.unapply` is a non-overloaded method which has a single, non-repeated parameter as its first parameter list and where args consists of two or more arguments is expanded to `C((args))`. Auto-tupling can be disabled by language feature "noAutoTupling". Conflicts: test/dotc/tests.scala --- src/dotty/tools/dotc/core/Definitions.scala | 2 +- src/dotty/tools/dotc/core/TypeOps.scala | 5 +++++ src/dotty/tools/dotc/core/Types.scala | 7 +++++++ src/dotty/tools/dotc/typer/Applications.scala | 16 ++++++++++++++-- src/dotty/tools/dotc/typer/ProtoTypes.scala | 14 ++++++++++++++ src/dotty/tools/dotc/typer/Typer.scala | 12 ++++++++++-- 6 files changed, 51 insertions(+), 5 deletions(-) (limited to 'src/dotty/tools/dotc') diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index 5c2890f29..bcf2bb74c 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -215,7 +215,7 @@ class Definitions { lazy val JavaSerializableClass = ctx.requiredClass("java.lang.Serializable") lazy val ComparableClass = ctx.requiredClass("java.lang.Comparable") lazy val ProductClass = ctx.requiredClass("scala.Product") - lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass + lazy val LanguageModuleClass = ctx.requiredModule("dotty.language").moduleClass.asClass // Annotation base classes lazy val AnnotationClass = ctx.requiredClass("scala.annotation.Annotation") diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 4ace0bebe..22a86766b 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -5,6 +5,7 @@ import Contexts._, Types._, Symbols._, Names._, Flags._, Scopes._ import SymDenotations._ import config.Printers._ import Decorators._ +import StdNames._ import util.SimpleMap trait TypeOps { this: Context => @@ -300,6 +301,10 @@ trait TypeOps { this: Context => def hasOption = ctx.base.settings.language.value exists (s => s == featureName || s == "_") hasImport || hasOption } + + /** Is auto-tupling enabled? */ + def canAutoTuple = + !featureEnabled(defn.LanguageModuleClass, nme.noAutoTupling) } object TypeOps { diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 6dda937d2..a0293d50a 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -685,6 +685,13 @@ object Types { case _ => Nil } + /** The parameter types in the first parameter section of a PolyType or MethodType, Empty list for others */ + final def firstParamTypes: List[Type] = this match { + case mt: MethodType => mt.paramTypes + case pt: PolyType => pt.resultType.firstParamTypes + case _ => Nil + } + /** Is this either not a method at all, or a parameterless method? */ final def isParameterless: Boolean = this match { case mt: MethodType => false diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index feaf678a2..4d26532d6 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -434,8 +434,17 @@ trait Applications extends Compatibility { self: Typer => def typedApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { def realApply(implicit ctx: Context): Tree = track("realApply") { - val proto = new FunProto(tree.args, pt, this) + var proto = new FunProto(tree.args, pt, this) val fun1 = typedExpr(tree.fun, proto) + + // Warning: The following line is dirty and fragile. We record that auto-tupling was demanded as + // a side effect in adapt. If it was, we assume the tupled proto-type in the rest of the application. + // This crucially relies on he fact that `proto` is used only in a single call of `adapt`, + // otherwise we would get possible cross-talk between different `adapt` calls using the same + // prototype. A cleaner alternative would be to return a modified prototype from `adapt` together with + // a modified tree but this would be more convoluted and less efficient. + if (proto.isTupled) proto = proto.tupled + methPart(fun1).tpe match { case funRef: TermRef => tryEither { implicit ctx => @@ -676,7 +685,10 @@ trait Applications extends Compatibility { self: Typer => var argTypes = unapplyArgs(unapplyApp.tpe) for (argType <- argTypes) assert(!argType.isInstanceOf[TypeBounds], unapplyApp.tpe.show) val bunchedArgs = argTypes match { - case argType :: Nil if argType.isRepeatedParam => untpd.SeqLiteral(args) :: Nil + case argType :: Nil => + if (argType.isRepeatedParam) untpd.SeqLiteral(args) :: Nil + else if (args.lengthCompare(1) > 0 && ctx.canAutoTuple) untpd.Tuple(args) :: Nil + else args case _ => args } if (argTypes.length != bunchedArgs.length) { diff --git a/src/dotty/tools/dotc/typer/ProtoTypes.scala b/src/dotty/tools/dotc/typer/ProtoTypes.scala index b4068408b..74be3f4cd 100644 --- a/src/dotty/tools/dotc/typer/ProtoTypes.scala +++ b/src/dotty/tools/dotc/typer/ProtoTypes.scala @@ -187,6 +187,20 @@ object ProtoTypes { typer.adapt(targ, formal) } + private var myTupled: Type = NoType + + /** The same proto-type but with all arguments combined in a single tuple */ + def tupled: FunProto = myTupled match { + case pt: FunProto => + pt + case _ => + myTupled = new FunProto(untpd.Tuple(args) :: Nil, resultType, typer) + tupled + } + + /** Somebody called the `tupled` method of this prototype */ + def isTupled: Boolean = myTupled.isInstanceOf[FunProto] + override def toString = s"FunProto(${args mkString ","} => $resultType)" def map(tm: TypeMap)(implicit ctx: Context): FunProto = diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 17c77be89..f961a4544 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1072,8 +1072,16 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit } } - def adaptToArgs(wtp: Type, pt: FunProto) = wtp match { - case _: MethodType | _: PolyType => tree + def adaptToArgs(wtp: Type, pt: FunProto): Tree = wtp match { + case _: MethodType | _: PolyType => + def isUnary = wtp.firstParamTypes match { + case ptype :: Nil => !ptype.isRepeatedParam + case _ => false + } + if (pt.args.lengthCompare(1) > 0 && isUnary && ctx.canAutoTuple) + adaptToArgs(wtp, pt.tupled) + else + tree case _ => tryInsertApply(tree, pt) { val more = tree match { case Apply(_, _) => " more" -- cgit v1.2.3