Skip to content

Commit

Permalink
Reject all explicitly written type references with bad bounds
Browse files Browse the repository at this point in the history
... except for type parameters, since these will need to be instantiated
with good types themselves. Fixes further unsoundness examples added to
neg/i15569.scala.
  • Loading branch information
odersky committed Jul 4, 2022
1 parent 298ef15 commit 7104d2e
Show file tree
Hide file tree
Showing 11 changed files with 43 additions and 32 deletions.
10 changes: 3 additions & 7 deletions compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,8 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
ctx
super.transform(tree)(using gadtCtx)
case tree: Ident =>
if tree.isType then
if tree.isType && !tree.symbol.is(Param) then
Checking.checkGoodBounds(tree.tpe, tree.srcPos)
checkNotPackage(tree)
else
if tree.symbol.is(Inline) && !Inlines.inInlineMethod then
Expand All @@ -295,6 +296,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
ctx.compilationUnit.needsInlining = true
if name.isTypeName then
Checking.checkRealizable(qual.tpe, qual.srcPos)
Checking.checkGoodBounds(tree.tpe, tree.srcPos)
withMode(Mode.Type)(super.transform(checkNotPackage(tree)))
else
checkNoConstructorProxy(tree)
Expand Down Expand Up @@ -345,12 +347,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
for arg <- args do
checkInferredWellFormed(arg)
val isInferred = arg.isInstanceOf[InferredTypeTree] || arg.span.isSynthetic
if !isInferred then
// only check explicit type arguments. We rely on inferred type arguments
// to either have good bounds (if they come from a constraint), or be derived
// from values that recursively need to have good bounds.
Checking.checkGoodBounds(arg.tpe, arg.srcPos)
if (fn.symbol != defn.ChildAnnot.primaryConstructor)
// Make an exception for ChildAnnot, which should really have AnyKind bounds
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])
Expand Down
10 changes: 5 additions & 5 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ object Checking {
checkBounds(args, tl.paramInfos, _.substParams(tl, _))

def checkGoodBounds(tpe: Type, pos: SrcPos)(using Context): Boolean =
def recur(tp: Type) = tp.dealias match
def recur(tp: Type): Boolean = tp.dealias match
case tp: TypeRef =>
checkGoodBounds(tp.info, pos)
case TypeBounds(lo, hi) if !(lo <:< hi) =>
val argStr = if tp eq tpe then "" else i" $tpe"
report.error(i"type argument$argStr has potentially unrealizable bounds $tp", pos)
recur(tp.info)
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
val tpStr = if tp eq tpe then "" else i" $tpe"
report.error(i"type$tpStr has potentially conflicting bounds $tp", pos)
false
case _ =>
true
Expand Down
5 changes: 0 additions & 5 deletions tests/init/neg/i5854.scala

This file was deleted.

5 changes: 0 additions & 5 deletions tests/neg-custom-args/fatal-warnings/i13820.scala

This file was deleted.

8 changes: 4 additions & 4 deletions tests/neg-strict/i1050.scala
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ object Indirect {
trait U {
trait X {
val q: A & B = ???
type M = q.L
type M = q.L // error: conflicting bounds
}
final lazy val p: X = ???
def brand(x: Any): p.M = x // error: conflicting bounds
Expand All @@ -119,7 +119,7 @@ object Indirect2 {
}
trait X {
val q: Y = ???
type M = q.r.L
type M = q.r.L // error: conflicting bounds
}
final lazy val p: X = ???
def brand(x: Any): p.M = x // error: conflicting bounds
Expand Down Expand Up @@ -152,7 +152,7 @@ object Rec2 {
}
trait X {
val q: Y = ???
type M = q.r.L
type M = q.r.L // error: conflicting bounds
}
final lazy val p: X = ???
def brand(x: Any): p.M = x // error: conflicting bounds
Expand All @@ -171,7 +171,7 @@ object Indirect3 {
}
trait X {
val q: Y = ???
type M = q.r.L
type M = q.r.L // error: conflicting bounds
}
final lazy val p: X = ???
def brand(x: Any): p.M = x // error: conflicting bounds
Expand Down
6 changes: 3 additions & 3 deletions tests/init/neg/i11572.scala → tests/neg/i11572.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ class A {
trait Bounded {
type T >: Cov[Int] <: Cov[String]
}
val t: Bounded = new Bounded { // error
val t: Bounded = new Bounded {
// Note: using this instead of t produces an error (as expected)
override type T >: t.T <: t.T
override type T >: t.T <: t.T // error // error (conflicting bounds)
}

val covInt = new Cov[Int] {
override def get: Int = 3
}
val str: String = ((covInt: t.T): Cov[String]).get // ClassCastException: class Integer cannot be cast to class String
val str: String = ((covInt: t.T): Cov[String]).get // error, was ClassCastException: class Integer cannot be cast to class String
}
5 changes: 5 additions & 0 deletions tests/neg/i13820.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
trait Expr { type T }

def foo[A](e: Expr { type T = A }) = e match
case e1: Expr { type T <: Int } =>
val i: Int = ??? : e1.T // error: unrealizable bounds
2 changes: 1 addition & 1 deletion tests/neg/i15568.check
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
-- Error: tests/neg/i15568.scala:3:15 ----------------------------------------------------------------------------------
3 |type Bar = Foo[? >: Int <: String] // error
| ^
| type argument has potentially unrealizable bounds >: Int <: String
| type has potentially conflicting bounds >: Int <: String
15 changes: 15 additions & 0 deletions tests/neg/i15569.scala
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,18 @@ def andThenSub[A, B, C](f: A <:< B, g: B <:< C): A <:< C =
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
unsound("hi :)")

@main def Test3 = (None: Option[Foo[?]]) match {
case _: Option[Foo[t]] =>
val unsound: Nothing = (5 : Any) : t // error
(unsound : Unit => Unit).apply(())
}

@main def Test4 =
type t >: Any <: Nothing
val unsound: Nothing = (5 : Any) : t // error
(unsound : Unit => Unit).apply(())

@main def Test5 =
type t >: Any <: Nothing
val unsound: List[Nothing] = List(5 : Any) : List[t] // error
(unsound.head : Unit => Unit).apply(())
5 changes: 5 additions & 0 deletions tests/neg/i5854.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class B {
val a: String = (((1: Any): b.A): Nothing): String // error
val b: { type A >: Any <: Nothing } = loop()
def loop(): Nothing = loop()
}
4 changes: 2 additions & 2 deletions tests/pos/i13820.scala
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
trait Expr { type T }

def foo[A](e: Expr { type T = A }) = e match
def foo[A <: Int](e: Expr { type T = A }) = e match
case e1: Expr { type T <: Int } =>
val i: Int = ??? : e1.T
val i: Int = ??? : e1.T

0 comments on commit 7104d2e

Please sign in to comment.