Skip to content
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

Check type arguments for bad bounds #15571

Merged
merged 5 commits into from
Jul 4, 2022
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
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/inlines/InlineReducer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import util.SimpleIdentityMap

import collection.mutable

/** A utility object offering methods for rewriting inlined code */
/** A utility class offering methods for rewriting inlined code */
class InlineReducer(inliner: Inliner)(using Context):
import tpd.*
import Inliner.{isElideableExpr, DefBuffer}
Expand Down
10 changes: 9 additions & 1 deletion compiler/src/dotty/tools/dotc/transform/PostTyper.scala
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import Decorators._
import Symbols._, SymUtils._, NameOps._
import ContextFunctionResults.annotateContextResults
import config.Printers.typr
import util.SrcPos
import reporting._

object PostTyper {
Expand Down Expand Up @@ -342,7 +343,14 @@ class PostTyper extends MacroTransform with IdentityDenotTransformer { thisPhase
if tree.symbol.is(Inline) then
ctx.compilationUnit.needsInlining = true
val tree1 @ TypeApply(fn, args) = normalizeTypeArgs(tree)
args.foreach(checkInferredWellFormed)
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
21 changes: 17 additions & 4 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -73,10 +73,11 @@ object Checking {
showInferred(MissingTypeParameterInTypeApp(arg.tpe), app, tpt))
}
for (arg, which, bound) <- TypeOps.boundsViolations(args, boundss, instantiate, app) do
report.error(
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
app, tpt),
arg.srcPos.focus)
if checkGoodBounds(arg.tpe, arg.srcPos.focus) then
report.error(
showInferred(DoesNotConformToBound(arg.tpe, which, bound),
app, tpt),
arg.srcPos.focus)

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

def checkGoodBounds(tpe: Type, pos: SrcPos)(using Context): Boolean =
def recur(tp: Type) = 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)
false
case _ =>
true
recur(tpe)

/** Check applied type trees for well-formedness. This means
* - all arguments are within their corresponding bounds
* - if type is a higher-kinded application with wildcard arguments,
Expand Down
4 changes: 4 additions & 0 deletions tests/neg/i15568.check
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
-- Error: tests/neg/i15568.scala:3:15 ----------------------------------------------------------------------------------
3 |type Bar = Foo[? >: Int <: String] // error
| ^
| type argument has potentially unrealizable bounds >: Int <: String
3 changes: 3 additions & 0 deletions tests/neg/i15568.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
trait Foo[X >: Int <: String]

type Bar = Foo[? >: Int <: String] // error
15 changes: 15 additions & 0 deletions tests/neg/i15569.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
trait Foo[X >: Any <: Nothing]

def andThenSub[A, B, C](f: A <:< B, g: B <:< C): A <:< C =
f.andThen(g)

@main def Test = (None: Option[Foo[?]]) match {
case _: Option[Foo[t]] =>
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
unsound("hi :)")
}
@main def Test2 =
type t >: Any <: Nothing
val unsound: Any <:< Nothing = andThenSub[Any, t, Nothing](summon, summon) // error
unsound("hi :)")