Skip to content

Commit cac3d78

Browse files
committed
Flag type creation rather than usage
Flag already creations of type that have possibly conflicting bounds. Creation sites are - type declarations other than type parameters - bindings in type patterns
1 parent 875b9f8 commit cac3d78

File tree

11 files changed

+69
-99
lines changed

11 files changed

+69
-99
lines changed

compiler/src/dotty/tools/dotc/reporting/messages.scala

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1448,9 +1448,16 @@ import transform.SymUtils._
14481448
class DoesNotConformToBound(tpe: Type, which: String, bound: Type)(using Context)
14491449
extends TypeMismatchMsg(
14501450
if which == "lower" then bound else tpe,
1451-
if which == "lower" then tpe else bound)(DoesNotConformToBoundID) {
1452-
def msg = em"Type argument ${tpe} does not conform to $which bound $bound"
1453-
}
1451+
if which == "lower" then tpe else bound)(DoesNotConformToBoundID):
1452+
private def isBounds = tpe match
1453+
case TypeBounds(lo, hi) => lo ne hi
1454+
case _ => false
1455+
override def canExplain = !isBounds
1456+
def msg =
1457+
if isBounds then
1458+
em"Type argument ${tpe} does not overlap with $which bound $bound"
1459+
else
1460+
em"Type argument ${tpe} does not conform to $which bound $bound"
14541461

14551462
class DoesNotConformToSelfType(category: String, selfType: Type, cls: Symbol,
14561463
otherSelf: Type, relation: String, other: Symbol)(

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

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import ContextFunctionResults.annotateContextResults
1616
import config.Printers.typr
1717
import util.SrcPos
1818
import reporting._
19+
import NameKinds.WildcardParamName
1920

2021
object PostTyper {
2122
val name: String = "posttyper"
@@ -280,8 +281,7 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
280281
ctx
281282
super.transform(tree)(using gadtCtx)
282283
case tree: Ident =>
283-
if tree.isType && !tree.symbol.is(Param) then
284-
Checking.checkGoodBounds(tree.tpe, tree.srcPos)
284+
if tree.isType then
285285
checkNotPackage(tree)
286286
else
287287
if tree.symbol.is(Inline) && !Inlines.inInlineMethod then
@@ -296,7 +296,6 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
296296
ctx.compilationUnit.needsInlining = true
297297
if name.isTypeName then
298298
Checking.checkRealizable(qual.tpe, qual.srcPos)
299-
Checking.checkGoodBounds(tree.tpe, tree.srcPos)
300299
withMode(Mode.Type)(super.transform(checkNotPackage(tree)))
301300
else
302301
checkNoConstructorProxy(tree)
@@ -399,13 +398,20 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
399398
val reference = ctx.settings.sourceroot.value
400399
val relativePath = util.SourceFile.relativePath(ctx.compilationUnit.source, reference)
401400
sym.addAnnotation(Annotation.makeSourceFile(relativePath))
402-
else (tree.rhs, sym.info) match
403-
case (rhs: LambdaTypeTree, bounds: TypeBounds) =>
404-
VarianceChecker.checkLambda(rhs, bounds)
405-
if sym.isOpaqueAlias then
406-
VarianceChecker.checkLambda(rhs, TypeBounds.upper(sym.opaqueAlias))
407-
case _ =>
401+
else
402+
if !sym.is(Param) then
403+
Checking.checkGoodBounds(tree.symbol)
404+
(tree.rhs, sym.info) match
405+
case (rhs: LambdaTypeTree, bounds: TypeBounds) =>
406+
VarianceChecker.checkLambda(rhs, bounds)
407+
if sym.isOpaqueAlias then
408+
VarianceChecker.checkLambda(rhs, TypeBounds.upper(sym.opaqueAlias))
409+
case _ =>
408410
processMemberDef(super.transform(tree))
411+
case tree: Bind =>
412+
if tree.symbol.isType && !tree.symbol.name.is(WildcardParamName) then
413+
Checking.checkGoodBounds(tree.symbol)
414+
super.transform(tree)
409415
case tree: New if isCheckable(tree) =>
410416
Checking.checkInstantiable(tree.tpe, tree.srcPos)
411417
super.transform(tree)

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

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,10 @@ object Checking {
7373
showInferred(MissingTypeParameterInTypeApp(arg.tpe), app, tpt))
7474
}
7575
for (arg, which, bound) <- TypeOps.boundsViolations(args, boundss, instantiate, app) do
76-
if checkGoodBounds(arg.tpe, arg.srcPos.focus) then
77-
report.error(
78-
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
79-
app, tpt),
80-
arg.srcPos.focus)
76+
report.error(
77+
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
78+
app, tpt),
79+
arg.srcPos.focus)
8180

8281
/** Check that type arguments `args` conform to corresponding bounds in `tl`
8382
* Note: This does not check the bounds of AppliedTypeTrees. These
@@ -86,17 +85,19 @@ object Checking {
8685
def checkBounds(args: List[tpd.Tree], tl: TypeLambda)(using Context): Unit =
8786
checkBounds(args, tl.paramInfos, _.substParams(tl, _))
8887

89-
def checkGoodBounds(tpe: Type, pos: SrcPos)(using Context): Boolean =
90-
def recur(tp: Type): Boolean = tp.dealias match
91-
case tp: TypeRef =>
92-
recur(tp.info)
93-
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) =>
94-
val tpStr = if tp eq tpe then "" else i" $tpe"
95-
report.error(i"type$tpStr has potentially conflicting bounds $tp", pos)
96-
false
97-
case _ =>
98-
true
99-
recur(tpe)
88+
def checkGoodBounds(sym: Symbol)(using Context): Boolean =
89+
val bad = findBadBounds(sym.typeRef)
90+
if bad.exists then
91+
report.error(em"$sym has possibly conflicting bounds $bad", sym.srcPos)
92+
!bad.exists
93+
94+
/** If `tp` dealiases to a typebounds L..H where not L <:< R
95+
* return the potentially conflicting bounds, otherwise return NoType.
96+
*/
97+
private def findBadBounds(tp: Type)(using Context): Type = tp.dealias match
98+
case tp: TypeRef => findBadBounds(tp.info)
99+
case tp @ TypeBounds(lo, hi) if !(lo <:< hi) => tp
100+
case _ => NoType
100101

101102
/** Check applied type trees for well-formedness. This means
102103
* - all arguments are within their corresponding bounds

tests/neg/i11572.scala

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ class A {
33
def get: X
44
}
55
trait Bounded {
6-
type T >: Cov[Int] <: Cov[String]
6+
type T >: Cov[Int] <: Cov[String] // error
77
}
88
val t: Bounded = new Bounded {
99
// Note: using this instead of t produces an error (as expected)
10-
override type T >: t.T <: t.T // error // error (conflicting bounds)
10+
override type T >: t.T <: t.T
1111
}
1212

1313
val covInt = new Cov[Int] {
1414
override def get: Int = 3
1515
}
16-
val str: String = ((covInt: t.T): Cov[String]).get // error, was ClassCastException: class Integer cannot be cast to class String
16+
val str: String = ((covInt: t.T): Cov[String]).get // ClassCastException: class Integer cannot be cast to class String
1717
}

tests/neg/i13820.scala

Lines changed: 0 additions & 5 deletions
This file was deleted.

tests/neg/i15568.check

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
-- Error: tests/neg/i15568.scala:3:15 ----------------------------------------------------------------------------------
1+
-- [E057] Type Mismatch Error: tests/neg/i15568.scala:3:15 -------------------------------------------------------------
22
3 |type Bar = Foo[? >: Int <: String] // error
33
| ^
4-
| type has potentially conflicting bounds >: Int <: String
4+
| Type argument >: Int <: String does not overlap with upper bound String

tests/neg/i15569.scala

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,36 @@ def andThenSub[A, B, C](f: A <:< B, g: B <:< C): A <:< C =
44
f.andThen(g)
55

66
@main def Test = (None: Option[Foo[?]]) match {
7-
case _: Option[Foo[t]] =>
8-
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
7+
case _: Option[Foo[t]] => // error
8+
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon)
99
unsound("hi :)")
1010
}
1111
@main def Test2 =
12-
type t >: Any <: Nothing
13-
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
12+
type t >: Any <: Nothing // error
13+
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon)
1414
unsound("hi :)")
1515

1616
@main def Test3 = (None: Option[Foo[?]]) match {
17-
case _: Option[Foo[t]] =>
18-
val unsound: Nothing = (5 : Any) : t // error
17+
case _: Option[Foo[t]] => // error
18+
val unsound: Nothing = (5 : Any) : t
1919
(unsound : Unit => Unit).apply(())
2020
}
2121

22+
@main def Test3ok = (None: Option[Foo[?]]) match {
23+
case _: Option[Foo[_]] => // ok
24+
}
25+
2226
@main def Test4 =
23-
type t >: Any <: Nothing
24-
val unsound: Nothing = (5 : Any) : t // error
27+
type t >: Any <: Nothing // error
28+
val unsound: Nothing = (5 : Any) : t
2529
(unsound : Unit => Unit).apply(())
2630

2731
@main def Test5 =
28-
type t >: Any <: Nothing
29-
val unsound: List[Nothing] = List(5 : Any) : List[t] // error
32+
type t >: Any <: Nothing // error
33+
val unsound: List[Nothing] = List(5 : Any) : List[t]
34+
(unsound.head : Unit => Unit).apply(())
35+
36+
@main def Test6 =
37+
type t[X] >: Any <: Nothing // error
38+
val unsound: List[Nothing] = List(5 : Any) : List[t[String]]
3039
(unsound.head : Unit => Unit).apply(())

tests/neg/i5854.scala

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
class B {
2-
val a: String = (((1: Any): b.A): Nothing): String // error
3-
val b: { type A >: Any <: Nothing } = loop()
2+
val a: String = (((1: Any): b.A): Nothing): String
3+
val b: { type A >: Any <: Nothing } = loop() // error
44
def loop(): Nothing = loop()
55
}

tests/pos/i13820.scala

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
trait Expr { type T }
22

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

tests/semanticdb/expect/i5854.scala

Lines changed: 0 additions & 11 deletions
This file was deleted.

0 commit comments

Comments
 (0)