Skip to content

Remove false unchecked warnings on refined types #14703

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
Mar 21, 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
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1764,11 +1764,11 @@ class TypeComparer(@constructorOnly initctx: Context) extends ConstraintHandling
case _ => false

/** Does type `tp1` have a member with name `name` whose normalized type is a subtype of
* the normalized type of the refinement `tp2`?
* the normalized type of the refinement of `tp2`?
* Normalization is as follows: If `tp2` contains a skolem to its refinement type,
* rebase both itself and the member info of `tp` on a freshly created skolem type.
*/
protected def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType): Boolean =
trace(i"hasMatchingMember($tp1 . $name :? ${tp2.refinedInfo}), mbr: ${tp1.member(name).info}", subtyping) {

def qualifies(m: SingleDenotation): Boolean =
Expand Down Expand Up @@ -2750,6 +2750,9 @@ object TypeComparer {
def matchesType(tp1: Type, tp2: Type, relaxed: Boolean)(using Context): Boolean =
comparing(_.matchesType(tp1, tp2, relaxed))

def hasMatchingMember(name: Name, tp1: Type, tp2: RefinedType)(using Context): Boolean =
comparing(_.hasMatchingMember(name, tp1, tp2))

def matchingMethodParams(tp1: MethodType, tp2: MethodType)(using Context): Boolean =
comparing(_.matchingMethodParams(tp1, tp2))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ object TypeTestsCasts {
case AndType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
case OrType(tp1, tp2) => recur(X, tp1) && recur(X, tp2)
case AnnotatedType(t, _) => recur(X, t)
case _: RefinedType => false
case tp2: RefinedType => recur(X, tp2.parent) && TypeComparer.hasMatchingMember(tp2.refinedName, X, tp2)
case _ => true
})

Expand Down
22 changes: 22 additions & 0 deletions tests/neg-custom-args/isInstanceOf/refined-types.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class A
class B extends A
type AA = A { type T = Int }
type BA = B { type T = Int }
type AL = A { type T >: Int }
type BL = B { type T >: Int }
type AU = A { type T <: Int }
type BU = B { type T <: Int }

def aa(x: AA) = x.isInstanceOf[BA] // was: the type test for BA cannot be checked at runtime
def al(x: AL) = x.isInstanceOf[BL] // was: the type test for BL cannot be checked at runtime
def au(x: AU) = x.isInstanceOf[BU] // was: the type test for BU cannot be checked at runtime

// an alias leaves nothing unchecked when type testing against one bound:
def bl(x: AA) = x.isInstanceOf[BL] // was: the type test for BL cannot be checked at runtime
def bu(x: AA) = x.isInstanceOf[BU] // was: the type test for BU cannot be checked at runtime

// but static knowledge of only one bound makes checking against an alias unchecked:
def al_ba(x: AL) = x.isInstanceOf[BA] // error: the type test for BA cannot be checked at runtime
def au_ba(x: AU) = x.isInstanceOf[BA] // error: the type test for BA cannot be checked at runtime
def al_bu(x: AL) = x.isInstanceOf[BU] // error: the type test for BU cannot be checked at runtime
def au_bl(x: AU) = x.isInstanceOf[BL] // error: the type test for BL cannot be checked at runtime