Skip to content

Delay opaque alias checking until PostTyper #16644

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 1 commit into from
Jan 10, 2023
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
7 changes: 7 additions & 0 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,13 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
case SingletonTypeTree(ref) =>
Checking.checkRealizable(ref.tpe, ref.srcPos)
super.transform(tree)
case tree: TypeBoundsTree =>
val TypeBoundsTree(lo, hi, alias) = tree
if !alias.isEmpty then
val bounds = TypeBounds(lo.tpe, hi.tpe)
if !bounds.contains(alias.tpe) then
report.error(em"type ${alias.tpe} outside bounds $bounds", tree.srcPos)
super.transform(tree)
case tree: TypeTree =>
tree.withType(
tree.tpe match {
Expand Down
4 changes: 0 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Typer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2164,10 +2164,6 @@ class Typer(@constructorOnly nestingLevel: Int = 0) extends Namer
val alias1 = typed(alias)
val lo2 = if (lo1.isEmpty) typed(untpd.TypeTree(defn.NothingType)) else lo1
val hi2 = if (hi1.isEmpty) typed(untpd.TypeTree(defn.AnyType)) else hi1
if !alias1.isEmpty then
val bounds = TypeBounds(lo2.tpe, hi2.tpe)
if !bounds.contains(alias1.tpe) then
report.error(em"type ${alias1.tpe} outside bounds $bounds", tree.srcPos)
assignType(cpy.TypeBoundsTree(tree)(lo2, hi2, alias1), lo2, hi2, alias1)

def typedBind(tree: untpd.Bind, pt: Type)(using Context): Tree = {
Expand Down
4 changes: 2 additions & 2 deletions tests/neg/i8337.scala
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
trait Foo[F <: Foo[F]]
class Bar extends Foo[Bar]

object Q { // error: recursion limit exceeded
opaque type X <: Foo[X] = Bar // error: out of bounds // error
object Q { // error: cyclic reference
opaque type X <: Foo[X] = Bar // error: cyclic reference
}
13 changes: 13 additions & 0 deletions tests/neg/opaque-bounds-1.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
abstract class Test {
opaque type FlagSet = Int

opaque type Flag <: FlagSet = String // error: type String outside bounds <: Test.this.FlagSet

object Flag {
def make(s: String): Flag = s
}

val f: Flag = Flag.make("hello")
val g: FlagSet = f

}
2 changes: 1 addition & 1 deletion tests/neg/opaque-bounds.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ class Test { // error: class Test cannot be instantiated

opaque type FlagSet = Int

opaque type Flag <: FlagSet = String // error: type String outside bounds <: Test.this.FlagSet
opaque type Flag <: FlagSet = String

object Flag {
def make(s: String): Flag = s
Expand Down
12 changes: 12 additions & 0 deletions tests/pos/recursive-opaques.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
object Syms:
import SymDs.*
opaque type Symbol <: AnyRef
= SymDenotation
opaque type ClassSymbol <: Symbol
= ClassDenotation

object SymDs:
import Syms.*
class SymDenotation(sym: Symbol)
class ClassDenotation(sym: Symbol) extends SymDenotation(sym)