Skip to content

Commit 74ca079

Browse files
oderskyallanrenucci
authored andcommitted
Add Any-kinded types
Allow a form of type polymorphism by adding an AnyKind upper bound. Types of any kind are subtypes of `AnyKind`. The "any-kinded types" are `AnyKind` itself, and all type parameters and abstract types having it as an upper bound. Any-kinded types cannot be used as types of values, nor can they be applied to type parameters. About the only thing one can do with them is use them as parameters for implicit searches.
1 parent 004f3d5 commit 74ca079

File tree

9 files changed

+177
-20
lines changed

9 files changed

+177
-20
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,9 @@ class Definitions {
304304
def ObjectMethods = List(Object_eq, Object_ne, Object_synchronized, Object_clone,
305305
Object_finalize, Object_notify, Object_notifyAll, Object_wait, Object_waitL, Object_waitLI)
306306

307+
lazy val AnyKindClass = completeClass(enterCompleteClassSymbol(ScalaPackageClass, tpnme.AnyKind, AbstractFinal, Nil))
308+
def AnyKindType = AnyKindClass.typeRef
309+
307310
/** Marker method to indicate an argument to a call-by-name parameter.
308311
* Created by byNameClosures and elimByName, eliminated by Erasure,
309312
*/
@@ -1158,6 +1161,7 @@ class Definitions {
11581161
lazy val syntheticScalaClasses = List(
11591162
AnyClass,
11601163
AnyRefAlias,
1164+
AnyKindClass,
11611165
RepeatedParamClass,
11621166
ByNameParamClass2x,
11631167
AnyValClass,

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -196,6 +196,7 @@ object StdNames {
196196
final val REIFY_TYPECREATOR_PREFIX: N = "$typecreator"
197197

198198
final val Any: N = "Any"
199+
final val AnyKind: N = "AnyKind"
199200
final val AnyVal: N = "AnyVal"
200201
final val ExprApi: N = "ExprApi"
201202
final val Mirror: N = "Mirror"

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

Lines changed: 23 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -207,12 +207,17 @@ class TypeApplications(val self: Type) extends AnyVal {
207207
case _ => Nil
208208
}
209209

210-
/** Is self type higher-kinded (i.e. of kind != "*")? */
210+
/** Is self type higher-kinded (i.e. of kind != "*")
211+
* or any-kinded (i.e. has AnyKind as upper bound)?
212+
*/
211213
def isHK(implicit ctx: Context): Boolean = hkResult.exists
212214

213-
/** If self type is higher-kinded, its result type, otherwise NoType */
215+
/** If self type is higher-kinded, its result type, otherwise NoType.
216+
* Note: The hkResult of an any-kinded type is again AnyKind.
217+
*/
214218
def hkResult(implicit ctx: Context): Type = self.dealias match {
215-
case self: TypeRef => self.info.hkResult
219+
case self: TypeRef =>
220+
if (self.symbol == defn.AnyKindClass) self else self.info.hkResult
216221
case self: AppliedType =>
217222
if (self.tycon.typeSymbol.isClass) NoType else self.superType.hkResult
218223
case self: HKTypeLambda => self.resultType
@@ -226,17 +231,24 @@ class TypeApplications(val self: Type) extends AnyVal {
226231
case _ => NoType
227232
}
228233

229-
/** Do self and other have the same kinds (not counting bounds and variances) */
234+
/** Do self and other have the same kinds (not counting bounds and variances)?
235+
* Note: An any-kinded type "has the same kind" as any other type.
236+
*/
230237
def hasSameKindAs(other: Type)(implicit ctx: Context): Boolean = {
231-
// println(i"check kind $self $other") // DEBUG
238+
def isAnyKind(tp: Type) = tp match {
239+
case tp: TypeRef => tp.symbol == defn.AnyKindClass
240+
case _ => false
241+
}
232242
val selfResult = self.hkResult
233243
val otherResult = other.hkResult
234-
if (selfResult.exists)
235-
otherResult.exists &&
236-
selfResult.hasSameKindAs(otherResult) &&
237-
self.typeParams.corresponds(other.typeParams)((sparam, oparam) =>
238-
sparam.paramInfo.hasSameKindAs(oparam.paramInfo))
239-
else !otherResult.exists
244+
isAnyKind(selfResult) || isAnyKind(otherResult) ||
245+
{ if (selfResult.exists)
246+
otherResult.exists &&
247+
selfResult.hasSameKindAs(otherResult) &&
248+
self.typeParams.corresponds(other.typeParams)((sparam, oparam) =>
249+
sparam.paramInfo.hasSameKindAs(oparam.paramInfo))
250+
else !otherResult.exists
251+
}
240252
}
241253

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

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,22 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
4848
private[this] var totalCount = 0
4949

5050
private[this] var myAnyClass: ClassSymbol = null
51+
private[this] var myAnyKindClass: ClassSymbol = null
5152
private[this] var myNothingClass: ClassSymbol = null
5253
private[this] var myNullClass: ClassSymbol = null
5354
private[this] var myObjectClass: ClassSymbol = null
5455
private[this] var myAnyType: TypeRef = null
56+
private[this] var myAnyKindType: TypeRef = null
5557
private[this] var myNothingType: TypeRef = null
5658

5759
def AnyClass = {
5860
if (myAnyClass == null) myAnyClass = defn.AnyClass
5961
myAnyClass
6062
}
63+
def AnyKindClass = {
64+
if (myAnyKindClass == null) myAnyKindClass = defn.AnyKindClass
65+
myAnyKindClass
66+
}
6167
def NothingClass = {
6268
if (myNothingClass == null) myNothingClass = defn.NothingClass
6369
myNothingClass
@@ -74,6 +80,10 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
7480
if (myAnyType == null) myAnyType = AnyClass.typeRef
7581
myAnyType
7682
}
83+
def AnyKindType = {
84+
if (myAnyKindType == null) myAnyKindType = AnyKindClass.typeRef
85+
myAnyKindType
86+
}
7787
def NothingType = {
7888
if (myNothingType == null) myNothingType = NothingClass.typeRef
7989
myNothingType
@@ -367,8 +377,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
367377
case _ =>
368378
val cls2 = tp2.symbol
369379
if (cls2.isClass) {
370-
if (cls2.typeParams.nonEmpty && tp1.isHK)
371-
recur(tp1, EtaExpansion(cls2.typeRef))
380+
if (cls2 eq AnyKindClass)
381+
return true
372382
else {
373383
val base = tp1.baseType(cls2)
374384
if (base.exists) {
@@ -540,7 +550,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
540550
def compareTypeBounds = tp1 match {
541551
case tp1 @ TypeBounds(lo1, hi1) =>
542552
((lo2 eq NothingType) || isSubType(lo2, lo1)) &&
543-
((hi2 eq AnyType) || isSubType(hi1, hi2))
553+
((hi2 eq AnyType) || (hi2 eq AnyKindType) || isSubType(hi1, hi2))
544554
case tp1: ClassInfo =>
545555
tp2 contains tp1
546556
case _ =>
@@ -1216,8 +1226,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12161226
if (tp1 eq tp2) tp1
12171227
else if (!tp1.exists) tp2
12181228
else if (!tp2.exists) tp1
1219-
else if ((tp1 isRef AnyClass) || (tp2 isRef NothingClass)) tp2
1220-
else if ((tp2 isRef AnyClass) || (tp1 isRef NothingClass)) tp1
1229+
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp2
1230+
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp1
12211231
else tp2 match { // normalize to disjunctive normal form if possible.
12221232
case OrType(tp21, tp22) =>
12231233
tp1 & tp21 | tp1 & tp22
@@ -1265,8 +1275,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
12651275
if (tp1 eq tp2) tp1
12661276
else if (!tp1.exists) tp1
12671277
else if (!tp2.exists) tp2
1268-
else if ((tp1 isRef AnyClass) || (tp2 isRef NothingClass)) tp1
1269-
else if ((tp2 isRef AnyClass) || (tp1 isRef NothingClass)) tp2
1278+
else if ((tp1 isRef AnyClass) || (tp1 isRef AnyKindClass) || (tp2 isRef NothingClass)) tp1
1279+
else if ((tp2 isRef AnyClass) || (tp2 isRef AnyKindClass) || (tp1 isRef NothingClass)) tp2
12701280
else {
12711281
val t1 = mergeIfSuper(tp1, tp2, canConstrain)
12721282
if (t1.exists) t1

compiler/src/dotty/tools/dotc/reporting/diagnostic/messages.scala

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1462,7 +1462,9 @@ object messages {
14621462

14631463
case class MissingTypeParameterFor(tpe: Type)(implicit ctx: Context)
14641464
extends Message(MissingTypeParameterForID) {
1465-
val msg = hl"missing type parameter for ${tpe}"
1465+
val msg =
1466+
if (tpe.derivesFrom(defn.AnyKindClass)) hl"${tpe} cannot be used as a value type"
1467+
else hl"missing type parameter for ${tpe}"
14661468
val kind = "Syntax"
14671469
val explanation = ""
14681470
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2147,7 +2147,7 @@ class Typer extends Namer
21472147

21482148
def adaptNoArgsImplicitMethod(wtp: MethodType): Tree = {
21492149
assert(wtp.isImplicitMethod)
2150-
val tvarsToInstantiate = tvarsInParams(tree, locked)
2150+
val tvarsToInstantiate = tvarsInParams(tree, locked).distinct
21512151
wtp.paramInfos.foreach(instantiateSelected(_, tvarsToInstantiate))
21522152
val constr = ctx.typerState.constraint
21532153

tests/neg/anykind.scala

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
object AnyKinds {
2+
3+
val x: AnyKind = 2 // error
4+
5+
def f[X <: AnyKind]: Any = {
6+
type T0 = AnyKind[String] // error
7+
type T1 = X[Int] // error
8+
type T2 = X { type F = Int } // error
9+
val x: X = ??? // error
10+
}
11+
12+
f[Int] // OK
13+
f[[X] => X] // OK
14+
f[Nothing] // OK
15+
16+
def g[X <: Any]: Any = {
17+
f[X] // OK
18+
}
19+
g[Int] // OK
20+
g[Nothing] // OK
21+
22+
23+
}

tests/neg/anykind2.scala

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
object AnyKinds {
2+
3+
def f[X <: AnyKind]: Any = {
4+
g[X] // error
5+
g[AnyKind] // error
6+
}
7+
8+
f[Int] // OK
9+
f[[X] => X] // OK
10+
f[Nothing] // OK
11+
12+
def g[X <: Any]: Any = {
13+
f[X] // OK
14+
}
15+
g[Int] // OK
16+
g[List] // error
17+
g[Nothing] // OK
18+
19+
20+
}

tests/pos/kindPolySemiGroup.scala

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
// Adapted from github:mandubian/kind-polymorphic-semigroup.scala
2+
sealed trait HList
3+
case class HCons[+HD, +TL](hd: HD, tl: TL) extends HList
4+
case object HNil extends HList
5+
6+
object Test {
7+
8+
type HNil = HNil.type
9+
10+
// Kind Extractor
11+
trait Kinder[MA] { type M <: AnyKind }
12+
object Kinder extends KinderLowerImplicits {
13+
type Aux[MA, M0 <: AnyKind] = Kinder[MA] { type M = M0 }
14+
15+
implicit def kinder1[M0[_], A0]: Kinder.Aux[M0[A0], M0] =
16+
new Kinder[M0[A0]] { type M[t] = M0[t] }
17+
implicit def kinder2[M0[_, _], A0, B0]: Kinder.Aux[M0[A0, B0], M0] =
18+
new Kinder[M0[A0, B0]] { type M[t, u] = M0[t, u]; type Args = HCons[A0, HCons[B0, HNil]] }
19+
}
20+
trait KinderLowerImplicits {
21+
implicit def kinder0[A]: Kinder.Aux[A, A] = new Kinder[A] { type M = A; type Args = HNil }
22+
}
23+
24+
// Kind Polymorphic Semigroup using shapeless "Polymorphic function"-style
25+
trait SemiGroup[M <: AnyKind] {
26+
// Just a mirror type of itself to ensure the owning of AppendFunction...
27+
type Self
28+
// the function accepting only monomorphic type MA allowed by this scoped Semigroup AppendFunction
29+
def append[MA](m1: MA, m2: MA)(implicit appender: SemiGroup.AppendFunction[Self, MA, M]) = appender(m1, m2)
30+
}
31+
32+
object SemiGroup {
33+
type Aux[M <: AnyKind, Self0] = SemiGroup[M] { type Self = Self0 }
34+
35+
// the monomorphic append function (yes we need to reify monomorphic types sometimes)
36+
trait AppendFunction[P, FA, F <: AnyKind] {
37+
def apply(m1: FA, m2: FA): FA
38+
}
39+
}
40+
41+
// Int SemiGroup instance
42+
implicit object SemiGroupInt extends SemiGroup[Int] {
43+
type Self = this.type
44+
implicit val appender: SemiGroup.AppendFunction[Self, Int, Int]= new SemiGroup.AppendFunction[Self, Int, Int] {
45+
def apply(m1: Int, m2: Int) = m1 + m2
46+
}
47+
}
48+
49+
// List SemiGroup instance
50+
implicit object SemiGroupList extends SemiGroup[List] {
51+
type Self = this.type
52+
implicit def appender[A]: SemiGroup.AppendFunction[Self, List[A], List] = new {
53+
def apply(m1: List[A], m2: List[A]) = m1 ++ m2
54+
}
55+
}
56+
57+
// Map SemiGroup instance
58+
implicit object SemiGroupMap extends SemiGroup[Map] {
59+
type Self = this.type
60+
implicit def appender[A, B]: SemiGroup.AppendFunction[Self, Map[A, B], Map] = new {
61+
def apply(m1: Map[A, B], m2: Map[A, B]) = m1 ++ m2
62+
}
63+
}
64+
65+
// Searching a semigroup and using it
66+
def semiGroup[M <: AnyKind](implicit sg: SemiGroup[M]): SemiGroup.Aux[M, sg.Self] = sg
67+
68+
semiGroup[Int].append(5, 8)
69+
semiGroup[List].append(List(1), List(3))
70+
semiGroup[Map].append(Map("toto" -> 1L), Map("tata" -> 3L))
71+
72+
// higher level append function
73+
def append[MA, M <: AnyKind, Self](m1: MA, m2: MA)(
74+
implicit kinder: Kinder.Aux[MA, M], semiGroup: SemiGroup.Aux[M, Self], appender: SemiGroup.AppendFunction[Self, MA, M]
75+
): MA = semiGroup.append(m1, m2)
76+
77+
import SemiGroupList.appender
78+
import SemiGroupMap.appender
79+
80+
val r1: Int = append(5, 8)
81+
82+
// TODO: Figure igure out why `M` below cannot be inferred
83+
val r2: List[Int] = append[M = List](List(1), List(3))
84+
val r3: Map[String, Long] = append[M = Map](Map("toto" -> 1L), Map("tata" -> 3L))
85+
}

0 commit comments

Comments
 (0)