Skip to content

Disallow unapplied types in aliases and bounds #9090

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -243,15 +243,15 @@ object Trees {
// ------ Categories of trees -----------------------------------

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

/** Instances of this class are trees for which isTerm is definitely true.
* Note that some trees have isTerm = true without being TermTrees (e.g. Ident, AnnotatedTree)
* Note that some trees have isTerm = true without being TermTrees (e.g. Ident, Annotated)
*/
trait TermTree[-T >: Untyped] extends Tree[T] {
type ThisTree[-T >: Untyped] <: TermTree[T]
Expand Down
17 changes: 17 additions & 0 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1086,6 +1086,22 @@ trait Checking {
checker.traverse(tree)
}

/** Check that user-defined (result) type is fully applied */
def checkFullyAppliedType(tree: Tree)(using Context): Unit = tree match
case TypeBoundsTree(lo, hi, alias) =>
checkFullyAppliedType(lo)
checkFullyAppliedType(hi)
checkFullyAppliedType(alias)
case Annotated(arg, annot) =>
checkFullyAppliedType(arg)
case LambdaTypeTree(_, body) =>
checkFullyAppliedType(body)
case _: TypeTree =>
case _ =>
if tree.tpe.typeParams.nonEmpty then
val what = if tree.symbol.exists then tree.symbol else i"type $tree"
ctx.error(em"$what takes type parameters", tree.sourcePos)

/** Check that we are in an inline context (inside an inline method or in inline code) */
def checkInInlineContext(what: String, posd: Positioned)(using Context): Unit =
if !Inliner.inInlineMethod && !ctx.isInlineContext then
Expand Down Expand Up @@ -1209,6 +1225,7 @@ trait ReChecking extends Checking {
import tpd._
override def checkEnum(cdef: untpd.TypeDef, cls: Symbol, firstParent: Symbol)(using Context): Unit = ()
override def checkRefsLegal(tree: tpd.Tree, badOwner: Symbol, allowed: (Name, Symbol) => Boolean, where: String)(using Context): Unit = ()
override def checkFullyAppliedType(tree: Tree)(using Context): Unit = ()
override def checkEnumCaseRefsLegal(cdef: TypeDef, enumCtx: Context)(using Context): Unit = ()
override def checkAnnotApplicable(annot: Tree, sym: Symbol)(using Context): Boolean = true
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Namer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1009,8 +1009,8 @@ class Namer { typer: Typer =>
case tp: TypeBounds =>
def recur(tp: Type): Type = tp match
case tp: HKTypeLambda if !tp.isDeclaredVarianceLambda =>
tp.withVariances(tp.paramNames.map(alwaysInvariant))
.derivedLambdaType(resType = recur(tp.resType))
val tp1 = tp.withVariances(tp.paramNames.map(alwaysInvariant))
tp1.derivedLambdaType(resType = recur(tp1.resType))
case tp => tp
tp.derivedTypeBounds(tp.lo, recur(tp.hi))
case _ =>
Expand Down
7 changes: 6 additions & 1 deletion compiler/src/dotty/tools/dotc/typer/Synthesizer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -277,9 +277,14 @@ class Synthesizer(typer: Typer)(using @constructorOnly c: Context):
val elemLabels = accessors.map(acc => ConstantType(Constant(acc.name.toString)))
val (monoType, elemsType) = mirroredType match
case mirroredType: HKTypeLambda =>
def accessorType(acc: Symbol) =
if cls.typeParams.hasSameLengthAs(mirroredType.paramRefs) then
acc.info.subst(cls.typeParams, mirroredType.paramRefs)
else
acc.info
val elems =
mirroredType.derivedLambdaType(
resType = TypeOps.nestedPairs(accessors.map(mirroredType.memberInfo(_).widenExpr))
resType = TypeOps.nestedPairs(accessors.map(accessorType))
)
(mkMirroredMonoType(mirroredType), elems)
case _ =>
Expand Down
21 changes: 11 additions & 10 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1664,13 +1664,16 @@ class Typer extends Namer
}
}

def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(using Context): Tree = {
private def typeIndexedLambdaTypeTree(
tree: untpd.LambdaTypeTree, tparams: List[untpd.TypeDef], body: untpd.Tree)(using Context) =
val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
val body1 = typedType(body)
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1)

def typedLambdaTypeTree(tree: untpd.LambdaTypeTree)(using Context): Tree =
val LambdaTypeTree(tparams, body) = tree
index(tparams)
val tparams1 = tparams.mapconserve(typed(_).asInstanceOf[TypeDef])
val body1 = typedType(tree.body)
assignType(cpy.LambdaTypeTree(tree)(tparams1, body1), tparams1, body1)
}
typeIndexedLambdaTypeTree(tree, tparams, body)

def typedTermLambdaTypeTree(tree: untpd.TermLambdaTypeTree)(using Context): Tree =
if dependentEnabled then
Expand Down Expand Up @@ -1923,14 +1926,12 @@ class Typer extends Namer
def typedTypeDef(tdef: untpd.TypeDef, sym: Symbol)(using Context): Tree = {
val TypeDef(name, rhs) = tdef
completeAnnotations(tdef, sym)
val rhs1 = tdef.rhs match {
val rhs1 = tdef.rhs match
case rhs @ LambdaTypeTree(tparams, body) =>
val tparams1 = tparams.map(typed(_)).asInstanceOf[List[TypeDef]]
val body1 = typedType(body)
assignType(cpy.LambdaTypeTree(rhs)(tparams1, body1), tparams1, body1)
typeIndexedLambdaTypeTree(rhs, tparams, body)
case rhs =>
typedType(rhs)
}
checkFullyAppliedType(rhs1)
assignType(cpy.TypeDef(tdef)(name, rhs1), sym)
}

Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i4557.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ class C0[A]
class C1[A, B]

object O {
type T0 = C0
type T0[X] = C0[X]
type T1 = C0[String, Int] // error
type T2[A] = C0[A, Int] // error

type S0 = C1
type S0[X, Y] = C1[X, Y]
type S1 = C1[Int] // error

class D0 extends T0 // error
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i7820.scala
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
trait A1 { type F[X <: F[_, _], Y] } // error: cyclic reference involving type F
trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F
trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F
trait A2 { type F[X <: F, Y] } // error: cyclic reference involving type F // error
trait A3 { type F[X >: F, Y] } // error: cyclic reference involving type F // error
6 changes: 3 additions & 3 deletions tests/neg/multi-param-derives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ object Test extends App {
given t2 [T] as Functor[[U] =>> (T, U)] {}
given t3 [T, U] as Functor[[V] =>> (T, U, V)] {}

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

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

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

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

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

case class Mono(i: Int) derives Bifunctor
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/parser-stability-12.scala
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
trait x0[]: // error
trait x1[x1 <:x0]
trait x1[x1 <:x0] // error: type x0 takes type parameters
extends x1[ // error
// error
7 changes: 7 additions & 0 deletions tests/neg/unapplied-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
trait T {
type L[X] = List[X]
type T1 <: L // error: takes type parameters
type T2 = L // error: takes type parameters
type T3 = List // error: takes type parameters
type T4 <: List // error: takes type parameters
}
2 changes: 1 addition & 1 deletion tests/pos-macros/i6588.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ def main(using QuoteContext) = {
foo[U]
foo[List[Int]]

type N = List
type N[+X] = List[X]
foo[N]
foo[List]

Expand Down
16 changes: 6 additions & 10 deletions tests/pos/X.scala
Original file line number Diff line number Diff line change
@@ -1,14 +1,10 @@
abstract class A() {
import scala.deriving._

var x: Int
trait FunctorK[F[_[_]]]
object FunctorK {
given [C] as FunctorK[[F[_]] =>> C] {}
given [T] as FunctorK[[F[_]] =>> Tuple1[F[T]]]

}

abstract class B() extends A() {

var xx: Int = 0;

def x = xx;
def x_=(y: Int) = xx = y;
def derived[F[_[_]]](using m: Mirror { type MirroredType[X[_]] = F[X] ; type MirroredElemTypes[_[_]] }, r: FunctorK[m.MirroredElemTypes]): FunctorK[F] = new FunctorK[F] {}
}

2 changes: 1 addition & 1 deletion tests/pos/hk-subtyping.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ object Test {
compare[Int, Int]
compare[Int, Any]

def f[C <: List] = {
def f[C <: [X] =>> List[X]] = {
compare[C[Int], List[Int]]
}

Expand Down
10 changes: 5 additions & 5 deletions tests/pos/i2232.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,15 @@ object Cats {
[A, B] =>> C[({type l[c0[_], c1[_, _]] = c1[A, B]})#l]

trait Category[C[_[_[_], _[_, _]]]] {
type -> = Cats.Cat[C]
type Obj = Cats.Obj[C]
type -> [X, Y] = Cats.Cat[C][X, Y]
type Obj[X] = Cats.Obj[C][X]

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

object Category {
type ByF[F[_, _]] = Category[_] { type -> = F }
type ByF[F[_, _]] = Category[_] { type -> [X, Y] = F[X, Y] }
}

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

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

val f: Int => Int = _ + 1
val g: Int => String = _.toString
f >>> g // error: no implicit arg found
f >>> g
}
4 changes: 0 additions & 4 deletions tests/pos/i3139.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,3 @@ trait Foo[T] {
object Test {
def foo[T](ev: Foo[T]): Foo[T] { type Base[A] = ev.Base[A] } = ev
}

object Test2 {
def foo[T](ev: Foo[T]): Foo[T] { type Base = ev.Base } = ev
}
2 changes: 1 addition & 1 deletion tests/pos/i3658.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ object App {
def main(args: Array[String]): Unit = {
trait ModuleSig {
type F[_]
type Type = F
type Type[X] = F[X]

def subst[F[_[_]]](fa: F[List]): F[Type]
}
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i5574.scala
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ object i5574 {
transparent inline def foo[T]: Any =
inline erasedValue[T] match {
case _: Box[f] =>
type t = f
type t[X] = f[X]
23
}

Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i6014-gadt.scala
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ object Test3 {

type K1Top = [t] =>> Any

class Foo[F <: K1Top]
class Foo[F[X] <: K1Top[X]]

inline def bar[T] = inline erasedValue[T] match {
case _: Foo[f] => summon[f[Int]]
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/i6159.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ trait B extends A

object O {
opaque type T[X <: A] = X
type U = T.U
type U[X <: A] = T.U[X]
object T{
type U[X <: A] = X
def t(a: T[B]): T[B] = a
Expand Down
4 changes: 2 additions & 2 deletions tests/pos/i7219.scala
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ object Test {
def x6: D[Int] = D()
}
export Types._
type D1 = Types.D
type U1 = Types.UC
type D1[X] = Types.D[X]
type U1[X] = Types.UC[X]

val y1: T = x1
val y2: U = x2
Expand Down
2 changes: 1 addition & 1 deletion tests/pos/mirror-implicit-scope.scala
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ object Test {
}

object K1 {
type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = F ; type MirroredElemTypes[_] }
type Generic[F[_]] = Mirror { type Scope = K1.type ; type MirroredType = [X] =>> F[X] ; type MirroredElemTypes[_] }
extension on [F[_] <: Product, T](gen: Generic[F]) {
inline def toRepr (t: F[T]): gen.MirroredElemTypes[T] = Tuple.fromProduct(t).asInstanceOf
}
Expand Down
4 changes: 2 additions & 2 deletions tests/pos/nestedLambdas.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ class Test {

val y: A[Int][Boolean] = x

def f[X <: T[Int]] = ???
def f[X <: [Y] =>> T[Int][Y]] = ???

f[A[Int]]

def g[X <: T] = ???
def g[X <: [Y] =>> [Z] =>> T[Y][Z]] = ???

g[A]

Expand Down
Loading