Skip to content

Commit 1c79982

Browse files
authored
Merge pull request #4423 from dotty-staging/fix-#4382
Fix #4382: Disallow wildcard arguments for higher-kinded types
2 parents 5ecdd8a + 5636765 commit 1c79982

File tree

12 files changed

+49
-23
lines changed

12 files changed

+49
-23
lines changed

compiler/src/dotty/tools/dotc/ast/Desugar.scala

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,8 @@ object desugar {
8888
else {
8989
def msg =
9090
s"no matching symbol for ${tp.symbol.showLocated} in ${defctx.owner} / ${defctx.effectiveScope.toList}"
91-
ErrorType(msg).assertingErrorsReported(msg)
92-
}
91+
ErrorType(msg).assertingErrorsReported(msg)
92+
}
9393
case _ =>
9494
mapOver(tp)
9595
}
@@ -366,6 +366,12 @@ object desugar {
366366
(if (args.isEmpty) tycon else AppliedTypeTree(tycon, args))
367367
.withPos(cdef.pos.startPos)
368368

369+
def isHK(tparam: Tree): Boolean = tparam match {
370+
case TypeDef(_, LambdaTypeTree(tparams, body)) => true
371+
case TypeDef(_, rhs: DerivedTypeTree) => isHK(rhs.watched)
372+
case _ => false
373+
}
374+
369375
def appliedRef(tycon: Tree, tparams: List[TypeDef] = constrTparams, widenHK: Boolean = false) = {
370376
val targs = for (tparam <- tparams) yield {
371377
val targ = refOfDef(tparam)
@@ -467,18 +473,29 @@ object desugar {
467473
// ev1: Eq[T1$1, T1$2], ..., evn: Eq[Tn$1, Tn$2]])
468474
// : Eq[C[T1$, ..., Tn$1], C[T1$2, ..., Tn$2]] = Eq
469475
//
470-
// If any of the T_i are higher-kinded, say `Ti[X1 >: L1 <: U1, ..., Xm >: Lm <: Um]`,
471-
// the corresponding type parameters for $ev_i are `Ti$1[_, ..., _], Ti$2[_, ..., _]`
472-
// (with m underscores `_`).
476+
// Higher-kinded type arguments `Ti` are omitted as evidence parameters.
477+
//
478+
// FIXME: This is too simplistic. Instead of just generating evidence arguments
479+
// for every first-kinded type parameter, we should look instead at the
480+
// actual types occurring in cases and derive parameters from these. E.g. in
481+
//
482+
// enum HK[F[_]] {
483+
// case C1(x: F[Int]) extends HK[F[Int]]
484+
// case C2(y: F[String]) extends HL[F[Int]]
485+
//
486+
// we would need evidence parameters for `F[Int]` and `F[String]`
487+
// We should generate Eq instances with the techniques
488+
// of typeclass derivation once that is available.
473489
def eqInstance = {
474490
val leftParams = constrTparams.map(derivedTypeParam(_, "$1"))
475491
val rightParams = constrTparams.map(derivedTypeParam(_, "$2"))
476-
val subInstances = (leftParams, rightParams).zipped.map((param1, param2) =>
477-
appliedRef(ref(defn.EqType), List(param1, param2), widenHK = true))
492+
val subInstances =
493+
for ((param1, param2) <- leftParams `zip` rightParams if !isHK(param1))
494+
yield appliedRef(ref(defn.EqType), List(param1, param2), widenHK = true)
478495
DefDef(
479496
name = nme.eqInstance,
480497
tparams = leftParams ++ rightParams,
481-
vparamss = List(makeImplicitParameters(subInstances)),
498+
vparamss = if (subInstances.isEmpty) Nil else List(makeImplicitParameters(subInstances)),
482499
tpt = appliedTypeTree(ref(defn.EqType),
483500
appliedRef(classTycon, leftParams) :: appliedRef(classTycon, rightParams) :: Nil),
484501
rhs = ref(defn.EqModule.termRef)).withFlags(Synthetic | Implicit)

compiler/src/dotty/tools/dotc/core/StdNames.scala

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import Symbols._
99
import Contexts._
1010
import Decorators.PreNamedString
1111
import util.NameTransformer
12-
import scala.collection.breakOut
1312

1413
object StdNames {
1514

compiler/src/dotty/tools/dotc/core/Symbols.scala

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import io.AbstractFile
3131
import language.implicitConversions
3232
import util.{NoSource, DotClass, Property}
3333
import scala.collection.JavaConverters._
34+
import config.Printers.typr
3435

3536
/** Creation methods for symbols */
3637
trait Symbols { this: Context =>
@@ -234,8 +235,8 @@ trait Symbols { this: Context =>
234235
def newStubSymbol(owner: Symbol, name: Name, file: AbstractFile = null): Symbol = {
235236
def stubCompleter = new StubInfo()
236237
val normalizedOwner = if (owner is ModuleVal) owner.moduleClass else owner
237-
println(s"creating stub for ${name.show}, owner = ${normalizedOwner.denot.debugString}, file = $file")
238-
println(s"decls = ${normalizedOwner.unforcedDecls.toList.map(_.debugString).mkString("\n ")}") // !!! DEBUG
238+
typr.println(s"creating stub for ${name.show}, owner = ${normalizedOwner.denot.debugString}, file = $file")
239+
typr.println(s"decls = ${normalizedOwner.unforcedDecls.toList.map(_.debugString).mkString("\n ")}") // !!! DEBUG
239240
//if (base.settings.debug.value) throw new Error()
240241
val stub = name match {
241242
case name: TermName =>

compiler/src/dotty/tools/dotc/core/TypeComparer.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -631,7 +631,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
631631
case EtaExpansion(tycon1) => recur(tycon1, tp2)
632632
case _ => tp2 match {
633633
case tp2: HKTypeLambda => false // this case was covered in thirdTry
634-
case _ => tp2.isLambdaSub && isSubType(tp1.resultType, tp2.appliedTo(tp1.paramRefs))
634+
case _ => tp2.typeParams.hasSameLengthAs(tp1.paramRefs) && isSubType(tp1.resultType, tp2.appliedTo(tp1.paramRefs))
635635
}
636636
}
637637
compareHKLambda

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ import dotty.tools.dotc.transform.Erasure
2929
import printing.Printer
3030
import Hashable._
3131
import Uniques._
32-
import collection.{mutable, Seq, breakOut}
32+
import collection.{mutable, Seq}
3333
import config.Config
3434
import annotation.tailrec
3535
import Flags.FlagSet

compiler/src/dotty/tools/dotc/typer/Checking.scala

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,11 @@ object Checking {
8686
if (boundsCheck) checkBounds(orderedArgs, bounds, instantiate)
8787

8888
def checkWildcardApply(tp: Type, pos: Position): Unit = tp match {
89-
case tp @ AppliedType(tycon, args) if args.exists(_.isInstanceOf[TypeBounds]) =>
90-
tycon match {
91-
case tycon: TypeLambda =>
92-
ctx.errorOrMigrationWarning(
93-
ex"unreducible application of higher-kinded type $tycon to wildcard arguments",
94-
pos)
95-
case _ =>
96-
checkWildcardApply(tp.superType, pos)
97-
}
89+
case tp @ AppliedType(tycon, args) =>
90+
if (tycon.isLambdaSub && args.exists(_.isInstanceOf[TypeBounds]))
91+
ctx.errorOrMigrationWarning(
92+
ex"unreducible application of higher-kinded type $tycon to wildcard arguments",
93+
pos)
9894
case _ =>
9995
}
10096
def checkValidIfApply(implicit ctx: Context): Unit =

tests/neg/i3976.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ object Test {
88
A == A
99
A == (B: Hoge[_])
1010

11-
A == B // error: cannot be compared
11+
A == B // should be error: cannot be compared, needs proper typeclass drivation of `Eq` to get there.
1212

1313
class C
1414

tests/neg/i4382.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
object App {
2+
type Id[A] >: A <: A
3+
def v1: Id[_] = ??? // error
4+
5+
type HkL[A] >: A
6+
def v2: HkL[_] = ??? // error
7+
8+
type HkU[A] <: A
9+
def v3: HkU[_] = ??? // error
10+
11+
type HkAbs[A]
12+
def v4: HkAbs[_] = ??? // error
13+
}
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)