Skip to content

Commit 461c477

Browse files
committed
Fix #4098: Check that refinements have good bounds
So far, we did the check only for member symbols. We have to do the same thing for type refinements that do not refer to a member.
1 parent 99b10eb commit 461c477

File tree

2 files changed

+41
-7
lines changed

2 files changed

+41
-7
lines changed

Diff for: compiler/src/dotty/tools/dotc/core/CheckRealizable.scala

+25-7
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,8 @@ object CheckRealizable {
3030
class NotFinal(sym: Symbol)(implicit ctx: Context)
3131
extends Realizability(i" refers to nonfinal $sym")
3232

33-
class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context)
34-
extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}")
33+
class HasProblemBounds(name: Name, info: Type)(implicit ctx: Context)
34+
extends Realizability(i" has a member $name with possibly conflicting bounds ${info.bounds.lo} <: ... <: ${info.bounds.hi}")
3535

3636
class HasProblemBaseArg(typ: Type, argBounds: TypeBounds)(implicit ctx: Context)
3737
extends Realizability(i" has a base type $typ with possibly conflicting parameter bounds ${argBounds.lo} <: ... <: ${argBounds.hi}")
@@ -96,6 +96,14 @@ class CheckRealizable(implicit ctx: Context) {
9696
else boundsRealizability(tp).andAlso(memberRealizability(tp))
9797
}
9898

99+
private def refinedNames(tp: Type): Set[Name] = tp.dealias match {
100+
case tp: RefinedType => refinedNames(tp.parent) + tp.refinedName
101+
case tp: AndType => refinedNames(tp.tp1) ++ refinedNames(tp.tp2)
102+
case tp: OrType => refinedNames(tp.tp1) ++ refinedNames(tp.tp2)
103+
case tp: TypeProxy => refinedNames(tp.underlying)
104+
case _ => Set.empty
105+
}
106+
99107
/** `Realizable` if `tp` has good bounds, a `HasProblem...` instance
100108
* pointing to a bad bounds member otherwise. "Has good bounds" means:
101109
*
@@ -107,12 +115,21 @@ class CheckRealizable(implicit ctx: Context) {
107115
* also lead to base types with bad bounds).
108116
*/
109117
private def boundsRealizability(tp: Type) = {
110-
val mbrProblems =
118+
119+
val memberProblems =
111120
for {
112121
mbr <- tp.nonClassTypeMembers
113122
if !(mbr.info.loBound <:< mbr.info.hiBound)
114123
}
115-
yield new HasProblemBounds(mbr)
124+
yield new HasProblemBounds(mbr.name, mbr.info)
125+
126+
val refinementProblems =
127+
for {
128+
name <- refinedNames(tp)
129+
mbr <- tp.member(name).alternatives
130+
if !(mbr.info.loBound <:< mbr.info.hiBound)
131+
}
132+
yield new HasProblemBounds(name, mbr.info)
116133

117134
def baseTypeProblems(base: Type) = base match {
118135
case AndType(base1, base2) =>
@@ -126,12 +143,13 @@ class CheckRealizable(implicit ctx: Context) {
126143
val baseProblems =
127144
tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems)
128145

129-
(((Realizable: Realizability)
130-
/: mbrProblems)(_ andAlso _)
146+
((((Realizable: Realizability)
147+
/: memberProblems)(_ andAlso _)
148+
/: refinementProblems)(_ andAlso _)
131149
/: baseProblems)(_ andAlso _)
132150
}
133151

134-
/** `Realizable` if all of `tp`'s non-struct fields have realizable types,
152+
/** `Realizable` if all of `tp`'s non-strict fields have realizable types,
135153
* a `HasProblemField` instance pointing to a bad field otherwise.
136154
*/
137155
private def memberRealizability(tp: Type) = {

Diff for: tests/neg/i4098.scala

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
object App {
2+
import scala.reflect.Selectable.reflectiveSelectable
3+
4+
def coerce[U, V](u: U): V = {
5+
type X = { type R >: U }
6+
type Y = { type R = V }
7+
type Z = X & Y
8+
val u1: Z#R = u // error: not a legal path
9+
u1
10+
}
11+
12+
def main(args: Array[String]): Unit = {
13+
val x: Int = coerce[String, Int]("a")
14+
println(x + 1)
15+
}
16+
}

0 commit comments

Comments
 (0)