Skip to content

Commit 4fa0715

Browse files
authored
Delay opaque alias checking until PostTyper (#16644)
When we have a TypeBounds tree that also has an alias, we checked that the alias is within bounds at Typer. This could provoke a CyclicReference when processing opaque type aliases where the type has a bound and also the alias refers back to the type. We avoid the problem by performing the check at PostTyper, analogous to other bounds checks. Fixes #16642
2 parents f56089b + a7035c2 commit 4fa0715

File tree

6 files changed

+35
-7
lines changed

6 files changed

+35
-7
lines changed

compiler/src/dotty/tools/dotc/transform/PostTyper.scala

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -433,6 +433,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
433433
case SingletonTypeTree(ref) =>
434434
Checking.checkRealizable(ref.tpe, ref.srcPos)
435435
super.transform(tree)
436+
case tree: TypeBoundsTree =>
437+
val TypeBoundsTree(lo, hi, alias) = tree
438+
if !alias.isEmpty then
439+
val bounds = TypeBounds(lo.tpe, hi.tpe)
440+
if !bounds.contains(alias.tpe) then
441+
report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos)
442+
super.transform(tree)
436443
case tree: TypeTree =>
437444
tree.withType(
438445
tree.tpe match {

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

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2164,10 +2164,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
21642164
val alias1 = typed(alias)
21652165
val lo2 = if (lo1.isEmpty) typed(untpd.TypeTree(defn.NothingType)) else lo1
21662166
val hi2 = if (hi1.isEmpty) typed(untpd.TypeTree(defn.AnyType)) else hi1
2167-
if !alias1.isEmpty then
2168-
val bounds = TypeBounds(lo2.tpe, hi2.tpe)
2169-
if !bounds.contains(alias1.tpe) then
2170-
report.error(em"type ${alias1.tpe} outside bounds $bounds", tree.srcPos)
21712167
assignType(cpy.TypeBoundsTree(tree)(lo2, hi2, alias1), lo2, hi2, alias1)
21722168

21732169
def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = {

tests/neg/i8337.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
trait Foo[F <: Foo[F]]
22
class Bar extends Foo[Bar]
33

4-
object Q { // error: recursion limit exceeded
5-
opaque type X <: Foo[X] = Bar // error: out of bounds // error
4+
object Q { // error: cyclic reference
5+
opaque type X <: Foo[X] = Bar // error: cyclic reference
66
}

tests/neg/opaque-bounds-1.scala

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
abstract class Test {
2+
opaque type FlagSet = Int
3+
4+
opaque type Flag <: FlagSet = String // error: type String outside bounds <: Test.this.FlagSet
5+
6+
object Flag {
7+
def make(s: String): Flag = s
8+
}
9+
10+
val f: Flag = Flag.make("hello")
11+
val g: FlagSet = f
12+
13+
}

tests/neg/opaque-bounds.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ class Test { // error: class Test cannot be instantiated
22

33
opaque type FlagSet = Int
44

5-
opaque type Flag <: FlagSet = String // error: type String outside bounds <: Test.this.FlagSet
5+
opaque type Flag <: FlagSet = String
66

77
object Flag {
88
def make(s: String): Flag = s

tests/pos/recursive-opaques.scala

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
object Syms:
2+
import SymDs.*
3+
opaque type Symbol <: AnyRef
4+
= SymDenotation
5+
opaque type ClassSymbol <: Symbol
6+
= ClassDenotation
7+
8+
object SymDs:
9+
import Syms.*
10+
class SymDenotation(sym: Symbol)
11+
class ClassDenotation(sym: Symbol) extends SymDenotation(sym)
12+

0 commit comments

Comments
 (0)