diff options
-rw-r--r-- | src/dotty/tools/dotc/core/Types.scala | 4 | ||||
-rw-r--r-- | src/dotty/tools/dotc/typer/TypeAssigner.scala | 32 | ||||
-rw-r--r-- | tests/pos/t1513a.scala | 36 | ||||
-rw-r--r-- | tests/pos/t1513b.scala | 16 |
4 files changed, 75 insertions, 13 deletions
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala index 87d94dcbe..cb423e186 100644 --- a/src/dotty/tools/dotc/core/Types.scala +++ b/src/dotty/tools/dotc/core/Types.scala @@ -2532,8 +2532,8 @@ object Types { /** A type for polymorphic methods */ class PolyType(val paramNames: List[TypeName])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type) extends CachedGroundType with GenericType with MethodOrPoly { - val paramBounds = paramBoundsExp(this) - val resType = resultTypeExp(this) + val paramBounds: List[TypeBounds] = paramBoundsExp(this) + val resType: Type = resultTypeExp(this) def variances = Nil protected def computeSignature(implicit ctx: Context) = resultSignature diff --git a/src/dotty/tools/dotc/typer/TypeAssigner.scala b/src/dotty/tools/dotc/typer/TypeAssigner.scala index 36404a68f..3266a38d1 100644 --- a/src/dotty/tools/dotc/typer/TypeAssigner.scala +++ b/src/dotty/tools/dotc/typer/TypeAssigner.scala @@ -321,21 +321,30 @@ trait TypeAssigner { case pt: PolyType => val paramNames = pt.paramNames if (hasNamedArg(args)) { - val argMap = new mutable.HashMap[Name, Type] + // Type arguments which are specified by name (immutable after this first loop) + val namedArgMap = new mutable.HashMap[Name, Type] for (NamedArg(name, arg) <- args) - if (argMap.contains(name)) + if (namedArgMap.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 + namedArgMap(name) = arg.tpe + + // Holds indexes of non-named typed arguments in paramNames val gapBuf = new mutable.ListBuffer[Int] - def nextPoly = { - val idx = gapBuf.length + def nextPoly(idx: Int) = { + val newIndex = gapBuf.length gapBuf += idx - PolyParam(pt, idx) + // Re-index unassigned type arguments that remain after transformation + PolyParam(pt, newIndex) + } + + // Type parameters after naming assignment, conserving paramNames order + val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) => + namedArgMap.getOrElse(pname, nextPoly(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) @@ -347,19 +356,20 @@ trait TypeAssigner { else { val gaps = gapBuf.toList pt.derivedPolyType( - gaps.map(paramNames.filterNot(argMap.contains)), + gaps.map(paramNames), 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) + if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes) else wrongNumberOfArgs(fn.tpe, "type ", pt.paramNames.length, tree.pos) } case _ => errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos) } + tree.withType(ownType) } @@ -383,8 +393,8 @@ trait TypeAssigner { def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) = tree.withType( - if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) - else target.tpe) + if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length) + else target.tpe) def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) = tree.withType(body.tpe) diff --git a/tests/pos/t1513a.scala b/tests/pos/t1513a.scala new file mode 100644 index 000000000..3c4c02376 --- /dev/null +++ b/tests/pos/t1513a.scala @@ -0,0 +1,36 @@ +object Test { + // Heterogeneous lists and natural numbers as defined in shapeless. + + sealed trait HList + sealed trait ::[H, T <: HList] extends HList + sealed trait HNil extends HList + + sealed trait Nat + sealed trait Succ[P <: Nat] extends Nat + sealed trait Zero extends Nat + + // Accessor type class to compute the N'th element of an HList L. + + trait Accessor[L <: HList, N <: Nat] { type Out } + object Accessor { + type Aux[L <: HList, N <: Nat, O] = Accessor[L, N] { type Out = O } + + // (H :: T).At[Zero] = H + implicit def caseZero[H, T <: HList]: Aux[H :: T, Zero, H] = ??? + + // T.At[N] = O => (H :: T).At[Succ[N]] = O + implicit def caseN[H, T <: HList, N <: Nat, O] + (implicit a: Aux[T, N, O]): Aux[H :: T, Succ[N], O] = ??? + } + + case class Proxy[T]() + + def at1[NN <: Nat, OO] (implicit e: Accessor.Aux[String :: HNil, NN, OO]): OO = ??? + def at2[NN <: Nat, OO](p: Proxy[NN])(implicit e: Accessor.Aux[String :: HNil, NN, OO]): OO = ??? + + // N is fixed by a value + at2(Proxy[Zero]): String + + // N is fixed as a type parameter (by name) + at1[NN = Zero]: String +} diff --git a/tests/pos/t1513b.scala b/tests/pos/t1513b.scala new file mode 100644 index 000000000..881187be0 --- /dev/null +++ b/tests/pos/t1513b.scala @@ -0,0 +1,16 @@ +object Test { + def f[T1 <: String, T2 <: Int, T3 <: Boolean](a1: T1, a2: T2, a3: T3) = () + + f ("", 1, true) + f[T1 = String] ("", 1, true) + f[T2 = Int] ("", 1, true) + f[T3 = Boolean] ("", 1, true) + f[T1 = String, T2 = Int] ("", 1, true) + f[T1 = String, T3 = Boolean] ("", 1, true) + f[T2 = Int, T1 = String] ("", 1, true) + f[T2 = Int, T3 = Boolean] ("", 1, true) + f[T3 = Boolean, T2 = Int] ("", 1, true) + f[T3 = Boolean, T1 = String] ("", 1, true) + f[T1 = String, T2 = Int, T3 = Boolean]("", 1, true) + f[String, Int, Boolean] ("", 1, true) +} |