diff --git a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala index 05925421bdb0..bac81942f8e3 100644 --- a/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala +++ b/compiler/src/dotty/tools/dotc/core/CheckRealizable.scala @@ -30,8 +30,8 @@ object CheckRealizable { class NotFinal(sym: Symbol)(implicit ctx: Context) extends Realizability(i" refers to nonfinal $sym") - class HasProblemBounds(typ: SingleDenotation)(implicit ctx: Context) - extends Realizability(i" has a member $typ with possibly conflicting bounds ${typ.info.bounds.lo} <: ... <: ${typ.info.bounds.hi}") + class HasProblemBounds(name: Name, info: Type)(implicit ctx: Context) + extends Realizability(i" has a member $name with possibly conflicting bounds ${info.bounds.lo} <: ... <: ${info.bounds.hi}") class HasProblemBaseArg(typ: Type, argBounds: TypeBounds)(implicit ctx: Context) 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) { else boundsRealizability(tp).andAlso(memberRealizability(tp)) } + private def refinedNames(tp: Type): Set[Name] = tp.dealias match { + case tp: RefinedType => refinedNames(tp.parent) + tp.refinedName + case tp: AndType => refinedNames(tp.tp1) ++ refinedNames(tp.tp2) + case tp: OrType => refinedNames(tp.tp1) ++ refinedNames(tp.tp2) + case tp: TypeProxy => refinedNames(tp.underlying) + case _ => Set.empty + } + /** `Realizable` if `tp` has good bounds, a `HasProblem...` instance * pointing to a bad bounds member otherwise. "Has good bounds" means: * @@ -107,12 +115,22 @@ class CheckRealizable(implicit ctx: Context) { * also lead to base types with bad bounds). */ private def boundsRealizability(tp: Type) = { - val mbrProblems = + + val memberProblems = for { mbr <- tp.nonClassTypeMembers if !(mbr.info.loBound <:< mbr.info.hiBound) } - yield new HasProblemBounds(mbr) + yield new HasProblemBounds(mbr.name, mbr.info) + + val refinementProblems = + for { + name <- refinedNames(tp) + if (name.isTypeName) + mbr <- tp.member(name).alternatives + if !(mbr.info.loBound <:< mbr.info.hiBound) + } + yield new HasProblemBounds(name, mbr.info) def baseTypeProblems(base: Type) = base match { case AndType(base1, base2) => @@ -126,12 +144,13 @@ class CheckRealizable(implicit ctx: Context) { val baseProblems = tp.baseClasses.map(_.baseTypeOf(tp)).flatMap(baseTypeProblems) - (((Realizable: Realizability) - /: mbrProblems)(_ andAlso _) + ((((Realizable: Realizability) + /: memberProblems)(_ andAlso _) + /: refinementProblems)(_ andAlso _) /: baseProblems)(_ andAlso _) } - /** `Realizable` if all of `tp`'s non-struct fields have realizable types, + /** `Realizable` if all of `tp`'s non-strict fields have realizable types, * a `HasProblemField` instance pointing to a bad field otherwise. */ private def memberRealizability(tp: Type) = { diff --git a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala index f108c15b2d8d..e14b7f122b6e 100644 --- a/compiler/src/dotty/tools/dotc/typer/Dynamic.scala +++ b/compiler/src/dotty/tools/dotc/typer/Dynamic.scala @@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.{Name, TermName} import dotty.tools.dotc.core.StdNames._ import dotty.tools.dotc.core.Types._ import dotty.tools.dotc.core.Decorators._ +import util.Positions._ import core.Symbols._ import core.Definitions import Inferencing._ @@ -49,7 +50,7 @@ trait Dynamic { self: Typer with Applications => * foo.bar[T0, ...](x = bazX, y = bazY, baz, ...) ~~> foo.applyDynamicNamed[T0, ...]("bar")(("x", bazX), ("y", bazY), ("", baz), ...) */ def typedDynamicApply(tree: untpd.Apply, pt: Type)(implicit ctx: Context): Tree = { - def typedDynamicApply(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = { + def typedDynamicApply(qual: untpd.Tree, name: Name, selPos: Position, targs: List[untpd.Tree]): Tree = { def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false } val args = tree.args val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic @@ -62,19 +63,19 @@ trait Dynamic { self: Typer with Applications => case arg => namedArgTuple("", arg) } val args1 = if (dynName == nme.applyDynamic) args else namedArgs - typedApply(untpd.Apply(coreDynamic(qual, dynName, name, targs), args1), pt) + typedApply(untpd.Apply(coreDynamic(qual, dynName, name, selPos, targs), args1), pt) } } tree.fun match { - case Select(qual, name) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, Nil) - case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => - typedDynamicApply(qual, name, targs) + case sel @ Select(qual, name) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, sel.pos, Nil) + case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) => + typedDynamicApply(qual, name, sel.pos, targs) case TypeApply(fun, targs) => - typedDynamicApply(fun, nme.apply, targs) + typedDynamicApply(fun, nme.apply, fun.pos, targs) case fun => - typedDynamicApply(fun, nme.apply, Nil) + typedDynamicApply(fun, nme.apply, fun.pos, Nil) } } @@ -86,26 +87,26 @@ trait Dynamic { self: Typer with Applications => * through an existing transformation of in typedAssign [foo.bar(baz) = quux ~~> foo.bar.update(baz, quux)]. */ def typedDynamicSelect(tree: untpd.Select, targs: List[Tree], pt: Type)(implicit ctx: Context): Tree = - typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, targs), pt) + typedApply(coreDynamic(tree.qualifier, nme.selectDynamic, tree.name, tree.pos, targs), pt) /** Translate selection that does not typecheck according to the normal rules into a updateDynamic. * foo.bar = baz ~~> foo.updateDynamic(bar)(baz) */ def typedDynamicAssign(tree: untpd.Assign, pt: Type)(implicit ctx: Context): Tree = { - def typedDynamicAssign(qual: untpd.Tree, name: Name, targs: List[untpd.Tree]): Tree = - typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, targs), tree.rhs), pt) + def typedDynamicAssign(qual: untpd.Tree, name: Name, selPos: Position, targs: List[untpd.Tree]): Tree = + typedApply(untpd.Apply(coreDynamic(qual, nme.updateDynamic, name, selPos, targs), tree.rhs), pt) tree.lhs match { - case Select(qual, name) if !isDynamicMethod(name) => - typedDynamicAssign(qual, name, Nil) - case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) => - typedDynamicAssign(qual, name, targs) + case sel @ Select(qual, name) if !isDynamicMethod(name) => + typedDynamicAssign(qual, name, sel.pos, Nil) + case TypeApply(sel @ Select(qual, name), targs) if !isDynamicMethod(name) => + typedDynamicAssign(qual, name, sel.pos, targs) case _ => errorTree(tree, ReassignmentToVal(tree.lhs.symbol.name)) } } - private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = { - val select = untpd.Select(qual, dynName) + private def coreDynamic(qual: untpd.Tree, dynName: Name, name: Name, selPos: Position, targs: List[untpd.Tree])(implicit ctx: Context): untpd.Apply = { + val select = untpd.Select(qual, dynName).withPos(selPos) val selectWithTypes = if (targs.isEmpty) select else untpd.TypeApply(select, targs) @@ -134,7 +135,7 @@ trait Dynamic { self: Typer with Applications => def structuralCall(selectorName: TermName, formals: List[Tree]) = { val selectable = adapt(qual, defn.SelectableType) val scall = untpd.Apply( - untpd.TypedSplice(selectable.select(selectorName)), + untpd.TypedSplice(selectable.select(selectorName)).withPos(tree.pos), (Literal(Constant(name.toString)) :: formals).map(untpd.TypedSplice(_))) typed(scall) } diff --git a/tests/neg/i4098.scala b/tests/neg/i4098.scala new file mode 100644 index 000000000000..0d2be82b17ae --- /dev/null +++ b/tests/neg/i4098.scala @@ -0,0 +1,16 @@ +object App { + import scala.reflect.Selectable.reflectiveSelectable + + def coerce[U, V](u: U): V = { + type X = { type R >: U } + type Y = { type R = V } + type Z = X & Y + val u1: Z#R = u // error: not a legal path + u1 + } + + def main(args: Array[String]): Unit = { + val x: Int = coerce[String, Int]("a") + println(x + 1) + } +} diff --git a/tests/neg/i4098a.scala b/tests/neg/i4098a.scala new file mode 100644 index 000000000000..71da92830a84 --- /dev/null +++ b/tests/neg/i4098a.scala @@ -0,0 +1,16 @@ +object App { + import scala.reflect.Selectable.reflectiveSelectable + + def coerce[U, V](u: U): V = { + type X = { val x: { type R >: U } } + type Y = { val x: { type R = V } } + lazy val z: X & Y = z + val u1: z.x.R = u // error: Object { type R >: U | V <: V } is not stable (with arrows under z.x) + u1 + } + + def main(args: Array[String]): Unit = { + val x: Int = coerce[String, Int]("a") + println(x + 1) + } +}