Skip to content

Commit 7224900

Browse files
committed
Disallow unapplied types in aliases and bounds
It's the same as scalac in this respect. Eearly on, we allowed aliases such as ``` type L = List ``` since we treated type parameters as type members, and higher-kinded types as existential types. This is no longer the case. So the original motivation for this is gone. Furthermore, such higher-kinded aliases and bounds tend to make code more obscure since the kind of a type is no longer obvious. They also tend to exploit lots of dark corners in the compiler, where we do not guard properly or early enough against kind-incorrectness. So the restricted system is both safer and leads to clearer code.
1 parent 028c666 commit 7224900

26 files changed

+97
-78
lines changed

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

+2-2
Original file line numberDiff line numberDiff line change
@@ -243,15 +243,15 @@ object Trees {
243243
// ------ Categories of trees -----------------------------------
244244

245245
/** Instances of this class are trees for which isType is definitely true.
246-
* Note that some trees have isType = true without being TypTrees (e.g. Ident, AnnotatedTree)
246+
* Note that some trees have isType = true without being TypTrees (e.g. Ident, Annotated)
247247
*/
248248
trait TypTree[-T >: Untyped] extends Tree[T] {
249249
type ThisTree[-T >: Untyped] <: TypTree[T]
250250
override def isType: Boolean = true
251251
}
252252

253253
/** Instances of this class are trees for which isTerm is definitely true.
254-
* Note that some trees have isTerm = true without being TermTrees (e.g. Ident, AnnotatedTree)
254+
* Note that some trees have isTerm = true without being TermTrees (e.g. Ident, Annotated)
255255
*/
256256
trait TermTree[-T >: Untyped] extends Tree[T] {
257257
type ThisTree[-T >: Untyped] <: TermTree[T]

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

+17
Original file line numberDiff line numberDiff line change
@@ -1086,6 +1086,22 @@ trait Checking {
10861086
checker.traverse(tree)
10871087
}
10881088

1089+
/** Check that user-defined (result) type is fully applied */
1090+
def checkFullyAppliedType(tree: Tree)(using Context): Unit = tree match
1091+
case TypeBoundsTree(lo, hi, alias) =>
1092+
checkFullyAppliedType(lo)
1093+
checkFullyAppliedType(hi)
1094+
checkFullyAppliedType(alias)
1095+
case Annotated(arg, annot) =>
1096+
checkFullyAppliedType(arg)
1097+
case LambdaTypeTree(_, body) =>
1098+
checkFullyAppliedType(body)
1099+
case _: TypeTree =>
1100+
case _ =>
1101+
if tree.tpe.typeParams.nonEmpty then
1102+
val what = if tree.symbol.exists then tree.symbol else i"type $tree"
1103+
ctx.error(em"$what takes type parameters", tree.sourcePos)
1104+
10891105
/** Check that we are in an inline context (inside an inline method or in inline code) */
10901106
def checkInInlineContext(what: String, posd: Positioned)(using Context): Unit =
10911107
if !Inliner.inInlineMethod && !ctx.isInlineContext then
@@ -1209,6 +1225,7 @@ trait ReChecking extends Checking {
12091225
import tpd._
12101226
override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(using Context): Unit = ()
12111227
override def checkRefsLegal(tree: tpd.Tree, badOwner: Symbol, allowed: (Name, Symbol) => Boolean, where: String)(using Context): Unit = ()
1228+
override def checkFullyAppliedType(tree: Tree)(using Context): Unit = ()
12121229
override def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(using Context): Unit = ()
12131230
override def checkAnnotApplicable(annot: Tree, sym: Symbol)(using Context): Boolean = true
12141231
}

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

+11-10
Original file line numberDiff line numberDiff line change
@@ -1664,13 +1664,16 @@ class Typer extends Namer
16641664
}
16651665
}
16661666

1667-
def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(using Context): Tree = {
1667+
private def typeIndexedLambdaTypeTree(
1668+
tree: untpd.LambdaTypeTree, tparams: List[untpd.TypeDef], body: untpd.Tree)(using Context) =
1669+
val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
1670+
val body1 = typedType(body)
1671+
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1)
1672+
1673+
def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(using Context): Tree =
16681674
val LambdaTypeTree(tparams, body) = tree
16691675
index(tparams)
1670-
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
1671-
val body1 = typedType(tree.body)
1672-
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1)
1673-
}
1676+
typeIndexedLambdaTypeTree(tree, tparams, body)
16741677

16751678
def typedTermLambdaTypeTree(tree: untpd.TermLambdaTypeTree)(using Context): Tree =
16761679
if dependentEnabled then
@@ -1923,14 +1926,12 @@ class Typer extends Namer
19231926
def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
19241927
val TypeDef(name, rhs) = tdef
19251928
completeAnnotations(tdef, sym)
1926-
val rhs1 = tdef.rhs match {
1929+
val rhs1 = tdef.rhs match
19271930
case rhs @ LambdaTypeTree(tparams, body) =>
1928-
val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
1929-
val body1 = typedType(body)
1930-
assignType(cpy.LambdaTypeTree(rhs)(tparams1, body1), tparams1, body1)
1931+
typeIndexedLambdaTypeTree(rhs, tparams, body)
19311932
case rhs =>
19321933
typedType(rhs)
1933-
}
1934+
checkFullyAppliedType(rhs1)
19341935
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
19351936
}
19361937

tests/neg/i4557.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ class C0[A]
22
class C1[A, B]
33

44
object O {
5-
type T0 = C0
5+
type T0[X] = C0[X]
66
type T1 = C0[String, Int] // error
77
type T2[A] = C0[A, Int] // error
88

9-
type S0 = C1
9+
type S0[X, Y] = C1[X, Y]
1010
type S1 = C1[Int] // error
1111

1212
class D0 extends T0 // error

tests/neg/i7820.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F
2-
trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F
3-
trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F
2+
trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F // error
3+
trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F // error

tests/neg/multi-param-derives.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ object Test extends App {
2727
given t2 [T] as Functor[[U] =>> (T, U)] {}
2828
given t3 [T, U] as Functor[[V] =>> (T, U, V)] {}
2929

30-
def derived[F[_]](using m: Mirror { type MirroredType = F ; type MirroredElemTypes[_] }, r: Functor[m.MirroredElemTypes]): Functor[F] = new Functor[F] {}
30+
def derived[F[_]](using m: Mirror { type MirroredType[X] = F[X] ; type MirroredElemTypes[_] }, r: Functor[m.MirroredElemTypes]): Functor[F] = new Functor[F] {}
3131
}
3232

3333
case class Mono(i: Int) derives Functor
@@ -43,7 +43,7 @@ object Test extends App {
4343
given [C] as FunctorK[[F[_]] =>> C] {}
4444
given [T] as FunctorK[[F[_]] =>> Tuple1[F[T]]]
4545

46-
def derived[F[_[_]]](using m: Mirror { type MirroredType = F ; type MirroredElemTypes[_[_]] }, r: FunctorK[m.MirroredElemTypes]): FunctorK[F] = new FunctorK[F] {}
46+
def derived[F[_[_]]](using m: Mirror { type MirroredType[X[_]] = F[X] ; type MirroredElemTypes[_[_]] }, r: FunctorK[m.MirroredElemTypes]): FunctorK[F] = new FunctorK[F] {}
4747
}
4848

4949
case class Mono(i: Int) derives FunctorK
@@ -61,7 +61,7 @@ object Test extends App {
6161
given t2 as Bifunctor[[T, U] =>> (T, U)] {}
6262
given t3 [T] as Bifunctor[[U, V] =>> (T, U, V)] {}
6363

64-
def derived[F[_, _]](using m: Mirror { type MirroredType = F ; type MirroredElemTypes[_, _] }, r: Bifunctor[m.MirroredElemTypes]): Bifunctor[F] = ???
64+
def derived[F[_, _]](using m: Mirror { type MirroredType[X, Y] = F[X, Y] ; type MirroredElemTypes[_, _] }, r: Bifunctor[m.MirroredElemTypes]): Bifunctor[F] = ???
6565
}
6666

6767
case class Mono(i: Int) derives Bifunctor

tests/neg/parser-stability-12.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
trait x0[]: // error
2-
trait x1[x1 <:x0]
2+
trait x1[x1 <:x0] // error: type x0 takes type parameters
33
extends x1[ // error
44
// error

tests/neg/unapplied-types.scala

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
trait T {
2+
type L[X] = List[X]
3+
type T1 <: L // error: takes type parameters
4+
type T2 = L // error: takes type parameters
5+
type T3 = List // error: takes type parameters
6+
type T4 <: List // error: takes type parameters
7+
}

tests/pos-macros/i6588.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ def main(using QuoteContext) = {
1515
foo[U]
1616
foo[List[Int]]
1717

18-
type N = List
18+
type N[+X] = List[X]
1919
foo[N]
2020
foo[List]
2121

tests/pos/X.scala

+6-10
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,10 @@
1-
abstract class A() {
1+
import scala.deriving._
22

3-
var x: Int
3+
trait FunctorK[F[_[_]]]
4+
object FunctorK {
5+
given [C] as FunctorK[[F[_]] =>> C] {}
6+
given [T] as FunctorK[[F[_]] =>> Tuple1[F[T]]]
47

5-
}
6-
7-
abstract class B() extends A() {
8-
9-
var xx: Int = 0;
10-
11-
def x = xx;
12-
def x_=(y: Int) = xx = y;
8+
def derived[F[_[_]]](using m: Mirror { type MirroredType[X[_]] = F[X] ; type MirroredElemTypes[_[_]] }, r: FunctorK[m.MirroredElemTypes]): FunctorK[F] = new FunctorK[F] {}
139
}
1410

tests/pos/hk-subtyping.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ object Test {
55
compare[Int, Int]
66
compare[Int, Any]
77

8-
def f[C <: List] = {
8+
def f[C <: [X] =>> List[X]] = {
99
compare[C[Int], List[Int]]
1010
}
1111

tests/pos/i2232.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ object Cats {
88
[A, B] =>> C[({type l[c0[_], c1[_, _]] = c1[A, B]})#l]
99

1010
trait Category[C[_[_[_], _[_, _]]]] {
11-
type -> = Cats.Cat[C]
12-
type Obj = Cats.Obj[C]
11+
type -> [X, Y] = Cats.Cat[C][X, Y]
12+
type Obj[X] = Cats.Obj[C][X]
1313

1414
def id[A: Obj]: A -> A
1515
def andThen[A, B, C](ab: A -> B, bc: B -> C): A -> C
1616
}
1717

1818
object Category {
19-
type ByF[F[_, _]] = Category[_] { type -> = F }
19+
type ByF[F[_, _]] = Category[_] { type -> [X, Y] = F[X, Y] }
2020
}
2121

2222
type Scal[f[_[_], _[_, _]]] = f[Trivial, Function1]
@@ -28,10 +28,10 @@ object Cats {
2828

2929
implicit class CategoryOps[F[_, _], A, B](ab: F[A, B]) {
3030
def >>>[C](bc: F[B, C])(implicit F: Category.ByF[F]): F[A, C] =
31-
F.andThen(ab, bc)
31+
??? // F.andThen(ab, bc) -- disabled since it does not typecheck
3232
}
3333

3434
val f: Int => Int = _ + 1
3535
val g: Int => String = _.toString
36-
f >>> g // error: no implicit arg found
36+
f >>> g
3737
}

tests/pos/i3139.scala

-4
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,3 @@ trait Foo[T] {
55
object Test {
66
def foo[T](ev: Foo[T]): Foo[T] { type Base[A] = ev.Base[A] } = ev
77
}
8-
9-
object Test2 {
10-
def foo[T](ev: Foo[T]): Foo[T] { type Base = ev.Base } = ev
11-
}

tests/pos/i3658.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ object App {
22
def main(args: Array[String]): Unit = {
33
trait ModuleSig {
44
type F[_]
5-
type Type = F
5+
type Type[X] = F[X]
66

77
def subst[F[_[_]]](fa: F[List]): F[Type]
88
}

tests/pos/i5574.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ object i5574 {
66
transparent inline def foo[T]: Any =
77
inline erasedValue[T] match {
88
case _: Box[f] =>
9-
type t = f
9+
type t[X] = f[X]
1010
23
1111
}
1212

tests/pos/i6014-gadt.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ object Test3 {
3131

3232
type K1Top = [t] =>> Any
3333

34-
class Foo[F <: K1Top]
34+
class Foo[F[X] <: K1Top[X]]
3535

3636
inline def bar[T] = inline erasedValue[T] match {
3737
case _: Foo[f] => summon[f[Int]]

tests/pos/i6159.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ trait B extends A
33

44
object O {
55
opaque type T[X <: A] = X
6-
type U = T.U
6+
type U[X <: A] = T.U[X]
77
object T{
88
type U[X <: A] = X
99
def t(a: T[B]): T[B] = a

tests/pos/i7219.scala

+2-2
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ object Test {
2424
def x6: D[Int] = D()
2525
}
2626
export Types._
27-
type D1 = Types.D
28-
type U1 = Types.UC
27+
type D1[X] = Types.D[X]
28+
type U1[X] = Types.UC[X]
2929

3030
val y1: T = x1
3131
val y2: U = x2

tests/pos/mirror-implicit-scope.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ object Test {
99
}
1010

1111
object K1 {
12-
type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] }
12+
type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = [X] =>> F[X] ; type MirroredElemTypes[_] }
1313
extension on [F[_] <: Product, T](gen: Generic[F]) {
1414
inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf
1515
}

tests/pos/opaque-groups.scala

+8-8
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ package object groups {
3232

3333
opaque type First[A] = A
3434
object First extends Wrapper {
35-
type F = First
35+
type F[A] = First[A]
3636
def wraps[G[_], A](ga: G[A]): G[First[A]] = ga
3737
def unwrap[G[_], A](gfa: G[First[A]]): G[A] = gfa
3838
implicit def firstSemigroup[A]: Semigroup[First[A]] =
@@ -41,7 +41,7 @@ package object groups {
4141

4242
opaque type Last[A] = A
4343
object Last extends Wrapper {
44-
type F = Last
44+
type F[A] = Last[A]
4545
def wraps[G[_], A](ga: G[A]): G[Last[A]] = ga
4646
def unwrap[G[_], A](gfa: G[Last[A]]): G[A] = gfa
4747
implicit def lastSemigroup[A]: Semigroup[Last[A]] =
@@ -50,7 +50,7 @@ package object groups {
5050

5151
opaque type Min[A] = A
5252
object Min extends Wrapper {
53-
type F = Min
53+
type F[A] = Min[A]
5454
def wraps[G[_], A](ga: G[A]): G[Min[A]] = ga
5555
def unwrap[G[_], A](gfa: G[Min[A]]): G[A] = gfa
5656
implicit def minSemigroup[A](implicit o: Ordering[A]): Semigroup[Min[A]] =
@@ -59,7 +59,7 @@ package object groups {
5959

6060
opaque type Max[A] = A
6161
object Max extends Wrapper {
62-
type F = Max
62+
type F[A] = Max[A]
6363
def wraps[G[_], A](ga: G[A]): G[Max[A]] = ga
6464
def unwrap[G[_], A](gfa: G[Max[A]]): G[A] = gfa
6565
implicit def maxSemigroup[A](implicit o: Ordering[A]): Semigroup[Max[A]] =
@@ -68,7 +68,7 @@ package object groups {
6868

6969
opaque type Plus[A] = A
7070
object Plus extends Wrapper {
71-
type F = Plus
71+
type F[A] = Plus[A]
7272
def wraps[G[_], A](ga: G[A]): G[Plus[A]] = ga
7373
def unwrap[G[_], A](gfa: G[Plus[A]]): G[A] = gfa
7474
implicit def plusSemigroup[A](implicit n: Numeric[A]): Semigroup[Plus[A]] =
@@ -77,7 +77,7 @@ package object groups {
7777

7878
opaque type Times[A] = A
7979
object Times extends Wrapper {
80-
type F = Times
80+
type F[A] = Times[A]
8181
def wraps[G[_], A](ga: G[A]): G[Times[A]] = ga
8282
def unwrap[G[_], A](gfa: G[Times[A]]): G[A] = gfa
8383
implicit def timesSemigroup[A](implicit n: Numeric[A]): Semigroup[Times[A]] =
@@ -86,7 +86,7 @@ package object groups {
8686

8787
opaque type Reversed[A] = A
8888
object Reversed extends Wrapper {
89-
type F = Reversed
89+
type F[A] = Reversed[A]
9090
def wraps[G[_], A](ga: G[A]): G[Reversed[A]] = ga
9191
def unwrap[G[_], A](gfa: G[Reversed[A]]): G[A] = gfa
9292
implicit def reversedOrdering[A](implicit o: Ordering[A]): Ordering[Reversed[A]] =
@@ -95,7 +95,7 @@ package object groups {
9595

9696
opaque type Unordered[A] = A
9797
object Unordered extends Wrapper {
98-
type F = Unordered
98+
type F[A] = Unordered[A]
9999
def wraps[G[_], A](ga: G[A]): G[Unordered[A]] = ga
100100
def unwrap[G[_], A](gfa: G[Unordered[A]]): G[A] = gfa
101101
implicit def unorderedOrdering[A]: Ordering[Unordered[A]] =

tests/pos/polyalias.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
object Test {
33

4-
type S = scala.Predef.Set
4+
type S[X] = scala.Predef.Set[X]
55

66
val z: S[_] = ???
77

0 commit comments

Comments
 (0)