Skip to content

Commit

Permalink
Allow simple type args of parent types to back reference
Browse files Browse the repository at this point in the history
Can't do so for higher-kinded type arguments, as eta-expanding them
requires completing them.  Nor for applied type arguments, as typing the
applied tree type requires completing the type constructor.
  • Loading branch information
dwijnand committed Mar 11, 2023
1 parent 833a41e commit ec0d0f3
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 19 deletions.
3 changes: 3 additions & 0 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,9 @@ object SymDenotations {
containsOpaques ||
is(Module, butNot = Package) && owner.seesOpaques

def isTouched(using Context): Boolean =
flagsUNSAFE.is(Touched) // do not force the info to check the flag

def isProvisional(using Context): Boolean =
flagsUNSAFE.is(Provisional) // do not force the info to check the flag

Expand Down
20 changes: 10 additions & 10 deletions compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -279,16 +279,16 @@ class TypeApplications(val self: Type) extends AnyVal {
case tp: TypeRef => tp.symbol == defn.AnyKindClass
case _ => false
}
val selfResult = self.hkResult
val otherResult = other.hkResult
isAnyKind(selfResult) || isAnyKind(otherResult) ||
{ if (selfResult.exists)
otherResult.exists &&
selfResult.hasSameKindAs(otherResult) &&
self.typeParams.corresponds(other.typeParams)((sparam, oparam) =>
sparam.paramInfo.hasSameKindAs(oparam.paramInfo))
else !otherResult.exists
}
val sparams = self.typeParams
val oparams = other.typeParams
if sparams.isEmpty && oparams.isEmpty then true // defer calling hkResult to avoid completing types
else
val selfResult = self.hkResult
val otherResult = other.hkResult
isAnyKind(selfResult) || isAnyKind(otherResult)
|| !selfResult.exists && !otherResult.exists
|| selfResult.hasSameKindAs(otherResult)
&& sparams.corresponds(oparams)((sp, op) => sp.paramInfo.hasSameKindAs(op.paramInfo))
}

/** Dealias type if it can be done without forcing the TypeRef's info */
Expand Down
26 changes: 24 additions & 2 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ object Checking {
* A NoType paramBounds is used as a sign that checking should be suppressed.
*/
def preCheckKind(arg: Tree, paramBounds: Type)(using Context): Tree =
if (arg.tpe.widen.isRef(defn.NothingClass) ||
if (arg.tpe.isExactlyNothing ||
!paramBounds.exists ||
arg.tpe.hasSameKindAs(paramBounds.bounds.hi)) arg
else errorTree(arg, em"Type argument ${arg.tpe} does not have the same kind as its bound $paramBounds")
Expand Down Expand Up @@ -336,7 +336,29 @@ object Checking {
if (locked.contains(tp) || tp.symbol.infoOrCompleter.isInstanceOf[NoCompleter])
throw CyclicReference(tp.symbol)
locked += tp
try if (!tp.symbol.isClass) checkInfo(tp.info)
try
if !tp.symbol.isClass
&& (!cycleOK || tp.symbol.isTouched)
// from pos/i8900-cycle.scala
// [T <: Cov[U], U <: T]
//
// if we force U while checking T
// then U <: T will encounter T with a NoCompleter
// and throw while cycleOK=false (bad)
//
// so instead we delay U to later
// then U <: T will see T <: Cov[U] and then encounter U with a NoCompleter
// and throw but then cycleOK=true because it's a type argument
//
// for any case of true illegal cycles, e.g. T1 in neg/cycles.scala
// type X = (U, U) // error: cycle (old)
// type U = X & Int // error: cycle (new)
// skipping untouched U will move the error to when U is completed
//
// previously running preCheckKind while typing Cov[U] and (U, U) forced the `U`s
// now completion is avoided in hasSameKindAs by looking at type parameters, leaving the `U`s untouched
then
checkInfo(tp.info)
finally locked -= tp
tp.withPrefix(pre1)
}
Expand Down
8 changes: 4 additions & 4 deletions tests/neg/cycles.scala
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,12 @@ class E {
}

class T1 {
type X = (U, U) // error: cycle
type U = X & Int
type X = (U, U)
type U = X & Int // error: cycle
}
class T2 {
type X = (U, U) // error: cycle
type U = X | Int
type X = (U, U)
type U = X | Int // error: cycle
}
object T12 {
val _ : (T1 {})#U = ??? // old-error: conflicting bounds
Expand Down
9 changes: 9 additions & 0 deletions tests/neg/i15177.app.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// like tests/pos/i15177.scala
// but with an applied type B[D]
class X[T] {
type Id
}
object A extends X[B[D]]
class B[C](id: A
.Id) // error
class D
8 changes: 8 additions & 0 deletions tests/neg/i15177.hk.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// like tests/pos/i15177.scala
// but with B being higher kinded
class X[T[_]] {
type Id
}
object A extends X[B]
class B[C](id: A
.Id) // error: type Id is not a member of object A
13 changes: 13 additions & 0 deletions tests/neg/i15177.ub.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// like tests/pos/i15177.scala
// but with T having an upper bound
// that B doesn't conform to
// just to be sure that not forcing B
// doesn't backdoor an illegal X[B]
class X[T <: C] {
type Id
}
object A
extends X[ // error
B] // error
class B(id: A.Id)
class C
4 changes: 2 additions & 2 deletions tests/neg/i4368.scala
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,8 @@ object Test6 {

object Test7 {
class Fix[F[_]] {
class Foo { type R >: F[T] <: F[T] } // error: cyclic
type T = F[Foo#R]
class Foo { type R >: F[T] <: F[T] }
type T = F[Foo#R] // error: cyclic
}

object App {
Expand Down
2 changes: 1 addition & 1 deletion tests/neg/i4369b.scala
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
trait X[R <: Z, Z >: X[R, R] <: X[R, R]] // error // error
trait X[R <: Z, Z >: X[R, R] <: X[R, R]] // error
class Z extends X[Z, Z]
20 changes: 20 additions & 0 deletions tests/pos/i15177.FakeEnum.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
trait FakeEnum[A, @specialized(Byte, Short, Int, Long) B]
{
trait Value {
self: A =>
def name: String
def id: B
}
}

object FakeEnumType
extends FakeEnum[FakeEnumType, Short]
{
val MEMBER1 = new FakeEnumType((0: Short), "MEMBER1") {}
val MEMBER2 = new FakeEnumType((1: Short), "MEMBER2") {}
}

sealed abstract
class FakeEnumType(val id: Short, val name: String)
extends FakeEnumType.Value
{}
5 changes: 5 additions & 0 deletions tests/pos/i15177.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class X[T] {
type Id
}
object A extends X[B]
class B(id: A.Id)

0 comments on commit ec0d0f3

Please sign in to comment.