Skip to content

Commit 5450507

Browse files
Fix #1513: misaligned by name type parameter type bounds
1 parent 3f3df3d commit 5450507

File tree

4 files changed

+75
-13
lines changed

4 files changed

+75
-13
lines changed

src/dotty/tools/dotc/core/Types.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2532,8 +2532,8 @@ object Types {
25322532
/** A type for polymorphic methods */
25332533
class PolyType(val paramNames: List[TypeName])(paramBoundsExp: GenericType => List[TypeBounds], resultTypeExp: GenericType => Type)
25342534
extends CachedGroundType with GenericType with MethodOrPoly {
2535-
val paramBounds = paramBoundsExp(this)
2536-
val resType = resultTypeExp(this)
2535+
val paramBounds: List[TypeBounds] = paramBoundsExp(this)
2536+
val resType: Type = resultTypeExp(this)
25372537
def variances = Nil
25382538

25392539
protected def computeSignature(implicit ctx: Context) = resultSignature

src/dotty/tools/dotc/typer/TypeAssigner.scala

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -321,21 +321,30 @@ trait TypeAssigner {
321321
case pt: PolyType =>
322322
val paramNames = pt.paramNames
323323
if (hasNamedArg(args)) {
324-
val argMap = new mutable.HashMap[Name, Type]
324+
// Type arguments which are specified by name (immutable after this first loop)
325+
val namedArgMap = new mutable.HashMap[Name, Type]
325326
for (NamedArg(name, arg) <- args)
326-
if (argMap.contains(name))
327+
if (namedArgMap.contains(name))
327328
ctx.error("duplicate name", arg.pos)
328329
else if (!paramNames.contains(name))
329330
ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos)
330331
else
331-
argMap(name) = arg.tpe
332+
namedArgMap(name) = arg.tpe
333+
334+
// Holds indexes of non-named typed arguments in paramNames
332335
val gapBuf = new mutable.ListBuffer[Int]
333-
def nextPoly = {
334-
val idx = gapBuf.length
336+
def nextPoly(idx: Int) = {
337+
val newIndex = gapBuf.length
335338
gapBuf += idx
336-
PolyParam(pt, idx)
339+
// Re-index unassigned type arguments that remain after transformation
340+
PolyParam(pt, newIndex)
341+
}
342+
343+
// Type parameters after naming assignment, conserving paramNames order
344+
val normArgs: List[Type] = paramNames.zipWithIndex.map { case (pname, idx) =>
345+
namedArgMap.getOrElse(pname, nextPoly(idx))
337346
}
338-
val normArgs = paramNames.map(pname => argMap.getOrElse(pname, nextPoly))
347+
339348
val transform = new TypeMap {
340349
def apply(t: Type) = t match {
341350
case PolyParam(`pt`, idx) => normArgs(idx)
@@ -347,19 +356,20 @@ trait TypeAssigner {
347356
else {
348357
val gaps = gapBuf.toList
349358
pt.derivedPolyType(
350-
gaps.map(paramNames.filterNot(argMap.contains)),
359+
gaps.map(paramNames),
351360
gaps.map(idx => transform(pt.paramBounds(idx)).bounds),
352361
resultType1)
353362
}
354363
}
355364
else {
356365
val argTypes = args.tpes
357-
if (sameLength(argTypes, paramNames)|| ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
366+
if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
358367
else wrongNumberOfArgs(fn.tpe, "type ", pt.paramNames.length, tree.pos)
359368
}
360369
case _ =>
361370
errorType(i"${err.exprStr(fn)} does not take type parameters", tree.pos)
362371
}
372+
363373
tree.withType(ownType)
364374
}
365375

@@ -383,8 +393,8 @@ trait TypeAssigner {
383393

384394
def assignType(tree: untpd.Closure, meth: Tree, target: Tree)(implicit ctx: Context) =
385395
tree.withType(
386-
if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length)
387-
else target.tpe)
396+
if (target.isEmpty) meth.tpe.widen.toFunctionType(tree.env.length)
397+
else target.tpe)
388398

389399
def assignType(tree: untpd.CaseDef, body: Tree)(implicit ctx: Context) =
390400
tree.withType(body.tpe)

tests/pos/t1513a.scala

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
object Test {
2+
// Heterogeneous lists and natural numbers as defined in shapeless.
3+
4+
sealed trait HList
5+
sealed trait ::[H, T <: HList] extends HList
6+
sealed trait HNil extends HList
7+
8+
sealed trait Nat
9+
sealed trait Succ[P <: Nat] extends Nat
10+
sealed trait Zero extends Nat
11+
12+
// Accessor type class to compute the N'th element of an HList L.
13+
14+
trait Accessor[L <: HList, N <: Nat] { type Out }
15+
object Accessor {
16+
type Aux[L <: HList, N <: Nat, O] = Accessor[L, N] { type Out = O }
17+
18+
// (H :: T).At[Zero] = H
19+
implicit def caseZero[H, T <: HList]: Aux[H :: T, Zero, H] = ???
20+
21+
// T.At[N] = O => (H :: T).At[Succ[N]] = O
22+
implicit def caseN[H, T <: HList, N <: Nat, O]
23+
(implicit a: Aux[T, N, O]): Aux[H :: T, Succ[N], O] = ???
24+
}
25+
26+
case class Proxy[T]()
27+
28+
def at1[NN <: Nat, OO] (implicit e: Accessor.Aux[String :: HNil, NN, OO]): OO = ???
29+
def at2[NN <: Nat, OO](p: Proxy[NN])(implicit e: Accessor.Aux[String :: HNil, NN, OO]): OO = ???
30+
31+
// N is fixed by a value
32+
at2(Proxy[Zero]): String
33+
34+
// N is fixed as a type parameter (by name)
35+
at1[NN = Zero]: String
36+
}

tests/pos/t1513b.scala

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
object Test {
2+
def f[T1 <: String, T2 <: Int, T3 <: Boolean](a1: T1, a2: T2, a3: T3) = ()
3+
4+
f ("", 1, true)
5+
f[T1 = String] ("", 1, true)
6+
f[T2 = Int] ("", 1, true)
7+
f[T3 = Boolean] ("", 1, true)
8+
f[T1 = String, T2 = Int] ("", 1, true)
9+
f[T1 = String, T3 = Boolean] ("", 1, true)
10+
f[T2 = Int, T1 = String] ("", 1, true)
11+
f[T2 = Int, T3 = Boolean] ("", 1, true)
12+
f[T3 = Boolean, T2 = Int] ("", 1, true)
13+
f[T3 = Boolean, T1 = String] ("", 1, true)
14+
f[T1 = String, T2 = Int, T3 = Boolean]("", 1, true)
15+
f[String, Int, Boolean] ("", 1, true)
16+
}

0 commit comments

Comments
 (0)