From 08fe4076136753aa59b8bf0b1a6fe4dceb3b5e9e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 4 Jan 2016 15:32:06 +0100 Subject: [PATCH 01/26] Remove unnecessary logic in toBounds --- src/dotty/tools/dotc/core/TypeApplications.scala | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index e09328205028..df842a1efaf3 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -548,8 +548,11 @@ class TypeApplications(val self: Type) extends AnyVal { self case _ => val v = tparam.variance + /* Not neeeded. if (v > 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.upper(self) else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) + else + */ else TypeAlias(self, v) } From a0b6c8e7188f62cdccf94fa72567624ce11a09f8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Thu, 7 Jan 2016 14:21:43 +0100 Subject: [PATCH 02/26] Support named type parameters Changes needed to support simple named type parameters. Not yet implemented: named arguments. --- src/dotty/tools/dotc/core/Flags.scala | 2 ++ src/dotty/tools/dotc/core/TypeApplications.scala | 2 +- src/dotty/tools/dotc/core/Types.scala | 8 +++++++- src/dotty/tools/dotc/parsing/Parsers.scala | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index 8c9db3a5c965..c60925728a26 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -594,6 +594,8 @@ object Flags { /** A private parameter accessor */ final val PrivateParamAccessor = allOf(Private, ParamAccessor) + final val NamedTypeParam = allOf(TypeParam, ParamAccessor) + /** A local parameter */ final val ParamAndLocal = allOf(Param, Local) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index df842a1efaf3..4726a16bbd57 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -553,7 +553,7 @@ class TypeApplications(val self: Type) extends AnyVal { else if (v < 0 && !(tparam is Local) && !(tparam is ExpandedTypeParam)) TypeBounds.lower(self) else */ - else TypeAlias(self, v) + TypeAlias(self, v) } /** The type arguments of this type's base type instance wrt. `base`. diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 8595da6405a9..27828c5889ec 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -485,7 +485,13 @@ object Types { } def goThis(tp: ThisType) = { val d = go(tp.underlying) - if (d.exists) d + if (d.exists) + if ((pre eq tp) && d.symbol.is(NamedTypeParam) && (d.symbol.owner eq tp.cls)) + // If we look for a named type parameter `P` in `C.this.P`, looking up + // the fully applied self type of `C` will give as an info the alias type + // `P = this.P`. We need to return a denotation with the underlying bounds instead. + d.symbol.denot + else d else // There is a special case to handle: // trait Super { this: Sub => private class Inner {} println(this.Inner) } diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index bad6b95dc469..983ee1ccfbfe 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1493,7 +1493,7 @@ object Parsers { atPos(modStart, in.offset) { if (in.token == TYPE) { in.nextToken() - mods | Param + mods | Param | ParamAccessor } else { if (mods.hasFlags) syntaxError("`type' expected") mods | Param | PrivateLocal From 9c3b6fad7cbe276b6cb749acbdaeb834e6323b49 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 9 Jan 2016 14:39:44 +0100 Subject: [PATCH 03/26] First version of named type arguments --- docs/SyntaxSummary.txt | 4 +- src/dotty/tools/dotc/parsing/Parsers.scala | 40 ++++++++++++++++--- src/dotty/tools/dotc/typer/TypeAssigner.scala | 26 ++++++++++-- src/dotty/tools/dotc/typer/Typer.scala | 38 +++++++++++------- tests/pickling/named-params.scala | 20 ++++++++++ tests/pos/named-params.scala | 17 ++++++++ 6 files changed, 121 insertions(+), 24 deletions(-) create mode 100644 tests/pickling/named-params.scala create mode 100644 tests/pos/named-params.scala diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt index 11f23da9455e..78f59e6d1d80 100644 --- a/docs/SyntaxSummary.txt +++ b/docs/SyntaxSummary.txt @@ -103,7 +103,7 @@ grammar. RefinedType ::= WithType {[nl] Refinement} RefinedTypeTree(t, ds) WithType ::= AnnotType {`with' AnnotType} (deprecated) AnnotType ::= SimpleType {Annotation} Annotated(t, annot) - SimpleType ::= SimpleType TypeArgs AppliedTypeTree(t, args) + SimpleType ::= SimpleType (TypeArgs | NamedTypeArgs) AppliedTypeTree(t, args) | SimpleType `#' id SelectFromTypeTree(t, name) | StableId | Path `.' `type' SingletonTypeTree(p) @@ -118,6 +118,8 @@ grammar. ParamType ::= [`=>'] ParamValueType ParamValueType ::= Type [`*'] PostfixOp(t, "*") TypeArgs ::= `[' ArgTypes `]' ts + NamedTypeArg ::= id `=' ArgType NamedArg(id, t) + NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' nts Refinement ::= `{' [Dcl] {semi [Dcl]} `}' ds TypeBounds ::= [`>:' Type] [`<: Type] | INT TypeBoundsTree(lo, hi) TypeParamBounds ::= TypeBounds {`<%' Type} {`:' Type} ContextBounds(typeBounds, tps) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 983ee1ccfbfe..58ec75605b59 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -740,7 +740,7 @@ object Parsers { private def simpleTypeRest(t: Tree): Tree = in.token match { case HASH => simpleTypeRest(typeProjection(t)) - case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs()) }) + case LBRACKET => simpleTypeRest(atPos(t.pos.start) { AppliedTypeTree(t, typeArgs(namedOK = true)) }) case _ => t } @@ -759,9 +759,38 @@ object Parsers { } else typ() + /** NamedTypeArg ::= id `=' ArgType + */ + val namedTypeArg = () => { + val name = ident() + accept(EQUALS) + NamedArg(name.toTypeName, argType()) + } + /** ArgTypes ::= ArgType {`,' ArgType} + * NamedTypeArg {`,' NamedTypeArg} */ - def argTypes() = commaSeparated(argType) + def argTypes(namedOK: Boolean = false) = { + def otherArgs(first: Tree, arg: () => Tree): List[Tree] = { + val rest = + if (in.token == COMMA) { + in.nextToken() + commaSeparated(arg) + } + else Nil + first :: rest + } + if (namedOK && in.token == IDENTIFIER) + argType() match { + case Ident(name) if in.token == EQUALS => + in.nextToken() + otherArgs(NamedArg(name, argType()), namedTypeArg) + case firstArg => + if (in.token == EQUALS) println(s"??? $firstArg") + otherArgs(firstArg, argType) + } + else commaSeparated(argType) + } /** FunArgType ::= ArgType | `=>' ArgType */ @@ -785,9 +814,10 @@ object Parsers { } else t } - /** TypeArgs ::= `[' ArgType {`,' ArgType} `]' - */ - def typeArgs(): List[Tree] = inBrackets(argTypes()) + /** TypeArgs ::= `[' ArgType {`,' ArgType} `]' + * NamedTypeArgs ::= `[' NamedTypeArg {`,' NamedTypeArg} `]' + */ + def typeArgs(namedOK: Boolean = false): List[Tree] = inBrackets(argTypes(namedOK)) /** Refinement ::= `{' RefineStatSeq `}' */ diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 42ee513cd8d1..6bb6b1429b02 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -4,6 +4,7 @@ package typer import core._ import ast._ +import ast.Trees._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ import util.Positions._ @@ -179,8 +180,11 @@ trait TypeAssigner { ErrorType } } - else if (d.symbol is TypeParamAccessor) // always dereference type param accessors - ensureAccessible(d.info.bounds.hi, superAccess, pos) + else if (d.symbol is TypeParamAccessor) + if (d.info.isAlias) + ensureAccessible(d.info.bounds.hi, superAccess, pos) + else // It's a named parameter, use the non-symbolic representation to pick up inherited versions as well + d.symbol.owner.thisType.select(d.symbol.name) else ctx.makePackageObjPrefixExplicit(tpe withDenot d) case _ => @@ -370,8 +374,24 @@ trait TypeAssigner { def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { val tparams = tycon.tpe.typeParams + def refineNamed(tycon: Type, arg: Tree) = arg match { + case NamedArg(name, argtpt) => + val tparam = tparams.find(_.name == name) match { + case Some(tparam) => tparam + case none => + val sym = tycon.member(name).symbol + if (sym.isAbstractType) sym + else if (sym.is(ParamAccessor)) sym.info.dealias.typeSymbol + else NoSymbol + } + if (tparam.exists) RefinedType(tycon, name, argtpt.tpe.toBounds(tparam)) + else errorType(s"$tycon does not have a parameter or abstract type member named $name", arg.pos) + case _ => + errorType(s"named and positional type arguments may not be mixed", arg.pos) + } val ownType = - if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) + if (args.head.isInstanceOf[NamedArg]) (tycon.tpe /: args)(refineNamed) + else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) else errorType(d"wrong number of type arguments for ${tycon.tpe}, should be ${tparams.length}", tree.pos) tree.withType(ownType) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index dd75e960e5ef..160cf808ca8b 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -865,26 +865,34 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit def typedAppliedTypeTree(tree: untpd.AppliedTypeTree)(implicit ctx: Context): Tree = track("typedAppliedTypeTree") { val tpt1 = typed(tree.tpt)(ctx retractMode Mode.Pattern) val tparams = tpt1.tpe.typeParams - var args = tree.args if (tparams.isEmpty) { ctx.error(d"${tpt1.tpe} does not take type parameters", tree.pos) tpt1 } else { - if (args.length != tparams.length) { - ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) - args = args.take(tparams.length) - } - def typedArg(arg: untpd.Tree, tparam: Symbol) = { - val (desugaredArg, argPt) = - if (ctx.mode is Mode.Pattern) - (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.info) - else - (arg, WildcardType) - val arg1 = typed(desugaredArg, argPt) - adaptTypeArg(arg1, tparam.info) - } - val args1 = args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] + var args = tree.args + val args1 = + if (args.head.isInstanceOf[untpd.NamedArg]) + for (arg @ NamedArg(id, argtpt) <- args) yield { + val argtpt1 = typedType(argtpt) + cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) + } + else { + if (args.length != tparams.length) { + ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) + args = args.take(tparams.length) + } + def typedArg(arg: untpd.Tree, tparam: Symbol) = { + val (desugaredArg, argPt) = + if (ctx.mode is Mode.Pattern) + (if (isVarPattern(arg)) desugar.patternVar(arg) else arg, tparam.info) + else + (arg, WildcardType) + val arg1 = typed(desugaredArg, argPt) + adaptTypeArg(arg1, tparam.info) + } + args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]] + } // check that arguments conform to bounds is done in phase PostTyper assignType(cpy.AppliedTypeTree(tree)(tpt1, args1), tpt1, args1) } diff --git a/tests/pickling/named-params.scala b/tests/pickling/named-params.scala new file mode 100644 index 000000000000..e9346bb10895 --- /dev/null +++ b/tests/pickling/named-params.scala @@ -0,0 +1,20 @@ +package namedparams + +abstract class C[type Elem, type Value](val elem: Elem) { + def toVal: Elem = ??? +} + + + +object Test { + val c = new C[String, String]("A") { + override def toVal = elem + } + val x: c.Elem = c.elem + + val c2: C { type Elem = String } = c + + val c3 = new C[Elem = String, Value = Int]("B") + val c4 = new C[Elem = String]("C") + val x2: c2.Elem = c2.elem +} diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala new file mode 100644 index 000000000000..51041078d3f4 --- /dev/null +++ b/tests/pos/named-params.scala @@ -0,0 +1,17 @@ +package namedparams + +abstract class C[type Elem, type Value](val elem: Elem) { + def toVal: Elem = ??? +} + + + +object Test { + val c = new C[String, String]("A") { + override def toVal = elem + } + val x: c.Elem = c.elem + + val c2: C { type Elem = String } = c + val x2: c2.Elem = c2.elem +} From f20f2a0d25eb4d063b62993afe0bb3fbd3257312 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Jan 2016 16:04:41 +0100 Subject: [PATCH 04/26] Make some operations surivive partial named parameter lists Types#underlyingClassRef and PostTyper#normalizeTree need to be changed so they can deal with partial named parameter lists. --- src/dotty/tools/dotc/core/Types.scala | 8 +++----- src/dotty/tools/dotc/transform/PostTyper.scala | 5 ++++- src/dotty/tools/dotc/typer/Checking.scala | 14 ++++++++++++-- 3 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 27828c5889ec..887b69712128 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -860,11 +860,9 @@ object Types { else NoType case tp: AnnotatedType => tp.underlying.underlyingClassRef(refinementOK) case tp: RefinedType => - if (refinementOK) tp.underlying.underlyingClassRef(refinementOK) - else { - val tycon = tp.withoutArgs(tp.argInfos) - if (tycon eq tp) NoType else tycon.underlyingClassRef(refinementOK) - } + def isParamName = tp.classSymbol.typeParams.exists(_.name == tp.refinedName) + if (refinementOK || isParamName) tp.underlying.underlyingClassRef(refinementOK) + else NoType case _ => NoType } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index 14edaa7b5c70..b38858b72a1c 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -68,7 +68,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran // TODO fill in } - /** Check bounds of AppliedTypeTrees. + /** Check bounds of AppliedTypeTrees and TypeApplys. * Replace type trees with TypeTree nodes. * Replace constant expressions with Literal nodes. * Note: Demanding idempotency instead of purity in literalize is strictly speaking too loose. @@ -99,6 +99,9 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran */ private def normalizeTree(tree: Tree)(implicit ctx: Context): Tree = tree match { case tree: TypeTree => tree + case TypeApply(fn, args) => + Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) + tree case _ => if (tree.isType) { Checking.typeChecker.traverse(tree) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 437902d0527e..0baf48fa3e18 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -49,12 +49,22 @@ object Checking { def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit = checkBounds(args, poly.paramBounds, _.substParams(poly, _)) - /** Check all AppliedTypeTree nodes in this tree for legal bounds */ + /** Check all AppliedTypeTree nodes in this tree for legal bounds @@@ */ val typeChecker = new TreeTraverser { def traverse(tree: Tree)(implicit ctx: Context) = { tree match { case AppliedTypeTree(tycon, args) => - val tparams = tycon.tpe.typeSymbol.typeParams + // If `args` is a list of named argiments, return corresponding type parameters, + // otherwise return type parameters unchanged + def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[TypeSymbol] = args match { + case Nil => Nil + case NamedArg(name, arg) :: args1 => + val (matchingParam :: Nil, others) = tparams.partition(_.name == name) + matchingParam :: matchNamed(others, args1) + case _ => + tparams + } + val tparams = matchNamed(tycon.tpe.typeSymbol.typeParams, args) val bounds = tparams.map(tparam => tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) checkBounds(args, bounds, _.substDealias(tparams, _)) From 79409ee5247bbe72a52338abf510aae66e40f5f5 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Jan 2016 16:08:04 +0100 Subject: [PATCH 05/26] Augment test case --- src/dotty/tools/dotc/core/TypeOps.scala | 14 ++++++++++---- tests/pos/named-params.scala | 7 +++++-- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 4251648a307b..420333695d22 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -444,10 +444,16 @@ trait TypeOps { this: Context => // TODO: Make standalone object. case tp: TypeRef => tp case tp @ RefinedType(tp1, name: TypeName) => - val prevInfo = refinements(name) - refinements = refinements.updated(name, - if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) - formals = formals.updated(name, tp1.typeParamNamed(name)) + tp.refinedInfo match { + case TypeAlias(TypeRef(pre, name1)) if (pre eq cls.thisType) && name1 == name => + // Don't record refinements of the form X = this.X (These can arise using named parameters). + typr.println(s"dropping refinement $tp") + case _ => + val prevInfo = refinements(name) + refinements = refinements.updated(name, + if (prevInfo == null) tp.refinedInfo else prevInfo & tp.refinedInfo) + formals = formals.updated(name, tp1.typeParamNamed(name)) + } normalizeToRef(tp1) case ErrorType => defn.AnyType diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala index 51041078d3f4..9f1410befa4f 100644 --- a/tests/pos/named-params.scala +++ b/tests/pos/named-params.scala @@ -1,10 +1,10 @@ package namedparams -abstract class C[type Elem, type Value](val elem: Elem) { +class C[type Elem, type Value](val elem: Elem) { def toVal: Elem = ??? } - +abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem) object Test { val c = new C[String, String]("A") { @@ -13,5 +13,8 @@ object Test { val x: c.Elem = c.elem val c2: C { type Elem = String } = c + + val c3 = new C[Elem = String, Value = Int]("B") + val c4 = new C[Elem = String]("C") val x2: c2.Elem = c2.elem } From 312c982867deb1e46768775cd7caefc4ee8b0164 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Jan 2016 17:39:16 +0100 Subject: [PATCH 06/26] Make named parameter alias handling more robust After unpickling we might see an alias X = pre.X where pre =:= the ThisType of the enclosing class. But it might not be `eq` to it. --- src/dotty/tools/dotc/core/TypeOps.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/core/TypeOps.scala b/src/dotty/tools/dotc/core/TypeOps.scala index 420333695d22..734d3185867f 100644 --- a/src/dotty/tools/dotc/core/TypeOps.scala +++ b/src/dotty/tools/dotc/core/TypeOps.scala @@ -445,7 +445,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object. tp case tp @ RefinedType(tp1, name: TypeName) => tp.refinedInfo match { - case TypeAlias(TypeRef(pre, name1)) if (pre eq cls.thisType) && name1 == name => + case TypeAlias(TypeRef(pre, name1)) if name1 == name && (pre =:= cls.thisType) => // Don't record refinements of the form X = this.X (These can arise using named parameters). typr.println(s"dropping refinement $tp") case _ => From 2475561fbc62bf8a8043eb9d7794432998e43ceb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Jan 2016 18:05:13 +0100 Subject: [PATCH 07/26] Avoid cyclic reference error when building dotty. --- src/dotty/tools/dotc/typer/TypeAssigner.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 6bb6b1429b02..aa51d70ffc48 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -4,7 +4,6 @@ package typer import core._ import ast._ -import ast.Trees._ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, Decorators._ import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ import util.Positions._ @@ -375,7 +374,7 @@ trait TypeAssigner { def assignType(tree: untpd.AppliedTypeTree, tycon: Tree, args: List[Tree])(implicit ctx: Context) = { val tparams = tycon.tpe.typeParams def refineNamed(tycon: Type, arg: Tree) = arg match { - case NamedArg(name, argtpt) => + case ast.Trees.NamedArg(name, argtpt) => val tparam = tparams.find(_.name == name) match { case Some(tparam) => tparam case none => From 577a0f193c5c7646171bf31968b365a24660c9cb Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 10 Jan 2016 18:07:38 +0100 Subject: [PATCH 08/26] Move failing test to pending Named parameters cause some elements to be unpickled in a different order as they are pickled. In particular term parameter aliases and type parameter aliases seem to be swapped. --- tests/{ => pending}/pickling/named-params.scala | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tests/{ => pending}/pickling/named-params.scala (100%) diff --git a/tests/pickling/named-params.scala b/tests/pending/pickling/named-params.scala similarity index 100% rename from tests/pickling/named-params.scala rename to tests/pending/pickling/named-params.scala From 08f7b3656e7ac95f1d447c29b82432398842cdb9 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 11 Jan 2016 11:05:40 +0100 Subject: [PATCH 09/26] Fix pending test for pickling There is a before/after difference having to do with the order in which class declarations show up. --- tests/pending/pickling/named-params.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/pending/pickling/named-params.scala b/tests/pending/pickling/named-params.scala index e9346bb10895..2697a7bb73ca 100644 --- a/tests/pending/pickling/named-params.scala +++ b/tests/pending/pickling/named-params.scala @@ -1,10 +1,10 @@ package namedparams -abstract class C[type Elem, type Value](val elem: Elem) { +class C[type Elem, type Value](val elem: Elem) { def toVal: Elem = ??? } - +class D[type Elem, V](elem: Elem) extends C[Elem, V](elem) object Test { val c = new C[String, String]("A") { From 8fa5a1fdd254fe30c91daf395e4820fc8febfa42 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2016 10:39:37 +0100 Subject: [PATCH 10/26] Fix PostTyper normalization for named args Needs to work also if named arg refers to an abstract type, not a parameter. --- src/dotty/tools/dotc/typer/Checking.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index 0baf48fa3e18..f18c5bf7cddf 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -54,13 +54,11 @@ object Checking { def traverse(tree: Tree)(implicit ctx: Context) = { tree match { case AppliedTypeTree(tycon, args) => - // If `args` is a list of named argiments, return corresponding type parameters, + // If `args` is a list of named arguments, return corresponding type parameters, // otherwise return type parameters unchanged - def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[TypeSymbol] = args match { - case Nil => Nil - case NamedArg(name, arg) :: args1 => - val (matchingParam :: Nil, others) = tparams.partition(_.name == name) - matchingParam :: matchNamed(others, args1) + def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[Symbol] = args match { + case (arg: NamedArg) :: _ => + for (NamedArg(name, arg) <- args) yield tycon.tpe.member(name).symbol case _ => tparams } From d3a5d37e6b4a4c9bddd52992d8af03f5a469bd8d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2016 10:40:31 +0100 Subject: [PATCH 11/26] Check named type params for welformedness rules. --- src/dotty/tools/dotc/typer/Namer.scala | 56 ++++++++++++++++++++++++-- test/dotc/tests.scala | 1 + tests/neg/named-params.scala | 22 ++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 tests/neg/named-params.scala diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala index ecbec7d07eb4..85c4e9208f17 100644 --- a/src/dotty/tools/dotc/typer/Namer.scala +++ b/src/dotty/tools/dotc/typer/Namer.scala @@ -576,8 +576,8 @@ class Namer { typer: Typer => /** The type signature of a ClassDef with given symbol */ override def completeInCreationContext(denot: SymDenotation): Unit = { - /** The type of a parent constructor. Types constructor arguments - * only if parent type contains uninstantiated type parameters. + /* The type of a parent constructor. Types constructor arguments + * only if parent type contains uninstantiated type parameters. */ def parentType(parent: untpd.Tree)(implicit ctx: Context): Type = if (parent.isType) { @@ -594,7 +594,12 @@ class Namer { typer: Typer => else typedAheadExpr(parent).tpe } - def checkedParentType(parent: untpd.Tree): Type = { + /* Check parent type tree `parent` for the following well-formedness conditions: + * (1) It must be a class type with a stable prefix (@see checkClassTypeWithStablePrefix) + * (2) If may not derive from itself + * (3) Overriding type parameters must be correctly forwarded. (@see checkTypeParamOverride) + */ + def checkedParentType(parent: untpd.Tree, paramAccessors: List[Symbol]): Type = { val ptype = parentType(parent)(ctx.superCallContext) if (cls.isRefinementClass) ptype else { @@ -608,10 +613,52 @@ class Namer { typer: Typer => ctx.error(i"cyclic inheritance: $cls extends itself$addendum", parent.pos) defn.ObjectType } + else if (!paramAccessors.forall(checkTypeParamOverride(pt, _))) + defn.ObjectType else pt } } + /* Check that every parameter with the same name as a visible named parameter in the parent + * class satisfies the following two conditions: + * (1) The overriding parameter is also named (i.e. not local/name mangled). + * (2) The overriding parameter is passed on directly to the parent parameter, or the + * parent parameter is not fully defined. + * @return true if conditions are satisfied, false otherwise. + */ + def checkTypeParamOverride(parent: Type, paramAccessor: Symbol): Boolean = { + var ok = true + val pname = paramAccessor.name + + def illegal(how: String): Unit = { + ctx.error(d"Illegal override of public type parameter $pname in $parent$how", paramAccessor.pos) + ok = false + } + + def checkAlias(tp: Type): Unit = tp match { + case tp: RefinedType => + if (tp.refinedName == pname) + tp.refinedInfo match { + case TypeAlias(alias) => + alias match { + case TypeRef(pre, name1) if name1 == pname && (pre =:= cls.thisType) => + // OK, parameter is passed on directly + case _ => + illegal(d".\nParameter is both redeclared and instantiated with $alias.") + } + case _ => // OK, argument is not fully defined + } + else checkAlias(tp.parent) + case _ => + } + if (parent.nonPrivateMember(paramAccessor.name).symbol.is(Param)) + if (paramAccessor is Private) + illegal("\nwith private parameter. Parameter definition needs to be prefixed with `type'.") + else + checkAlias(parent) + ok + } + val selfInfo = if (self.isEmpty) NoType else if (cls.is(Module)) { @@ -634,7 +681,8 @@ class Namer { typer: Typer => index(constr) symbolOfTree(constr).ensureCompleted() - val parentTypes = ensureFirstIsClass(parents map checkedParentType) + val tparamAccessors = decls.filter(_ is TypeParamAccessor).toList + val parentTypes = ensureFirstIsClass(parents.map(checkedParentType(_, tparamAccessors))) val parentRefs = ctx.normalizeToClassRefs(parentTypes, cls, decls) typr.println(s"completing $denot, parents = $parents, parentTypes = $parentTypes, parentRefs = $parentRefs") diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index 3610482364ea..a61646deda9b 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -121,6 +121,7 @@ class tests extends CompilerTest { @Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil, xerrors = 3) @Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3) @Test def neg_companions = compileFile(negDir, "companions", xerrors = 1) + @Test def namedParams = compileFile(negDir, "named-params", xerrors = 3) @Test def neg_over = compileFile(negDir, "over", xerrors = 3) @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14) @Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1) diff --git a/tests/neg/named-params.scala b/tests/neg/named-params.scala new file mode 100644 index 000000000000..c1f055e10216 --- /dev/null +++ b/tests/neg/named-params.scala @@ -0,0 +1,22 @@ +package namedparams + +class C[type Elem, type Value](val elem: Elem) { + def toVal: Elem = ??? +} + +abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem) +abstract class D2[Elem, V](elem: Elem) extends C[Elem, V](elem) // error +abstract class D3[type Elem, V](x: V) extends C[V, V](x) // error +abstract class D4[type Elem](elem: Elem) extends C[Elem, Elem] // error +object Test { + val c = new C[String, String]("A") { + override def toVal = elem + } + val x: c.Elem = c.elem + + val c2: C { type Elem = String } = c + + val c3 = new C[Elem = String, Value = Int]("B") + val c4 = new C[Elem = String]("C") + val x2: c2.Elem = c2.elem +} From 9d08a172b6325dab4cc1264f3e97253b1df97956 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2016 11:37:21 +0100 Subject: [PATCH 12/26] Annotate test with // error indications We had same fleyness in number of errors of cycle.scala which prompted this. --- tests/neg/cycles.scala | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/tests/neg/cycles.scala b/tests/neg/cycles.scala index ced6f56b529d..50bfa36f16bc 100644 --- a/tests/neg/cycles.scala +++ b/tests/neg/cycles.scala @@ -1,21 +1,22 @@ -class Foo[T <: U, U <: T] // error: cycle +class Foo[T <: U, U <: T] // error: illegal cyclic reference: upper bound U of type T refers back to the type itself + +class Bar[T >: T] // error: illegal cyclic reference: lower bound T of type T refers back to the type itself -class Bar[T >: T] // error: cycle class A { val x: T = ??? - type T <: x.type // error: cycle + type T <: x.type // error: cyclic reference involving value x } class B { - type T <: x.type // error: cycle - final val x: T = ??? + type T <: x.type // error: illegal cyclic reference: upper bound B.this.T(B.this.x) of type T refers back to the type itself + val x: T = ??? } class C { final val x: D#T = ??? class D { - type T <: x.type // error: cycle + type T <: x.type // error: cyclic reference involving value x val z: x.type = ??? } } From a6edb4f1fa35032dad174750df62a9b68842a34d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 12 Jan 2016 12:03:43 +0100 Subject: [PATCH 13/26] More named param tests Also, add an unrelated test file to pos. --- tests/pos/CollectionStrawMan3.scala | 408 ++++++++++++++++++++++++++++ tests/pos/named-params.scala | 12 + tests/pos/t2991.scala | 17 ++ 3 files changed, 437 insertions(+) create mode 100644 tests/pos/CollectionStrawMan3.scala create mode 100644 tests/pos/t2991.scala diff --git a/tests/pos/CollectionStrawMan3.scala b/tests/pos/CollectionStrawMan3.scala new file mode 100644 index 000000000000..47d3b52d65ab --- /dev/null +++ b/tests/pos/CollectionStrawMan3.scala @@ -0,0 +1,408 @@ +package strawman.collections + +import Predef.{augmentString => _, wrapString => _, _} +import scala.reflect.ClassTag + +/** A strawman architecture for new collections. It contains some + * example collection classes and methods with the intent to expose + * some key issues. It would be good to compare this to other + * implementations of the same functionality, to get an idea of the + * strengths and weaknesses of different collection architectures. + * + * For a test file, see tests/run/CollectionTests.scala. + * + * This one is like CollectionStrawMan1, but with the named parameter + * scheme for hk types. + */ +object CollectionStrawMan1 { + + /* ------------ Base Traits -------------------------------- */ + + /** Replaces TraversableOnce */ + trait CanIterate[type +Elem] { + def iterator: Iterator[Elem] + } + + /** Base trait for instances that can construct a collection from an iterator */ + trait FromIterator[+C <: Iterable] { + def fromIterator[B](it: Iterator[B]): C[Elem = B] + } + + /** Base trait for companion objects of collections */ + trait IterableFactory[+C <: Iterable] extends FromIterator[C] { + def empty[X]: C[Elem = X] = fromIterator(Iterator.empty) + def apply[A](xs: A*): C[Elem = A] = fromIterator(Iterator(xs: _*)) + } + + /** Base trait for generic collections */ + trait Iterable[type +Elem] extends CanIterate[Elem] with FromIterator[Iterable] + + /** Base trait for sequence collections */ + trait Seq[type +Elem] extends Iterable[Elem] with FromIterator[Seq] { + def apply(i: Int): Elem + def length: Int + } + + /* ------------ Operations ----------------------------------- */ + + /** Operations returning types unrelated to current collection */ + trait Ops[A] extends Any { + def iterator: Iterator[A] + def foreach(f: A => Unit): Unit = iterator.foreach(f) + def foldLeft[B](z: B)(op: (B, A) => B): B = iterator.foldLeft(z)(op) + def foldRight[B](z: B)(op: (A, B) => B): B = iterator.foldRight(z)(op) + def indexWhere(p: A => Boolean): Int = iterator.indexWhere(p) + def isEmpty: Boolean = !iterator.hasNext + def head: A = iterator.next + def view: View[A] = new View(iterator) + def to[C <: Iterable](fi: FromIterator[C]): C[Elem = A] = fi.fromIterator(iterator) + } + + /** Transforms returning same collection type */ + trait MonoTransforms[A, Repr] extends Any { + protected def iter: Iterator[A] + protected def fromIter(it: => Iterator[A]): Repr + def partition(p: A => Boolean): (Repr, Repr) = { + val (xs, ys) = iter.partition(p) + (fromIter(xs), fromIter(ys)) + } + def drop(n: Int): Repr = fromIter(iter.drop(n)) + } + + /** Transforms returning same collection type constructor */ + trait PolyTransforms[A, C <: CanIterate] extends Any { + protected def iter: Iterator[A] + protected def fromIter[B](it: => Iterator[B]): C[Elem = B] + def map[B](f: A => B): C[Elem = B] = fromIter(iter.map(f)) + def flatMap[B](f: A => CanIterate[B]): C[Elem = B] = fromIter(iter.flatMap(f(_))) + def ++[B >: A](xs: CanIterate[B]): C[Elem = B] = fromIter(iter ++ xs) + def zip[B](xs: CanIterate[B]): C[Elem = (A, B)] = fromIter(iter.zip(xs.iterator)) + } + + /** Transforms that only apply to Seq */ + trait MonoTransformsOfSeqs[A, Repr] extends Any with MonoTransforms[A, Repr] { + def reverse: Repr = fromIter(iter.reverse) + } + + /** Implementation of Ops for all generic collections */ + implicit class IterableOps[A](val c: Iterable[A]) + extends AnyVal with Ops[A] { + def iterator = c.iterator + } + + /** Implementation of MonoTransforms for all generic collections */ + implicit class IterableMonoTransforms[A, C <: Iterable](val c: Iterable[A] with FromIterator[C]) + extends AnyVal with MonoTransforms[A, C[Elem = A]] { + protected def iter = c.iterator + protected def fromIter(it: => Iterator[A]): C[Elem = A] = c.fromIterator(it) + } + + /** Implementation of PolyTransforms for all generic collections */ + implicit class IterablePolyTransforms[A, C <: Iterable](val c: Iterable[A] with FromIterator[C]) + extends AnyVal with PolyTransforms[A, C] { + protected def iter = c.iterator + protected def fromIter[B](it: => Iterator[B]) = c.fromIterator(it) + } + + /** Implementation of MonoTransformsForSeqs for all generic collections */ + implicit class SeqMonoTransforms[A, C <: Seq](val c: Seq[A] with FromIterator[C]) + extends AnyVal with MonoTransformsOfSeqs[A, C[Elem = A]] { + protected def iter = c.iterator + protected def fromIter(it: => Iterator[A]) = c.fromIterator(it) + } + + /* --------- Concrete collection types ------------------------------- */ + + /** Concrete collection type: List */ + sealed trait List[+A] extends Seq[A] with FromIterator[List] { + def isEmpty: Boolean + def head: A + def tail: List[A] + def iterator = new ListIterator[A](this) + def fromIterator[B](it: Iterator[B]): List[B] = List.fromIterator(it) + def apply(i: Int): A = { + require(!isEmpty) + if (i == 0) head else tail.apply(i - 1) + } + def length: Int = + if (isEmpty) 0 else 1 + tail.length + } + + case class Cons[+A](x: A, xs: List[A]) extends List[A] { + def isEmpty = false + def head = x + def tail = xs + } + + case object Nil extends List[Nothing] { + def isEmpty = true + def head = ??? + def tail = ??? + } + + object List extends IterableFactory[List] { + def fromIterator[B](it: Iterator[B]): List[B] = it match { + case it: ListIterator[B] => it.toList + case _ => if (it.hasNext) Cons(it.next, fromIterator(it)) else Nil + } + } + + class ListIterator[+A](xs: List[A]) extends Iterator[A] { + private[this] var current = xs + def hasNext = !current.isEmpty + def next = { val r = current.head; current = current.tail; r } + def toList = current + } + + /** Concrete collection type: ArrayBuffer */ + class ArrayBuffer[A] private (initElems: Array[AnyRef], initLength: Int) extends Seq[A] with FromIterator[ArrayBuffer] { + def this() = this(new Array[AnyRef](16), 0) + private var elems: Array[AnyRef] = initElems + private var start = 0 + private var limit = initLength + def apply(i: Int) = elems(start + i).asInstanceOf[A] + def length = limit - start + def iterator = new ArrayBufferIterator[A](elems, start, length) + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = + ArrayBuffer.fromIterator(it) + def +=(elem: A): this.type = { + if (limit == elems.length) { + if (start > 0) { + Array.copy(elems, start, elems, 0, length) + limit -= start + start = 0 + } + else { + val newelems = new Array[AnyRef](limit * 2) + Array.copy(elems, 0, newelems, 0, limit) + elems = newelems + } + } + elems(limit) = elem.asInstanceOf[AnyRef] + limit += 1 + this + } + def trimStart(n: Int): Unit = start += (n max 0) + override def toString = s"ArrayBuffer(${elems.slice(start, limit).mkString(", ")})" + } + + object ArrayBuffer extends IterableFactory[ArrayBuffer] { + def fromIterator[B](it: Iterator[B]): ArrayBuffer[B] = it match { + case Iterator.Concat(fst: ArrayBufferIterator[_], snd: ArrayBufferIterator[_]) => + val elems = new Array[AnyRef](fst.remaining + snd.remaining) + Array.copy(fst.elems, fst.start, elems, 0, fst.remaining) + Array.copy(snd.elems, snd.start, elems, fst.remaining, snd.remaining) + new ArrayBuffer(elems, elems.length) + case it @ Iterator.Partition(underlying, _, buf, _) => + while (underlying.hasNext) it.distribute() + buf.asInstanceOf[ArrayBuffer[B]] + case it if it.remaining >= 0 => + val elems = new Array[AnyRef](it.remaining) + for (i <- 0 until elems.length) elems(i) = it.next.asInstanceOf[AnyRef] + new ArrayBuffer[B](elems, elems.length) + case _ => + val buf = new ArrayBuffer[B] + while (it.hasNext) buf += it.next + buf + } + } + + class ArrayBufferIterator[A](val elems: Array[AnyRef], initStart: Int, length: Int) extends RandomAccessIterator[A] { + val limit = length + def apply(n: Int) = elems(initStart + n).asInstanceOf[A] + } + + /** Concrete collection type: View */ + class View[+A](it: => Iterator[A]) extends CanIterate[A] { + def iterator = it + } + + implicit class ViewOps[A](val v: View[A]) extends AnyVal with Ops[A] { + def iterator = v.iterator + def cache = to(ArrayBuffer).view + } + + implicit class ViewMonoTransforms[A](val v: View[A]) + extends AnyVal with MonoTransforms[A, View[A]] { + protected def iter = v.iterator + protected def fromIter(it: => Iterator[A]): View[A] = new View(it) + } + + implicit class ViewPolyTransforms[A](val v: View[A]) + extends AnyVal with PolyTransforms[A, View] { + protected def iter = v.iterator + protected def fromIter[B](it: => Iterator[B]) = new View(it) + } + + /** Concrete collection type: String */ + implicit class StringOps(val s: String) extends AnyVal with Ops[Char] { + def iterator: Iterator[Char] = new RandomAccessIterator[Char] { + override val limit = s.length + def apply(n: Int) = s.charAt(n) + } + } + + implicit class StringMonoTransforms(val s: String) + extends AnyVal with MonoTransformsOfSeqs[Char, String] { + protected def iter = StringOps(s).iterator + protected def fromIter(it: => Iterator[Char]) = { + val sb = new StringBuilder + for (ch <- it) sb.append(ch) + sb.toString + } + } + + implicit class StringPolyTransforms(val s: String) + extends AnyVal with PolyTransforms[Char, Seq] { + protected def iter = StringOps(s).iterator + protected def fromIter[B](it: => Iterator[B]) = List.fromIterator(it) + def map(f: Char => Char): String = { + val sb = new StringBuilder + for (ch <- s) sb.append(f(ch)) + sb.toString + } + def flatMap(f: Char => String) = { + val sb = new StringBuilder + for (ch <- s) sb.append(f(ch)) + sb.toString + } + def ++(xs: CanIterate[Char]): String = { + val sb = new StringBuilder(s) + for (ch <- xs.iterator) sb.append(ch) + sb.toString + } + def ++(xs: String): String = s + xs + } + +/* ---------- Iterators --------------------------------------------------- */ + + /** A core Iterator class */ + trait Iterator[+A] extends CanIterate[A] { self => + def hasNext: Boolean + def next: A + def iterator = this + def foldLeft[B](z: B)(op: (B, A) => B): B = + if (hasNext) foldLeft(op(z, next))(op) else z + def foldRight[B](z: B)(op: (A, B) => B): B = + if (hasNext) op(next, foldRight(z)(op)) else z + def foreach(f: A => Unit): Unit = + while (hasNext) f(next) + def indexWhere(p: A => Boolean): Int = { + var i = 0 + while (hasNext) { + if (p(next)) return i + i += 1 + } + -1 + } + def map[B](f: A => B): Iterator[B] = Iterator.Map(this, f) + def flatMap[B](f: A => CanIterate[B]): Iterator[B] = Iterator.FlatMap(this, f) + def ++[B >: A](xs: CanIterate[B]): Iterator[B] = Iterator.Concat(this, xs.iterator) + def partition(p: A => Boolean): (Iterator[A], Iterator[A]) = { + val lookaheadTrue, lookaheadFalse = new ArrayBuffer[A] + (Iterator.Partition(this, p, lookaheadTrue, lookaheadFalse), + Iterator.Partition[A](this, !p(_), lookaheadFalse, lookaheadTrue)) + } + def drop(n: Int): Iterator[A] = Iterator.Drop(this, n) + def zip[B](that: CanIterate[B]): Iterator[(A, B)] = Iterator.Zip(this, that.iterator) + def reverse: Iterator[A] = { + var elems: List[A] = Nil + while (hasNext) elems = Cons(next, elems) + elems.iterator + } + + /** If this iterator results from applying a transfomation to another iterator, + * that other iterator, otherwise the iterator itself. + */ + def underlying: Iterator[_] = this + + /** If the number of elements still to be returned by this iterator is known, + * that number, otherwise -1. + */ + def remaining = -1 + } + + object Iterator { + val empty: Iterator[Nothing] = new Iterator[Nothing] { + def hasNext = false + def next = ??? + override def remaining = 0 + } + def apply[A](xs: A*): Iterator[A] = new RandomAccessIterator[A] { + override val limit = xs.length + def apply(n: Int) = xs(n) + } + def nextOnEmpty = throw new NoSuchElementException("next on empty iterator") + + case class Map[A, B](override val underlying: Iterator[A], f: A => B) extends Iterator[B] { + def hasNext = underlying.hasNext + def next = f(underlying.next) + override def remaining = underlying.remaining + } + case class FlatMap[A, B](override val underlying: Iterator[A], f: A => CanIterate[B]) extends Iterator[B] { + private var myCurrent: Iterator[B] = Iterator.empty + private def current = { + while (!myCurrent.hasNext && underlying.hasNext) + myCurrent = f(underlying.next).iterator + myCurrent + } + def hasNext = current.hasNext + def next = current.next + } + case class Concat[A](override val underlying: Iterator[A], other: Iterator[A]) extends Iterator[A] { + private var myCurrent = underlying + private def current = { + if (!myCurrent.hasNext && myCurrent.eq(underlying)) myCurrent = other + myCurrent + } + def hasNext = current.hasNext + def next = current.next + override def remaining = + if (underlying.remaining >= 0 && other.remaining >= 0) + underlying.remaining + other.remaining + else -1 + } + case class Partition[A](override val underlying: Iterator[A], p: A => Boolean, lookahead: ArrayBuffer[A], dual: ArrayBuffer[A]) extends Iterator[A] { + def distribute() = { + val elem = underlying.next + (if (p(elem)) lookahead else dual) += elem + } + final def hasNext: Boolean = + !lookahead.isEmpty || underlying.hasNext && { distribute(); hasNext } + final def next = + if (hasNext) { + val r = lookahead.head + lookahead.trimStart(1) + r + } else Iterator.nextOnEmpty + } + case class Drop[A](override val underlying: Iterator[A], n: Int) extends Iterator[A] { + var toSkip = n + def hasNext: Boolean = underlying.hasNext && ( + toSkip == 0 || { underlying.next; toSkip -= 1; hasNext }) + def next = if (hasNext) underlying.next else nextOnEmpty + override def remaining = (underlying.remaining - toSkip) max -1 + } + case class Zip[A, B](override val underlying: Iterator[A], other: Iterator[B]) extends Iterator[(A, B)] { + def hasNext = underlying.hasNext && other.hasNext + def next = (underlying.next, other.next) + override def remaining = underlying.remaining min other.remaining + } + case class Reverse[A](override val underlying: RandomAccessIterator[A]) extends RandomAccessIterator[A] { + def apply(n: Int) = underlying.apply(underlying.limit - 1 - n) + def limit = underlying.remaining + } + } + + trait RandomAccessIterator[+A] extends Iterator[A] { self => + def apply(n: Int): A + def limit: Int + var start = 0 + override def remaining = (limit - start) max 0 + def hasNext = start < limit + def next: A = { val r = this(start); start += 1; r } + override def drop(n: Int): Iterator[A] = { start += (n max 0); this } + override def reverse: Iterator[A] = new Iterator.Reverse(this) + } +} + diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala index 9f1410befa4f..9668ec34ce8d 100644 --- a/tests/pos/named-params.scala +++ b/tests/pos/named-params.scala @@ -18,3 +18,15 @@ object Test { val c4 = new C[Elem = String]("C") val x2: c2.Elem = c2.elem } + +// Adapated from i94-nada +trait Test1 { + trait Monad[type Elem] { + def unit: Elem + } + sealed abstract class Either[A,B] + case class Left[A,B](unit: A) extends Either[A,B] with Monad[A] + case class Right[A,B](unit: B) extends Either[A,B] with Monad[B] + def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit) + println(flatMap(Left(1), {x: Int => Left(x)})) +} diff --git a/tests/pos/t2991.scala b/tests/pos/t2991.scala new file mode 100644 index 000000000000..f11b082c6f64 --- /dev/null +++ b/tests/pos/t2991.scala @@ -0,0 +1,17 @@ +class X { + def f(x: AnyRef) = x.toString + def f(x: AnyRef, y: AnyRef*) = y.mkString(x.toString) +} + +class Y { + def f(x: Int) = x.toString + def f(x: Int, y: Int*) = y.mkString(x.toString) +} + +object Test { + val x: AnyRef = "a" + val res0 = new X + res0.f(x) + val res1 = new Y + res1.f(5) +} From 511a4e8e13a54cbed580308621267afdc2ed309d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 17 Jan 2016 12:24:26 +0100 Subject: [PATCH 14/26] Address reviewer comments --- src/dotty/tools/dotc/typer/Checking.scala | 4 ++-- src/dotty/tools/dotc/typer/TypeAssigner.scala | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index f18c5bf7cddf..fec5b86f85ca 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -57,8 +57,8 @@ object Checking { // If `args` is a list of named arguments, return corresponding type parameters, // otherwise return type parameters unchanged def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[Symbol] = args match { - case (arg: NamedArg) :: _ => - for (NamedArg(name, arg) <- args) yield tycon.tpe.member(name).symbol + case (_: NamedArg) :: _ => + for (NamedArg(name, _) <- args) yield tycon.tpe.member(name).symbol case _ => tparams } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index aa51d70ffc48..2d9e5505898a 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -375,6 +375,7 @@ trait TypeAssigner { val tparams = tycon.tpe.typeParams def refineNamed(tycon: Type, arg: Tree) = arg match { case ast.Trees.NamedArg(name, argtpt) => + // Dotty deviation: importing ast.Trees._ and matching on NamedArg gives a cyclic ref error val tparam = tparams.find(_.name == name) match { case Some(tparam) => tparam case none => From 19b702dbcb72dc5df7c2ead00111310b2ff7759c Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 11:51:35 +0100 Subject: [PATCH 15/26] Add doc comment --- src/dotty/tools/dotc/core/Flags.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/src/dotty/tools/dotc/core/Flags.scala b/src/dotty/tools/dotc/core/Flags.scala index c60925728a26..0bf0d2506717 100644 --- a/src/dotty/tools/dotc/core/Flags.scala +++ b/src/dotty/tools/dotc/core/Flags.scala @@ -594,6 +594,7 @@ object Flags { /** A private parameter accessor */ final val PrivateParamAccessor = allOf(Private, ParamAccessor) + /** A type parameter introduced with [type ... ] */ final val NamedTypeParam = allOf(TypeParam, ParamAccessor) /** A local parameter */ From 75d1237023062f4d232600f5c623b0ee719e964e Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 11:51:56 +0100 Subject: [PATCH 16/26] Make type parameter reordering generally available. --- src/dotty/tools/dotc/core/SymDenotations.scala | 17 +++++++++++++++++ .../core/unpickleScala2/Scala2Unpickler.scala | 14 +++----------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index 4495b40961af..c8109dd44c72 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1481,6 +1481,23 @@ object SymDenotations { if (myMemberCache != null) myMemberCache invalidate sym.name } + /** Make sure the type parameters of this class are `tparams`, reorder definitions + * in scope if necessary. + * @pre All type parameters in `tparams` are entered in class scope `info.decls`. + */ + def updateTypeParams(tparams: List[Symbol])(implicit ctx: Context): Unit = + if (!typeParams.corresponds(tparams)(_.name == _.name)) { + val decls = info.decls + val decls1 = newScope + for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name)) + for (sym <- decls) if (!typeParams.contains(sym)) decls1.enter(sym) + val ci = classInfo + // dotty deviation; overloading resolution on next line fails if prefix `ci` is not a value. + // See test pending/pos/overloaddefault.scala + info = ci.derivedClassInfo(decls = decls1) + myTypeParams = null + } + /** All members of this class that have the given name. * The elements of the returned pre-denotation all * have existing symbols. diff --git a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala index b1b229768fe8..7a13388aebcf 100644 --- a/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala +++ b/src/dotty/tools/dotc/core/unpickleScala2/Scala2Unpickler.scala @@ -132,18 +132,10 @@ object Scala2Unpickler { } else { registerCompanionPair(scalacCompanion, denot.classSymbol) } - val declsTypeParams = denot.typeParams - val declsInRightOrder = - if (declsTypeParams.corresponds(tparams)(_.name == _.name)) decls - else { // create new scope with type parameters in right order - val decls1 = newScope - for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name)) - for (sym <- decls) if (!declsTypeParams.contains(sym)) decls1.enter(sym) - decls1 - } - denot.info = ClassInfo( // final info - denot.owner.thisType, denot.classSymbol, parentRefs, declsInRightOrder, ost) + denot.info = ClassInfo( // final info, except possibly for typeparams ordering + denot.owner.thisType, denot.classSymbol, parentRefs, decls, ost) + denot.updateTypeParams(tparams) } } From eead54537fa536b5710e3ed3879251848f6d47f7 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 11:52:37 +0100 Subject: [PATCH 17/26] Add test to illustrate overloading problem. --- tests/pending/pos/overloaddefault.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 tests/pending/pos/overloaddefault.scala diff --git a/tests/pending/pos/overloaddefault.scala b/tests/pending/pos/overloaddefault.scala new file mode 100644 index 000000000000..ed539719df44 --- /dev/null +++ b/tests/pending/pos/overloaddefault.scala @@ -0,0 +1,15 @@ +trait Scope +class MScope extends Scope + +case class CI(pre: Int, decls: Scope) { + def derivedCI(pre: Int) = new CI(pre, decls) + def derivedCI(pre: Int = this.pre, decls: Scope = this.decls) = new CI(pre, decls) +} + +object Test { + def ci = new CI(1, new MScope) + val decls1 = new MScope + ci.derivedCI(2, decls = decls1) + ci.derivedCI(pre = 2) + ci.derivedCI(decls = decls1) +} From a6b7326b0f5e80eb052f5f0de7615f42c3bf183d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 17:46:17 +0100 Subject: [PATCH 18/26] Fix assertion error message --- src/dotty/tools/dotc/ast/Desugar.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dotty/tools/dotc/ast/Desugar.scala b/src/dotty/tools/dotc/ast/Desugar.scala index 87694843ad6a..a7b5546d5a4a 100644 --- a/src/dotty/tools/dotc/ast/Desugar.scala +++ b/src/dotty/tools/dotc/ast/Desugar.scala @@ -67,7 +67,7 @@ object desugar { val defctx = ctx.outersIterator.dropWhile(_.scope eq ctx.scope).next var local = defctx.denotNamed(tp.name).suchThat(_ is ParamOrAccessor).symbol if (local.exists) (defctx.owner.thisType select local).dealias - else throw new Error(s"no matching symbol for ${sym.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}") + else throw new Error(s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope}") case _ => mapOver(tp) } From 5fad3df0f3fe1fada2ef8c2e9ae4f363acd58aea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 17:49:22 +0100 Subject: [PATCH 19/26] Fix review comment in previous PR Was not fixed by accident then, so we do it here now. --- src/dotty/tools/dotc/core/TypeApplications.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/dotty/tools/dotc/core/TypeApplications.scala b/src/dotty/tools/dotc/core/TypeApplications.scala index 4726a16bbd57..8f8a7dbdda6a 100644 --- a/src/dotty/tools/dotc/core/TypeApplications.scala +++ b/src/dotty/tools/dotc/core/TypeApplications.scala @@ -244,8 +244,7 @@ class TypeApplications(val self: Type) extends AnyVal { else tsym.infoOrCompleter match { case completer: TypeParamsCompleter => val tparams = completer.completerTypeParams(tsym) - if (tsym.isClass) tparams - else defn.LambdaTrait(tparams.map(_.variance)).typeParams + defn.LambdaTrait(tparams.map(_.variance)).typeParams case _ => if (!tsym.isCompleting || tsym.isAliasType) tsym.info.typeParams else From ae38b6dd689f3b72f9a09660cdd7e6831d9cb2c0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Mon, 18 Jan 2016 18:40:11 +0100 Subject: [PATCH 20/26] Fix problem of overloading resolution when receiver is not stable. --- src/dotty/tools/dotc/core/SymDenotations.scala | 5 +---- src/dotty/tools/dotc/transform/Constructors.scala | 2 +- src/dotty/tools/dotc/transform/RestoreScopes.scala | 3 +-- src/dotty/tools/dotc/typer/Applications.scala | 9 +++++++-- tests/{pending => }/pos/overloaddefault.scala | 0 5 files changed, 10 insertions(+), 9 deletions(-) rename tests/{pending => }/pos/overloaddefault.scala (100%) diff --git a/src/dotty/tools/dotc/core/SymDenotations.scala b/src/dotty/tools/dotc/core/SymDenotations.scala index c8109dd44c72..4bc7f2aa373f 100644 --- a/src/dotty/tools/dotc/core/SymDenotations.scala +++ b/src/dotty/tools/dotc/core/SymDenotations.scala @@ -1491,10 +1491,7 @@ object SymDenotations { val decls1 = newScope for (tparam <- tparams) decls1.enter(decls.lookup(tparam.name)) for (sym <- decls) if (!typeParams.contains(sym)) decls1.enter(sym) - val ci = classInfo - // dotty deviation; overloading resolution on next line fails if prefix `ci` is not a value. - // See test pending/pos/overloaddefault.scala - info = ci.derivedClassInfo(decls = decls1) + info = classInfo.derivedClassInfo(decls = decls1) myTypeParams = null } diff --git a/src/dotty/tools/dotc/transform/Constructors.scala b/src/dotty/tools/dotc/transform/Constructors.scala index 265ad32174d4..b6ebd7d90393 100644 --- a/src/dotty/tools/dotc/transform/Constructors.scala +++ b/src/dotty/tools/dotc/transform/Constructors.scala @@ -246,7 +246,7 @@ class Constructors extends MiniPhaseTransform with SymTransformer { thisTransfor // Drop accessors that are not retained from class scope if (dropped.nonEmpty) { - val clsInfo = cls.classInfo // TODO investigate: expand clsInfo to cls.info => dotty type error + val clsInfo = cls.classInfo cls.copy( info = clsInfo.derivedClassInfo( decls = clsInfo.decls.filteredScope(!dropped.contains(_)))) diff --git a/src/dotty/tools/dotc/transform/RestoreScopes.scala b/src/dotty/tools/dotc/transform/RestoreScopes.scala index fe24186acc90..5d755dd9344b 100644 --- a/src/dotty/tools/dotc/transform/RestoreScopes.scala +++ b/src/dotty/tools/dotc/transform/RestoreScopes.scala @@ -48,9 +48,8 @@ class RestoreScopes extends MiniPhaseTransform with IdentityDenotTransformer { t } pkg.enter(cls) - val cinfo = cls.classInfo tree.symbol.copySymDenotation( - info = cinfo.derivedClassInfo( // Dotty deviation: Cannot expand cinfo inline without a type error + info = cls.classInfo.derivedClassInfo( decls = restoredDecls: Scope)).installAfter(thisTransform) tree } diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 649b8088fe8d..86980f3379ba 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -262,8 +262,13 @@ trait Applications extends Compatibility { self: Typer => val receiver: Tree = methPart(normalizedFun) match { case Select(receiver, _) => receiver case mr => mr.tpe.normalizedPrefix match { - case mr: TermRef => ref(mr) - case _ => EmptyTree + case mr: TermRef => + ref(mr) + case mr: TypeRef if this.isInstanceOf[TestApplication[_]] => + // In this case it is safe to skolemize now; we will produce a stable prefix for the actual call. + ref(mr.narrow) + case _ => + EmptyTree } } val getterPrefix = diff --git a/tests/pending/pos/overloaddefault.scala b/tests/pos/overloaddefault.scala similarity index 100% rename from tests/pending/pos/overloaddefault.scala rename to tests/pos/overloaddefault.scala From b9ff5b70f2badf68fd23ee8e2ce943927ba5f0ea Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 19 Jan 2016 12:41:53 +0100 Subject: [PATCH 21/26] Take defult parameters into account for overloading resolution. The current Scala spec only considers methods without default parameters for overloading resolution (unless only a single one remains anyway after filtering by shape). This is needlessly restrictive. But dropping this restriction (as dotty does) can lead to ambiguity errors, which is why run/t8197 did not compile anymore. We fix the problem by a last try rule: If after asSpecific tests there are still several alternatives, and only one of them is without default arguments, pick that one. I tried an alternative rule which would make the distinction on default params earlier but that one fails for the overloaded tree copier functions in Trees.scala (the method with default parameters is also the one which is more specific). --- src/dotty/tools/dotc/typer/Applications.scala | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 86980f3379ba..217b75f618bd 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -262,13 +262,13 @@ trait Applications extends Compatibility { self: Typer => val receiver: Tree = methPart(normalizedFun) match { case Select(receiver, _) => receiver case mr => mr.tpe.normalizedPrefix match { - case mr: TermRef => - ref(mr) - case mr: TypeRef if this.isInstanceOf[TestApplication[_]] => - // In this case it is safe to skolemize now; we will produce a stable prefix for the actual call. - ref(mr.narrow) - case _ => - EmptyTree + case mr: TermRef => ref(mr) + case mr => + if (this.isInstanceOf[TestApplication[_]]) + // In this case it is safe to skolemize now; we will produce a stable prefix for the actual call. + ref(mr.narrow) + else + EmptyTree } } val getterPrefix = @@ -1105,10 +1105,13 @@ trait Applications extends Compatibility { self: Typer => // the arguments (which are constants) to be adapted to Byte. If we had picked // `candidates` instead, no solution would have been found. case alts => -// overload.println(i"ambiguous $alts%, %") - val deepPt = pt.deepenProto - if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) - else alts + val noDefaults = alts.filter(!_.symbol.hasDefaultParams) + if (noDefaults.length == 1) noDefaults // return unique alternative without default parameters if it exists + else { + val deepPt = pt.deepenProto + if (deepPt ne pt) resolveOverloaded(alts, deepPt, targs) + else alts + } } } From 2334164018a00064e3f15d9a4a1166c44a9439e8 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Wed, 20 Jan 2016 19:09:08 +0100 Subject: [PATCH 22/26] Add some flexibility in comparing named and unnamed parameterized types. --- src/dotty/tools/dotc/core/TypeComparer.scala | 41 +++++++++++++- tests/pos/hk-named.scala | 58 ++++++++++++++++++++ 2 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 tests/pos/hk-named.scala diff --git a/src/dotty/tools/dotc/core/TypeComparer.scala b/src/dotty/tools/dotc/core/TypeComparer.scala index 6ec60546684a..4e7a4a75db70 100644 --- a/src/dotty/tools/dotc/core/TypeComparer.scala +++ b/src/dotty/tools/dotc/core/TypeComparer.scala @@ -371,7 +371,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { case _ => compareRefinedSlow || fourthTry(tp1, tp2) || - compareHkLambda(tp2, tp1, inOrder = false) + compareHkLambda(tp2, tp1, inOrder = false) || + compareAliasedRefined(tp2, tp1, inOrder = false) } else // fast path, in particular for refinements resulting from parameterization. isSubType(tp1, skipped2) && @@ -491,7 +492,9 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { } isNewSubType(tp1.underlying.widenExpr, tp2) || comparePaths case tp1: RefinedType => - isNewSubType(tp1.parent, tp2) || compareHkLambda(tp1, tp2, inOrder = true) + isNewSubType(tp1.parent, tp2) || + compareHkLambda(tp1, tp2, inOrder = true) || + compareAliasedRefined(tp1, tp2, inOrder = true) case AndType(tp11, tp12) => // Rewrite (T111 | T112) & T12 <: T2 to (T111 & T12) <: T2 and (T112 | T12) <: T2 // and analogously for T11 & (T121 | T122) & T12 <: T2 @@ -614,6 +617,35 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { false } + /** Say we are comparing a refined type `P{type M = U}` or `P{type M >: L <: U}`. + * If P#M refers to a BaseTypeArg aliased to some other typeref P#N, + * do the same comparison with `P{type N = U}` or `P{type N >: L <: U}`, respectively. + * This allows to handle situations involving named type params like this one: + * + * trait Lambda[type Elem] + * trait Lst[T] extends Lambda[T] + * + * compareAliasedRefined is necessary so we establish that + * + * Lst[Int] = Lst[Elem = Int] + */ + private def compareAliasedRefined(rt: RefinedType, other: Type, inOrder: Boolean) = { + val mbr = refinedSymbol(rt) + mbr.is(BaseTypeArg) && { + mbr.info match { + case TypeAlias(TypeRef(_, aliasName)) => + val rt1 = rt.derivedRefinedType(rt.parent, aliasName, rt.refinedInfo) + subtyping.println(i"rewiring $rt to $rt1 in comparison with $other") + if (inOrder) isSubType(rt1, other) else isSubType(other, rt1) + case _ => + false + } + } + } + + /** The symbol referred to in the refinement of `rt` */ + private def refinedSymbol(rt: RefinedType) = rt.parent.member(rt.refinedName).symbol + /** Returns true iff either `tp11 <:< tp21` or `tp12 <:< tp22`, trying at the same time * to keep the constraint as wide as possible. Specifically, if * @@ -742,11 +774,14 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling { /** A type has been covered previously in subtype checking if it * is some combination of TypeRefs that point to classes, where the * combiners are RefinedTypes, AndTypes or AnnotatedTypes. + * One exception: Refinements referring to basetype args are never considered + * to be already covered. This is necessary because such refined types might + * still need to be compared with a compareAliasRefined. */ private def isCovered(tp: Type): Boolean = tp.dealias.stripTypeVar match { case tp: TypeRef => tp.symbol.isClass && tp.symbol != NothingClass && tp.symbol != NullClass case tp: ProtoType => false - case tp: RefinedType => isCovered(tp.parent) + case tp: RefinedType => isCovered(tp.parent) && !refinedSymbol(tp).is(BaseTypeArg) case tp: AnnotatedType => isCovered(tp.underlying) case AndType(tp1, tp2) => isCovered(tp1) && isCovered(tp2) case _ => false diff --git a/tests/pos/hk-named.scala b/tests/pos/hk-named.scala new file mode 100644 index 000000000000..5f2cb6c742dd --- /dev/null +++ b/tests/pos/hk-named.scala @@ -0,0 +1,58 @@ +import language.higherKinds + +object hk0 { + + trait Lambda[type Elem] + + abstract class Functor[F <: Lambda] { + def map[A, B](f: A => B): F[Elem = A] => F[Elem = B] + } + + object test1 { + class ListT[T] extends Lambda[T] + + val ml: Functor[ListT] = ??? + val mx = ml + var xs: ListT[Int] = ??? + var ys: ListT { type Elem = Int } = xs + xs = ys + val mm: (Int => Boolean) => ListT[Int] => ListT[Boolean] = mx.map[Int, Boolean] + val mm2: (Int => Boolean) => ListT[Int] => ListT[Boolean] = mx.map + } +} + + +object higherKinded { + + type Untyped = Null + + class Tree[type -Attr >: Untyped] { + type ThisType <: Tree + def withString(s: String): ThisType[Attr = String] = withString(s) + } +/* + class Ident[-Attr >: Untyped] extends Tree[Attr] { + type ThisType = Ident + } + + val id = new Ident[Integer] + + val y = id.withString("abc") + + val z: Ident[String] = y + + val zz: tpd.Tree = y + + abstract class Instance[T >: Untyped] {g + type Tree = higherKinded.Tree[T] + } + + object tpd extends Instance[String] + + def transform(tree: Tree[String]) = { + val tree1 = tree.withString("") + tree1: Tree[String] + } +*/ +} + From 91c846b7558e07746ede8bf20ea1d0ca464d9b1d Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sun, 24 Jan 2016 20:48:21 +0100 Subject: [PATCH 23/26] Use hasNamedArgs instead of repeating test inline. --- src/dotty/tools/dotc/ast/TreeInfo.scala | 4 ++++ src/dotty/tools/dotc/typer/Applications.scala | 8 ++------ src/dotty/tools/dotc/typer/TypeAssigner.scala | 2 +- src/dotty/tools/dotc/typer/Typer.scala | 3 ++- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/dotty/tools/dotc/ast/TreeInfo.scala b/src/dotty/tools/dotc/ast/TreeInfo.scala index 2b0f0aee3e54..c1efd0b0b020 100644 --- a/src/dotty/tools/dotc/ast/TreeInfo.scala +++ b/src/dotty/tools/dotc/ast/TreeInfo.scala @@ -215,6 +215,10 @@ trait TreeInfo[T >: Untyped <: Type] { self: Trees.Instance[T] => case _ => false } + /** Does this list contain a named argument tree? */ + def hasNamedArg(args: List[Any]) = args exists isNamedArg + val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] + /** Is this pattern node a catch-all (wildcard or variable) pattern? */ def isDefaultCase(cdef: CaseDef) = cdef match { case CaseDef(pat, EmptyTree, _) => isWildcardArg(pat) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 217b75f618bd..c7acd3c1fa03 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -29,8 +29,6 @@ import language.implicitConversions object Applications { import tpd._ - private val isNamedArg = (arg: Any) => arg.isInstanceOf[Trees.NamedArg[_]] - def hasNamedArg(args: List[Any]) = args exists isNamedArg def extractorMemberType(tp: Type, name: Name, errorPos: Position = NoPosition)(implicit ctx:Context) = { val ref = tp.member(name).suchThat(_.info.isParameterless) @@ -1061,10 +1059,8 @@ trait Applications extends Compatibility { self: Typer => def narrowByShapes(alts: List[TermRef]): List[TermRef] = { if (normArgs exists (_.isInstanceOf[untpd.Function])) - if (args exists (_.isInstanceOf[Trees.NamedArg[_]])) - narrowByTrees(alts, args map treeShape, resultType) - else - narrowByTypes(alts, normArgs map typeShape, resultType) + if (hasNamedArg(args)) narrowByTrees(alts, args map treeShape, resultType) + else narrowByTypes(alts, normArgs map typeShape, resultType) else alts } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 2d9e5505898a..1b31e43115b4 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -390,7 +390,7 @@ trait TypeAssigner { errorType(s"named and positional type arguments may not be mixed", arg.pos) } val ownType = - if (args.head.isInstanceOf[NamedArg]) (tycon.tpe /: args)(refineNamed) + if (hasNamedArg(args)) (tycon.tpe /: args)(refineNamed) else if (sameLength(tparams, args)) tycon.tpe.appliedTo(args.tpes) else errorType(d"wrong number of type arguments for ${tycon.tpe}, should be ${tparams.length}", tree.pos) tree.withType(ownType) diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 160cf808ca8b..6cd7b5c72b7f 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -17,6 +17,7 @@ import SymDenotations._ import Annotations._ import Names._ import NameOps._ +import Applications._ import Flags._ import Decorators._ import ErrorReporting._ @@ -872,7 +873,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else { var args = tree.args val args1 = - if (args.head.isInstanceOf[untpd.NamedArg]) + if (hasNamedArg(args)) for (arg @ NamedArg(id, argtpt) <- args) yield { val argtpt1 = typedType(argtpt) cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) From ea8043137c2be8c2bc38c0b699251d3feae5afad Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 29 Jan 2016 21:48:55 +0100 Subject: [PATCH 24/26] Allow Named Arguments in TypeArgs Lets one also pass named arguments to methods. --- docs/SyntaxSummary.txt | 2 +- src/dotty/tools/dotc/ast/tpd.scala | 2 +- src/dotty/tools/dotc/parsing/Parsers.scala | 4 +- .../tools/dotc/transform/PostTyper.scala | 42 ++++++++++++++++-- src/dotty/tools/dotc/typer/Applications.scala | 11 ++++- src/dotty/tools/dotc/typer/TypeAssigner.scala | 43 +++++++++++++++++-- src/dotty/tools/dotc/typer/Typer.scala | 6 +-- test/dotc/tests.scala | 2 +- tests/neg/named-params.scala | 12 ++++++ tests/pos/named-params.scala | 16 ++++++- 10 files changed, 121 insertions(+), 19 deletions(-) diff --git a/docs/SyntaxSummary.txt b/docs/SyntaxSummary.txt index 78f59e6d1d80..d4f7ceadec50 100644 --- a/docs/SyntaxSummary.txt +++ b/docs/SyntaxSummary.txt @@ -162,7 +162,7 @@ grammar. | `_' | `(' ExprsInParens `)' Parens(exprs) | SimpleExpr `.' id Select(expr, id) - | SimpleExpr TypeArgs TypeApply(expr, args) + | SimpleExpr (TypeArgs | NamedTypeArgs) TypeApply(expr, args) | SimpleExpr1 ArgumentExprs Apply(expr, args) | XmlExpr ExprsInParens ::= ExprInParens {`,' ExprInParens} diff --git a/src/dotty/tools/dotc/ast/tpd.scala b/src/dotty/tools/dotc/ast/tpd.scala index b78e4c79f3b2..8e52d695b684 100644 --- a/src/dotty/tools/dotc/ast/tpd.scala +++ b/src/dotty/tools/dotc/ast/tpd.scala @@ -67,7 +67,7 @@ object tpd extends Trees.Instance[Type] with TypedTreeInfo { def Typed(expr: Tree, tpt: Tree)(implicit ctx: Context): Typed = ta.assignType(untpd.Typed(expr, tpt), tpt) - def NamedArg(name: Name, arg: Tree)(implicit ctx: Context) = + def NamedArg(name: Name, arg: Tree)(implicit ctx: Context): NamedArg = ta.assignType(untpd.NamedArg(name, arg), arg) def Assign(lhs: Tree, rhs: Tree)(implicit ctx: Context): Assign = diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index 58ec75605b59..bb8fbe08b082 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -1075,7 +1075,7 @@ object Parsers { * | Path * | `(' [ExprsInParens] `)' * | SimpleExpr `.' Id - * | SimpleExpr TypeArgs + * | SimpleExpr (TypeArgs | NamedTypeArgs) * | SimpleExpr1 ArgumentExprs */ def simpleExpr(): Tree = { @@ -1124,7 +1124,7 @@ object Parsers { in.nextToken() simpleExprRest(selector(t), canApply = true) case LBRACKET => - val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs()) } + val tapp = atPos(t.pos.start, in.offset) { TypeApply(t, typeArgs(namedOK = true)) } simpleExprRest(tapp, canApply = true) case LPAREN | LBRACE if canApply => val app = atPos(t.pos.start, in.offset) { Apply(t, argumentExprs()) } diff --git a/src/dotty/tools/dotc/transform/PostTyper.scala b/src/dotty/tools/dotc/transform/PostTyper.scala index b38858b72a1c..d552c16f7ffb 100644 --- a/src/dotty/tools/dotc/transform/PostTyper.scala +++ b/src/dotty/tools/dotc/transform/PostTyper.scala @@ -143,6 +143,41 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } } + private def normalizeTypeArgs(tree: TypeApply)(implicit ctx: Context): TypeApply = tree.tpe match { + case pt: PolyType => // wait for more arguments coming + tree + case _ => + def decompose(tree: TypeApply): (Tree, List[Tree]) = tree.fun match { + case fun: TypeApply => + val (tycon, args) = decompose(fun) + (tycon, args ++ tree.args) + case _ => + (tree.fun, tree.args) + } + def reorderArgs(pnames: List[Name], namedArgs: List[NamedArg], otherArgs: List[Tree]): List[Tree] = pnames match { + case pname :: pnames1 => + namedArgs.partition(_.name == pname) match { + case (NamedArg(_, arg) :: _, namedArgs1) => + arg :: reorderArgs(pnames1, namedArgs1, otherArgs) + case _ => + val otherArg :: otherArgs1 = otherArgs + otherArg :: reorderArgs(pnames1, namedArgs, otherArgs1) + } + case nil => + assert(namedArgs.isEmpty && otherArgs.isEmpty) + Nil + } + val (tycon, args) = decompose(tree) + tycon.tpe.widen match { + case PolyType(pnames) => + val (namedArgs, otherArgs) = args.partition(isNamedArg) + val args1 = reorderArgs(pnames, namedArgs.asInstanceOf[List[NamedArg]], otherArgs) + TypeApply(tycon, args1).withPos(tree.pos).withType(tree.tpe) + case _ => + tree + } + } + override def transform(tree: Tree)(implicit ctx: Context): Tree = try normalizeTree(tree) match { case tree: Ident => @@ -152,15 +187,16 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisTran } case tree: Select => transformSelect(paramFwd.adaptRef(tree), Nil) - case tree @ TypeApply(fn, args) => + case tree: TypeApply => + val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree) Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType]) fn match { case sel: Select => val args1 = transform(args) val sel1 = transformSelect(sel, args1) - if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree)(sel1, args1) + if (superAcc.isProtectedAccessor(sel1)) sel1 else cpy.TypeApply(tree1)(sel1, args1) case _ => - super.transform(tree) + super.transform(tree1) } case tree @ Assign(sel: Select, _) => superAcc.transformAssign(super.transform(tree)) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index c7acd3c1fa03..098385d4be67 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -603,12 +603,19 @@ trait Applications extends Compatibility { self: Typer => protected def handleUnexpectedFunType(tree: untpd.Apply, fun: Tree)(implicit ctx: Context): Tree = throw new Error(s"unexpected type.\n fun = $fun,\n methPart(fun) = ${methPart(fun)},\n methPart(fun).tpe = ${methPart(fun).tpe},\n tpe = ${fun.tpe}") + def typedNamedArgs(args: List[untpd.Tree])(implicit ctx: Context) = + for (arg @ NamedArg(id, argtpt) <- args) yield { + val argtpt1 = typedType(argtpt) + cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) + } + def typedTypeApply(tree: untpd.TypeApply, pt: Type)(implicit ctx: Context): Tree = track("typedTypeApply") { - var typedArgs = tree.args mapconserve (typedType(_)) + val isNamed = hasNamedArg(tree.args) + var typedArgs = if (isNamed) typedNamedArgs(tree.args) else tree.args.mapconserve(typedType(_)) val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt)) typedFn.tpe.widen match { case pt: PolyType => - if (typedArgs.length <= pt.paramBounds.length) + if (typedArgs.length <= pt.paramBounds.length && !isNamed) typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg) case _ => } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1b31e43115b4..1b9488ae3b3f 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -8,6 +8,8 @@ import Scopes._, Contexts._, Constants._, Types._, Symbols._, Names._, Flags._, import ErrorReporting._, Annotations._, Denotations._, SymDenotations._, StdNames._, TypeErasure._ import util.Positions._ import config.Printers._ +import ast.Trees._ +import collection.mutable trait TypeAssigner { import tpd._ @@ -305,9 +307,44 @@ trait TypeAssigner { def assignType(tree: untpd.TypeApply, fn: Tree, args: List[Tree])(implicit ctx: Context) = { val ownType = fn.tpe.widen match { case pt: PolyType => - val argTypes = args.tpes - if (sameLength(argTypes, pt.paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) - else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos) + val paramNames = pt.paramNames + if (hasNamedArg(args)) { + val argMap = new mutable.HashMap[Name, Type] + for (NamedArg(name, arg) <- args) + if (argMap.contains(name)) + ctx.error("duplicate name", arg.pos) + else if (!paramNames.contains(name)) + ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos) + else + argMap(name) = arg.tpe + val gapBuf = new mutable.ListBuffer[Int] + def nextPoly = { + val idx = gapBuf.length + gapBuf += idx + PolyParam(pt, idx) + } + val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextPoly)) + val transform = new TypeMap { + def apply(t: Type) = t match { + case PolyParam(`pt`, idx) => normArgs(idx) + case _ => mapOver(t) + } + } + val resultType1 = transform(pt.resultType) + if (gapBuf.isEmpty) resultType1 + else { + val gaps = gapBuf.toList + pt.derivedPolyType( + gaps.map(paramNames.filterNot(argMap.contains)), + gaps.map(idx => transform(pt.paramBounds(idx)).bounds), + resultType1) + } + } + else { + val argTypes = args.tpes + if (sameLength(argTypes, paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) + else errorType(d"wrong number of type parameters for ${fn.tpe}; expected: ${pt.paramNames.length}", tree.pos) + } case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) } diff --git a/src/dotty/tools/dotc/typer/Typer.scala b/src/dotty/tools/dotc/typer/Typer.scala index 6cd7b5c72b7f..7602d1a627a3 100644 --- a/src/dotty/tools/dotc/typer/Typer.scala +++ b/src/dotty/tools/dotc/typer/Typer.scala @@ -873,11 +873,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit else { var args = tree.args val args1 = - if (hasNamedArg(args)) - for (arg @ NamedArg(id, argtpt) <- args) yield { - val argtpt1 = typedType(argtpt) - cpy.NamedArg(arg)(id, argtpt1).withType(argtpt1.tpe) - } + if (hasNamedArg(args)) typedNamedArgs(args) else { if (args.length != tparams.length) { ctx.error(d"wrong number of type arguments for ${tpt1.tpe}, should be ${tparams.length}", tree.pos) diff --git a/test/dotc/tests.scala b/test/dotc/tests.scala index a61646deda9b..f01f06ae3a09 100644 --- a/test/dotc/tests.scala +++ b/test/dotc/tests.scala @@ -121,7 +121,7 @@ class tests extends CompilerTest { @Test def neg_autoTupling = compileFile(posDir, "autoTuplingTest", args = "-language:noAutoTupling" :: Nil, xerrors = 3) @Test def neg_autoTupling2 = compileFile(negDir, "autoTuplingTest", xerrors = 3) @Test def neg_companions = compileFile(negDir, "companions", xerrors = 1) - @Test def namedParams = compileFile(negDir, "named-params", xerrors = 3) + @Test def namedParams = compileFile(negDir, "named-params", xerrors = 14) @Test def neg_over = compileFile(negDir, "over", xerrors = 3) @Test def neg_overrides = compileFile(negDir, "overrides", xerrors = 14) @Test def neg_overrideClass = compileFile(negDir, "overrideClass", List("-language:Scala2"), xerrors = 1) diff --git a/tests/neg/named-params.scala b/tests/neg/named-params.scala index c1f055e10216..9ef4ed066d69 100644 --- a/tests/neg/named-params.scala +++ b/tests/neg/named-params.scala @@ -19,4 +19,16 @@ object Test { val c3 = new C[Elem = String, Value = Int]("B") val c4 = new C[Elem = String]("C") val x2: c2.Elem = c2.elem + + val c5 = new C[Elem1 = String, Value0 = Int]("B") // error // error + + def d2[E, V](x: E) = new C[Elem = E, Value = V](x) + + val dup = d2[E = Int, V = String, E = Boolean](2) // error + val z1 = d2[Elem = Int, Value = String](1) // error // error + val z2 = d2[Value = String, Elem = Int](1) // error // error + val z3 = d2[Elem = Int](1) // error + val z4 = d2[Value = Int]("AAA") // error + val z5 = d2[Elem = Int][Value = String](1) //error // error + } diff --git a/tests/pos/named-params.scala b/tests/pos/named-params.scala index 9668ec34ce8d..bcd64ea4b911 100644 --- a/tests/pos/named-params.scala +++ b/tests/pos/named-params.scala @@ -4,7 +4,7 @@ class C[type Elem, type Value](val elem: Elem) { def toVal: Elem = ??? } -abstract class D[type Elem, V](elem: Elem) extends C[Elem, V](elem) +class D[type Elem, V](elem: Elem) extends C[Elem, V](elem) object Test { val c = new C[String, String]("A") { @@ -17,6 +17,18 @@ object Test { val c3 = new C[Elem = String, Value = Int]("B") val c4 = new C[Elem = String]("C") val x2: c2.Elem = c2.elem + + def d1[E, V](x: E) = new D[E, V](x) + def d2[E, V](x: E) = new C[Elem = E, Value = V](x) + + val y1 = d1[Int, String](1) + val y2 = d1[E = Int](2) + val y3 = d1[V = String](3) + val z1 = d2[E = Int, V = String](1) + val z2 = d2[V = String, E = Int](1) + val z3 = d2[E = Int](1) + val z4 = d2[V = Int]("AAA") + val z5 = d2[E = Int][V = String](1) } // Adapated from i94-nada @@ -30,3 +42,5 @@ trait Test1 { def flatMap[X,Y,M <: Monad](m: M[Elem = X], f: X => M[Elem = Y]): M[Elem = Y] = f(m.unit) println(flatMap(Left(1), {x: Int => Left(x)})) } + + From 90f8a22eb4374088eacd2e3f920af02a86f215d0 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Tue, 9 Feb 2016 13:22:07 +0100 Subject: [PATCH 25/26] Simplify logic in matchNamed --- src/dotty/tools/dotc/typer/Checking.scala | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/dotty/tools/dotc/typer/Checking.scala b/src/dotty/tools/dotc/typer/Checking.scala index fec5b86f85ca..7e279b9e4550 100644 --- a/src/dotty/tools/dotc/typer/Checking.scala +++ b/src/dotty/tools/dotc/typer/Checking.scala @@ -56,12 +56,11 @@ object Checking { case AppliedTypeTree(tycon, args) => // If `args` is a list of named arguments, return corresponding type parameters, // otherwise return type parameters unchanged - def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[Symbol] = args match { - case (_: NamedArg) :: _ => + def matchNamed(tparams: List[TypeSymbol], args: List[Tree]): List[Symbol] = + if (hasNamedArg(args)) for (NamedArg(name, _) <- args) yield tycon.tpe.member(name).symbol - case _ => + else tparams - } val tparams = matchNamed(tycon.tpe.typeSymbol.typeParams, args) val bounds = tparams.map(tparam => tparam.info.asSeenFrom(tycon.tpe.normalizedPrefix, tparam.owner.owner).bounds) From c534a1bc3bd4bc8a49f5d293119961c24b653b57 Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Fri, 12 Feb 2016 08:49:38 +0100 Subject: [PATCH 26/26] Address reviewer comments --- src/dotty/tools/dotc/parsing/Parsers.scala | 1 - src/dotty/tools/dotc/typer/Applications.scala | 8 ++++++- src/dotty/tools/dotc/typer/TypeAssigner.scala | 22 +++++++++---------- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/dotty/tools/dotc/parsing/Parsers.scala b/src/dotty/tools/dotc/parsing/Parsers.scala index bb8fbe08b082..a9ef55a30eae 100644 --- a/src/dotty/tools/dotc/parsing/Parsers.scala +++ b/src/dotty/tools/dotc/parsing/Parsers.scala @@ -786,7 +786,6 @@ object Parsers { in.nextToken() otherArgs(NamedArg(name, argType()), namedTypeArg) case firstArg => - if (in.token == EQUALS) println(s"??? $firstArg") otherArgs(firstArg, argType) } else commaSeparated(argType) diff --git a/src/dotty/tools/dotc/typer/Applications.scala b/src/dotty/tools/dotc/typer/Applications.scala index 098385d4be67..08c347d0d130 100644 --- a/src/dotty/tools/dotc/typer/Applications.scala +++ b/src/dotty/tools/dotc/typer/Applications.scala @@ -615,7 +615,13 @@ trait Applications extends Compatibility { self: Typer => val typedFn = typedExpr(tree.fun, PolyProto(typedArgs.tpes, pt)) typedFn.tpe.widen match { case pt: PolyType => - if (typedArgs.length <= pt.paramBounds.length && !isNamed) + def namedParamBound(arg: Tree): Option[Type] = { + val NamedArg(id, _) = arg + val idx = pt.paramNames.indexOf(id) + if (idx < 0) None else Some(pt.paramBounds(idx)) + } + val paramBounds = if (isNamed) typedArgs.flatMap(namedParamBound) else pt.paramBounds + if (typedArgs.length <= pt.paramBounds.length) typedArgs = typedArgs.zipWithConserve(pt.paramBounds)(adaptTypeArg) case _ => } diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 1b9488ae3b3f..44a22da181c5 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -317,13 +317,10 @@ trait TypeAssigner { ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos) else argMap(name) = arg.tpe - val gapBuf = new mutable.ListBuffer[Int] - def nextPoly = { - val idx = gapBuf.length - gapBuf += idx - PolyParam(pt, idx) - } - val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextPoly)) + var missingParamCount = 0 + def nextMissingParam = + try PolyParam(pt, missingParamCount) finally missingParamCount += 1 + val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextMissingParam)) val transform = new TypeMap { def apply(t: Type) = t match { case PolyParam(`pt`, idx) => normArgs(idx) @@ -331,12 +328,15 @@ trait TypeAssigner { } } val resultType1 = transform(pt.resultType) - if (gapBuf.isEmpty) resultType1 + if (missingParamCount == 0) resultType1 else { - val gaps = gapBuf.toList + val (missingParamNames, missingParamBounds) = + (paramNames, pt.paramBounds).zipped.filter { + case (pname, _) => !argMap.contains(pname) + } pt.derivedPolyType( - gaps.map(paramNames.filterNot(argMap.contains)), - gaps.map(idx => transform(pt.paramBounds(idx)).bounds), + missingParamNames, + missingParamBounds.map(transform(_).bounds), resultType1) } }