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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 17 additions & 0 deletions
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

Lines changed: 11 additions & 10 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 2 additions & 2 deletions
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

Lines changed: 3 additions & 3 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 7 additions & 0 deletions
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

Lines changed: 1 addition & 1 deletion
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

Lines changed: 6 additions & 10 deletions
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

0 commit comments

Comments
 (0)