Skip to content

Commit 298ef15

Browse files
authored
Merge pull request #15571 from dotty-staging/fix-15569
Check type arguments for bad bounds
2 parents cde3d13 + 745236a commit 298ef15

File tree

7 files changed

+50
-7
lines changed

7 files changed

+50
-7
lines changed

compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import util.SimpleIdentityMap
1414

1515
import collection.mutable
1616

17-
/** A utility object offering methods for rewriting inlined code */
17+
/** A utility class offering methods for rewriting inlined code */
1818
class InlineReducer(inliner: Inliner)(using Context):
1919
import tpd.*
2020
import Inliner.{isElideableExpr, DefBuffer}

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

+9-1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import Decorators._
1414
import Symbols._, SymUtils._, NameOps._
1515
import ContextFunctionResults.annotateContextResults
1616
import config.Printers.typr
17+
import util.SrcPos
1718
import reporting._
1819

1920
object PostTyper {
@@ -342,7 +343,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
342343
if tree.symbol.is(Inline) then
343344
ctx.compilationUnit.needsInlining = true
344345
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
345-
args.foreach(checkInferredWellFormed)
346+
for arg <- args do
347+
checkInferredWellFormed(arg)
348+
val isInferred = arg.isInstanceOf[InferredTypeTree] || arg.span.isSynthetic
349+
if !isInferred then
350+
// only check explicit type arguments. We rely on inferred type arguments
351+
// to either have good bounds (if they come from a constraint), or be derived
352+
// from values that recursively need to have good bounds.
353+
Checking.checkGoodBounds(arg.tpe, arg.srcPos)
346354
if (fn.symbol != defn.ChildAnnot.primaryConstructor)
347355
// Make an exception for ChildAnnot, which should really have AnyKind bounds
348356
Checking.checkBounds(args, fn.tpe.widen.asInstanceOf[PolyType])

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

+17-4
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,11 @@ object Checking {
7373
showInferred(MissingTypeParameterInTypeApp(arg.tpe), app, tpt))
7474
}
7575
for (arg, which, bound) <- TypeOps.boundsViolations(args, boundss, instantiate, app) do
76-
report.error(
77-
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
78-
app, tpt),
79-
arg.srcPos.focus)
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)
8081

8182
/** Check that type arguments `args` conform to corresponding bounds in `tl`
8283
* Note: This does not check the bounds of AppliedTypeTrees. These
@@ -85,6 +86,18 @@ object Checking {
8586
def checkBounds(args: List[tpd.Tree], tl: TypeLambda)(using Context): Unit =
8687
checkBounds(args, tl.paramInfos, _.substParams(tl, _))
8788

89+
def checkGoodBounds(tpe: Type, pos: SrcPos)(using Context): Boolean =
90+
def recur(tp: Type) = tp.dealias match
91+
case tp: TypeRef =>
92+
checkGoodBounds(tp.info, pos)
93+
case TypeBounds(lo, hi) if !(lo <:< hi) =>
94+
val argStr = if tp eq tpe then "" else i" $tpe"
95+
report.error(i"type argument$argStr has potentially unrealizable bounds $tp", pos)
96+
false
97+
case _ =>
98+
true
99+
recur(tpe)
100+
88101
/** Check applied type trees for well-formedness. This means
89102
* - all arguments are within their corresponding bounds
90103
* - if type is a higher-kinded application with wildcard arguments,

tests/neg/i15568.check

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
-- Error: tests/neg/i15568.scala:3:15 ----------------------------------------------------------------------------------
2+
3 |type Bar = Foo[? >: Int <: String] // error
3+
| ^
4+
| type argument has potentially unrealizable bounds >: Int <: String

tests/neg/i15568.scala

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
trait Foo[X >: Int <: String]
2+
3+
type Bar = Foo[? >: Int <: String] // error

tests/neg/i15569.scala

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
trait Foo[X >: Any <: Nothing]
2+
3+
def andThenSub[A, B, C](f: A <:< B, g: B <:< C): A <:< C =
4+
f.andThen(g)
5+
6+
@main def Test = (None: Option[Foo[?]]) match {
7+
case _: Option[Foo[t]] =>
8+
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
9+
unsound("hi :)")
10+
}
11+
@main def Test2 =
12+
type t >: Any <: Nothing
13+
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
14+
unsound("hi :)")
15+

0 commit comments

Comments
 (0)