Skip to content

Commit bd87107

Browse files
authored
Merge pull request #4122 from dotty-staging/fix-#4098
Fix #4098: Check that refinements have good bounds
2 parents e82d606 + 3f3f1ba commit bd87107

File tree

4 files changed

+77
-25
lines changed

4 files changed

+77
-25
lines changed

compiler/src/dotty/tools/dotc/core/CheckRealizable.scala

Lines changed: 26 additions & 7 deletions
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,22 @@ 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+
if (name.isTypeName)
130+
mbr <- tp.member(name).alternatives
131+
if !(mbr.info.loBound <:< mbr.info.hiBound)
132+
}
133+
yield new HasProblemBounds(name, mbr.info)
116134

117135
def baseTypeProblems(base: Type) = base match {
118136
case AndType(base1, base2) =>
@@ -126,12 +144,13 @@ class CheckRealizable(implicit ctx: Context) {
126144
val baseProblems =
127145
tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems)
128146

129-
(((Realizable: Realizability)
130-
/: mbrProblems)(_ andAlso _)
147+
((((Realizable: Realizability)
148+
/: memberProblems)(_ andAlso _)
149+
/: refinementProblems)(_ andAlso _)
131150
/: baseProblems)(_ andAlso _)
132151
}
133152

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

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.{Name, TermName}
1111
import dotty.tools.dotc.core.StdNames._
1212
import dotty.tools.dotc.core.Types._
1313
import dotty.tools.dotc.core.Decorators._
14+
import util.Positions._
1415
import core.Symbols._
1516
import core.Definitions
1617
import Inferencing._
@@ -49,7 +50,7 @@ trait Dynamic { self: Typer with Applications =>
4950
* foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...)
5051
*/
5152
def typedDynamicApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = {
52-
def typedDynamicApply(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = {
53+
def typedDynamicApply(qual: untpd.Tree, name: Name, selPos: Position, targs: List[untpd.Tree]): Tree = {
5354
def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
5455
val args = tree.args
5556
val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
@@ -62,19 +63,19 @@ trait Dynamic { self: Typer with Applications =>
6263
case arg => namedArgTuple("", arg)
6364
}
6465
val args1 = if (dynName == nme.applyDynamic) args else namedArgs
65-
typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targs), args1), pt)
66+
typedApply(untpd.Apply(coreDynamic(qual, dynName, name, selPos, targs), args1), pt)
6667
}
6768
}
6869

6970
tree.fun match {
70-
case Select(qual, name) if !isDynamicMethod(name) =>
71-
typedDynamicApply(qual, name, Nil)
72-
case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
73-
typedDynamicApply(qual, name, targs)
71+
case sel @ Select(qual, name) if !isDynamicMethod(name) =>
72+
typedDynamicApply(qual, name, sel.pos, Nil)
73+
case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) =>
74+
typedDynamicApply(qual, name, sel.pos, targs)
7475
case TypeApply(fun, targs) =>
75-
typedDynamicApply(fun, nme.apply, targs)
76+
typedDynamicApply(fun, nme.apply, fun.pos, targs)
7677
case fun =>
77-
typedDynamicApply(fun, nme.apply, Nil)
78+
typedDynamicApply(fun, nme.apply, fun.pos, Nil)
7879
}
7980
}
8081

@@ -86,26 +87,26 @@ trait Dynamic { self: Typer with Applications =>
8687
* through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)].
8788
*/
8889
def typedDynamicSelect(tree: untpd.Select, targs: List[Tree], pt: Type)(implicit ctx: Context): Tree =
89-
typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, targs), pt)
90+
typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, tree.pos, targs), pt)
9091

9192
/** Translate selection that does not typecheck according to the normal rules into a updateDynamic.
9293
* foo.bar = baz ~~> foo.updateDynamic(bar)(baz)
9394
*/
9495
def typedDynamicAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = {
95-
def typedDynamicAssign(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree =
96-
typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targs), tree.rhs), pt)
96+
def typedDynamicAssign(qual: untpd.Tree, name: Name, selPos: Position, targs: List[untpd.Tree]): Tree =
97+
typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, selPos, targs), tree.rhs), pt)
9798
tree.lhs match {
98-
case Select(qual, name) if !isDynamicMethod(name) =>
99-
typedDynamicAssign(qual, name, Nil)
100-
case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
101-
typedDynamicAssign(qual, name, targs)
99+
case sel @ Select(qual, name) if !isDynamicMethod(name) =>
100+
typedDynamicAssign(qual, name, sel.pos, Nil)
101+
case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) =>
102+
typedDynamicAssign(qual, name, sel.pos, targs)
102103
case _ =>
103104
errorTree(tree, ReassignmentToVal(tree.lhs.symbol.name))
104105
}
105106
}
106107

107-
private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = {
108-
val select = untpd.Select(qual, dynName)
108+
private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, selPos: Position, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = {
109+
val select = untpd.Select(qual, dynName).withPos(selPos)
109110
val selectWithTypes =
110111
if (targs.isEmpty) select
111112
else untpd.TypeApply(select, targs)
@@ -134,7 +135,7 @@ trait Dynamic { self: Typer with Applications =>
134135
def structuralCall(selectorName: TermName, formals: List[Tree]) = {
135136
val selectable = adapt(qual, defn.SelectableType)
136137
val scall = untpd.Apply(
137-
untpd.TypedSplice(selectable.select(selectorName)),
138+
untpd.TypedSplice(selectable.select(selectorName)).withPos(tree.pos),
138139
(Literal(Constant(name.toString)) :: formals).map(untpd.TypedSplice(_)))
139140
typed(scall)
140141
}

tests/neg/i4098.scala

Lines changed: 16 additions & 0 deletions
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+
}

tests/neg/i4098a.scala

Lines changed: 16 additions & 0 deletions
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 = { val x: { type R >: U } }
6+
type Y = { val x: { type R = V } }
7+
lazy val z: X & Y = z
8+
val u1: z.x.R = u // error: Object { type R >: U | V <: V } is not stable (with arrows under z.x)
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)