Skip to content

Commit 5c5d155

Browse files
committed
Refine kind checking
Need to also check parameter kinds of higher-kinded types, not just result kinds.
1 parent 757cf3f commit 5c5d155

File tree

4 files changed

+38
-27
lines changed

4 files changed

+38
-27
lines changed

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

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -251,18 +251,21 @@ class TypeApplications(val self: Type) extends AnyVal {
251251
}
252252

253253
/** Is self type higher-kinded (i.e. of kind != "*")? */
254-
def isHK(implicit ctx: Context): Boolean = self.dealias match {
255-
case self: TypeRef => self.info.isHK
256-
case self: RefinedType => false
257-
case self: HKTypeLambda => true
258-
case self: SingletonType => false
254+
def isHK(implicit ctx: Context): Boolean = hkResult.exists
255+
256+
/** If self type is higher-kinded, its result type, otherwise NoType */
257+
def hkResult(implicit ctx: Context): Type = self.dealias match {
258+
case self: TypeRef => self.info.hkResult
259+
case self: RefinedType => NoType
260+
case self: HKTypeLambda => self.resultType
261+
case self: SingletonType => NoType
259262
case self: TypeVar =>
260263
// Using `origin` instead of `underlying`, as is done for typeParams,
261264
// avoids having to set ephemeral in some cases.
262-
self.origin.isHK
263-
case self: WildcardType => self.optBounds.isHK
264-
case self: TypeProxy => self.superType.isHK
265-
case _ => false
265+
self.origin.hkResult
266+
case self: WildcardType => self.optBounds.hkResult
267+
case self: TypeProxy => self.superType.hkResult
268+
case _ => NoType
266269
}
267270

268271
/** Dealias type if it can be done without forcing the TypeRef's info */

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

Lines changed: 22 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -100,30 +100,38 @@ object Checking {
100100
checkValidIfHKApply(ctx.addMode(Mode.AllowLambdaWildcardApply))
101101
}
102102

103-
/** Check that the rank of the kind of `arg` does not exceed the rank of the
104-
* kind of `paramBounds`. E.g. if `paramBounds` has *-kind, `arg` must have
105-
* *-kind as well, and analogously for higher kinds.
106-
* More detailed kind checking is done as part of checkBounds in PostTyper.
107-
* The purpose of checkKindRank is to do a rough test earlier in Typer,
103+
/** Check that kind of `arg` has the same outline as the kind of paramBounds.
104+
* E.g. if `paramBounds` has kind * -> *, `arg` must have that kind as well,
105+
* and analogously for all other kinds. This kind checking does not take into account
106+
* variances or bounds. The more detailed kind checking is done as part of checkBounds in PostTyper.
107+
* The purpose of preCheckKind is to do a rough test earlier in Typer,
108108
* in order to prevent scenarios that lead to self application of
109-
* types. Self application needs to be avoided since it can lead to stackoverflows.
110-
* A test case is neg/i2771.scala.
109+
* types. Self application needs to be avoided since it can lead to stack overflows.
110+
* Test cases are neg/i2771.scala and neg/i2771b.scala.
111111
*/
112-
def checkKindRank(arg: Tree, paramBounds: TypeBounds)(implicit ctx: Context): Tree = {
112+
def preCheckKind(arg: Tree, paramBounds: TypeBounds)(implicit ctx: Context): Tree = {
113113
def result(tp: Type): Type = tp match {
114114
case tp: HKTypeLambda => tp.resultType
115115
case tp: TypeProxy => result(tp.superType)
116116
case _ => defn.AnyType
117117
}
118-
def kindOK(argType: Type, boundType: Type): Boolean =
119-
!argType.isHK ||
120-
boundType.isHK && kindOK(result(argType), result(boundType))
118+
def kindOK(argType: Type, boundType: Type): Boolean = {
119+
// println(i"check kind rank2$arg $argType $boundType") // DEBUG
120+
val argResult = argType.hkResult
121+
val boundResult = argType.hkResult
122+
if (argResult.exists)
123+
boundResult.exists &&
124+
kindOK(boundResult, argResult) &&
125+
argType.typeParams.corresponds(boundType.typeParams)((ap, bp) =>
126+
kindOK(ap.paramInfo, bp.paramInfo))
127+
else !boundResult.exists
128+
}
121129
if (kindOK(arg.tpe, paramBounds.hi)) arg
122-
else errorTree(arg, em"${arg.tpe} takes type parameters")
130+
else errorTree(arg, em"${arg.tpe} has wrong kind")
123131
}
124132

125-
def checkKindRanks(args: List[Tree], paramBoundss: List[TypeBounds])(implicit ctx: Context): List[Tree] = {
126-
val args1 = args.zipWithConserve(paramBoundss)(checkKindRank)
133+
def preCheckKinds(args: List[Tree], paramBoundss: List[TypeBounds])(implicit ctx: Context): List[Tree] = {
134+
val args1 = args.zipWithConserve(paramBoundss)(preCheckKind)
127135
args1 ++ args.drop(paramBoundss.length)
128136
// add any arguments that do not correspond to a parameter back,
129137
// so the wrong number of parameters is reported afterwards.

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import NameOps._
1414
import collection.mutable
1515
import reporting.diagnostic.Message
1616
import reporting.diagnostic.messages._
17-
import Checking.{checkKindRank, checkKindRanks, checkNoPrivateLeaks}
17+
import Checking.{preCheckKind, preCheckKinds, checkNoPrivateLeaks}
1818

1919
trait TypeAssigner {
2020
import tpd._
@@ -358,7 +358,7 @@ trait TypeAssigner {
358358
else if (!paramNames.contains(name))
359359
ctx.error(s"undefined parameter name, required: ${paramNames.mkString(" or ")}", arg.pos)
360360
else
361-
namedArgMap(name) = checkKindRank(arg, paramBoundsByName(name.asTypeName)).tpe
361+
namedArgMap(name) = preCheckKind(arg, paramBoundsByName(name.asTypeName)).tpe
362362

363363
// Holds indexes of non-named typed arguments in paramNames
364364
val gapBuf = new mutable.ListBuffer[Int]
@@ -391,7 +391,7 @@ trait TypeAssigner {
391391
}
392392
}
393393
else {
394-
val argTypes = checkKindRanks(args, pt.paramInfos).tpes
394+
val argTypes = preCheckKinds(args, pt.paramInfos).tpes
395395
if (sameLength(argTypes, paramNames) || ctx.phase.prev.relaxedTyping) pt.instantiate(argTypes)
396396
else wrongNumberOfTypeArgs(fn.tpe, pt.typeParams, args, tree.pos)
397397
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1140,7 +1140,7 @@ class Typer extends Namer with TypeAssigner with Applications with Implicits wit
11401140
}
11411141
args.zipWithConserve(tparams)(typedArg(_, _)).asInstanceOf[List[Tree]]
11421142
}
1143-
val args2 = checkKindRanks(args1, tparams.map(_.paramInfo.bounds))
1143+
val args2 = preCheckKinds(args1, tparams.map(_.paramInfo.bounds))
11441144
// check that arguments conform to bounds is done in phase PostTyper
11451145
assignType(cpy.AppliedTypeTree(tree)(tpt1, args2), tpt1, args2)
11461146
}

0 commit comments

Comments
 (0)