From a18e64574cefbd7de32f482a54324e1bd4b4767c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Mar 2014 10:48:44 +0100 Subject: [PATCH 1/2] 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))`. --- 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 | 10 ++++++++-- 4 files changed, 43 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index bb30d9a9c7a6..a4251b2cb786 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -703,6 +703,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 cf0d7d0a1b1a..4596ef0a197f 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -435,8 +435,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 => @@ -677,7 +686,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) 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 b4068408b4ab..74be3f4cd1c6 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 51eba3b02564..bb218f53368b 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -1069,8 +1069,14 @@ 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) adaptToArgs(wtp, pt.tupled) + else tree case _ => tryInsertApply(tree, pt) { val more = tree match { case Apply(_, _) => " more" From a7deafaa13f726d393db454e6bf77cc01524c089 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 7 Mar 2014 11:05:02 +0100 Subject: [PATCH 2/2] Removed explicit tuplings from dotc codebase. Eliminated all "Dotty deviations" which were due to lack of auto-tupling. --- src/dotty/tools/dotc/ast/Desugar.scala | 2 +- src/dotty/tools/dotc/config/Settings.scala | 2 +- src/dotty/tools/dotc/core/Contexts.scala | 2 +- src/dotty/tools/dotc/core/Definitions.scala | 2 +- src/dotty/tools/dotc/core/pickling/PickleBuffer.scala | 10 +++++----- .../tools/dotc/reporting/UniqueMessagePositions.scala | 2 +- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 027c3238d91c..01aaf0b5ffe7 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -728,7 +728,7 @@ object desugar { */ private object VarPattern { def unapply(tree: Tree)(implicit ctx: Context): Option[VarInfo] = tree match { - case id: Ident => Some((id, TypeTree())) // Dotty deviation: No auto-tupling + case id: Ident => Some(id, TypeTree()) case Typed(id: Ident, tpt) => Some((id, tpt)) case _ => None } diff --git a/src/dotty/tools/dotc/config/Settings.scala b/src/dotty/tools/dotc/config/Settings.scala index 7f2ad2a4fbd7..b3bf2f1348be 100644 --- a/src/dotty/tools/dotc/config/Settings.scala +++ b/src/dotty/tools/dotc/config/Settings.scala @@ -60,7 +60,7 @@ object Settings { copy(aliases = aliases :+ abbrv)(idx) def dependsOn[U](setting: Setting[U], value: U): Setting[T] = - copy(depends = depends :+ ((setting, value)))(idx) // Dotty deviation: no auto-tupling + copy(depends = depends :+ (setting, value))(idx) def valueIn(state: SettingsState): T = state.value(idx).asInstanceOf[T] diff --git a/src/dotty/tools/dotc/core/Contexts.scala b/src/dotty/tools/dotc/core/Contexts.scala index 9be2e2f43dd9..37d16e84754f 100644 --- a/src/dotty/tools/dotc/core/Contexts.scala +++ b/src/dotty/tools/dotc/core/Contexts.scala @@ -273,7 +273,7 @@ object Contexts { newctx.implicitsCache = null newctx.setCreationTrace() // Dotty deviation: Scala2x allows access to private members implicitCache and setCreationTrace - // even from a subclass prefix. Dotty (and Java) do not. I think that's a bug in Scala2x. + // even from a subclass prefix. Dotty (and Java) do not. It's confirmed as a bug in Scala2x. newctx.asInstanceOf[FreshContext] } diff --git a/src/dotty/tools/dotc/core/Definitions.scala b/src/dotty/tools/dotc/core/Definitions.scala index ef16a970dfa4..7b562278a065 100644 --- a/src/dotty/tools/dotc/core/Definitions.scala +++ b/src/dotty/tools/dotc/core/Definitions.scala @@ -289,7 +289,7 @@ class Definitions { lazy val targs = ft.argInfos if ((FunctionClasses contains tsym) && (targs.length - 1 <= MaxFunctionArity) && - (FunctionClass(targs.length - 1) == tsym)) Some((targs.init, targs.last)) // Dotty deviation: no auto-tupling + (FunctionClass(targs.length - 1) == tsym)) Some(targs.init, targs.last) else None } } diff --git a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala index 2bedcab92cfb..ef2b4acb2700 100644 --- a/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala +++ b/src/dotty/tools/dotc/core/pickling/PickleBuffer.scala @@ -229,8 +229,8 @@ object PickleBuffer { PARAM -> Param, PACKAGE -> Package, MACRO -> Macro, - BYNAMEPARAM -> ((Method, Covariant)), // Dotty deviation: no auto-tupling - LABEL -> ((Label, Contravariant)), // Dotty deviation: no auto-tupling + BYNAMEPARAM -> (Method, Covariant), + LABEL -> (Label, Contravariant), ABSOVERRIDE -> AbsOverride, LOCAL -> Local, JAVA -> JavaDefined, @@ -238,16 +238,16 @@ object PickleBuffer { STABLE -> Stable, STATIC -> Static, CASEACCESSOR -> CaseAccessor, - DEFAULTPARAM -> ((DefaultParameterized, Trait)), // Dotty deviation: no auto-tupling + DEFAULTPARAM -> (DefaultParameterized, Trait), BRIDGE -> Bridge, ACCESSOR -> Accessor, SUPERACCESSOR -> SuperAccessor, PARAMACCESSOR -> ParamAccessor, MODULEVAR -> Scala2ModuleVar, LAZY -> Lazy, - MIXEDIN -> ((MixedIn, Scala2Existential)), // Dotty deviation: no auto-tupling + MIXEDIN -> (MixedIn, Scala2Existential), EXPANDEDNAME -> ExpandedName, - IMPLCLASS -> ((Scala2PreSuper, ImplClass)), // Dotty deviation: no auto-tupling + IMPLCLASS -> (Scala2PreSuper, ImplClass), SPECIALIZED -> Specialized, DEFAULTINIT -> DefaultInit, VBRIDGE -> VBridge, diff --git a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala index 483228693d5b..93e24b27f04f 100644 --- a/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala +++ b/src/dotty/tools/dotc/reporting/UniqueMessagePositions.scala @@ -21,7 +21,7 @@ trait UniqueMessagePositions extends Reporter { override def isHidden(d: Diagnostic)(implicit ctx: Context): Boolean = super.isHidden(d) || { d.pos.exists && { - positions get ((ctx.source, d.pos.point)) match { // Dotty deviation: no autotupling + positions get (ctx.source, d.pos.point) match { case Some(s) if s.level >= d.severity.level => true case _ => positions((ctx.source, d.pos.point)) = d.severity; false }