From f468f17a3d7f3db7f29cb1c01d813182684d2eba Mon Sep 17 00:00:00 2001 From: Martin Odersky Date: Sat, 16 Nov 2019 13:22:23 +0100 Subject: [PATCH 1/2] Fix #7526: Don't check class info within bounds when computing denotations When computing a denotation, always assume that ClassInfos override TypeBounds and other ClassInfos. RefChecks will verify later that the overrides are legal. This is needed to prevent infinite recursion involving interleaved subtype checks and denotation computations prompted by findMember. --- .../dotty/tools/dotc/core/Denotations.scala | 6 +++--- .../src/dotty/tools/dotc/core/Types.scala | 20 +++++++++---------- tests/neg/i7526.scala | 18 +++++++++++++++++ tests/neg/override-inner-class.scala | 5 +++++ 4 files changed, 36 insertions(+), 13 deletions(-) create mode 100644 tests/neg/i7526.scala create mode 100644 tests/neg/override-inner-class.scala diff --git a/compiler/src/dotty/tools/dotc/core/Denotations.scala b/compiler/src/dotty/tools/dotc/core/Denotations.scala index e47def78fba1..57c678ad8cb1 100644 --- a/compiler/src/dotty/tools/dotc/core/Denotations.scala +++ b/compiler/src/dotty/tools/dotc/core/Denotations.scala @@ -459,7 +459,7 @@ object Denotations { /** Sym preference provided types also override */ def prefer(sym1: Symbol, sym2: Symbol, info1: Type, info2: Type) = preferSym(sym1, sym2) && - info1.overrides(info2, sym1.matchNullaryLoosely || sym2.matchNullaryLoosely) + info1.overrides(info2, sym1.matchNullaryLoosely || sym2.matchNullaryLoosely, checkClassInfo = false) def handleDoubleDef = if (preferSym(sym1, sym2)) denot1 @@ -600,13 +600,13 @@ object Denotations { case tp1: TypeBounds => tp2 match { case tp2: TypeBounds => if (safeIntersection) tp1 safe_& tp2 else tp1 & tp2 - case tp2: ClassInfo if tp1 contains tp2 => tp2 + case tp2: ClassInfo => tp2 case _ => mergeConflict(sym1, sym2, tp1, tp2) } case tp1: ClassInfo => tp2 match { case tp2: ClassInfo if tp1.cls eq tp2.cls => tp1.derivedClassInfo(tp1.prefix & tp2.prefix) - case tp2: TypeBounds if tp2 contains tp1 => tp1 + case tp2: TypeBounds => tp1 case _ => mergeConflict(sym1, sym2, tp1, tp2) } diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index 7f1554c3dbec..dbc1acfd2903 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -910,21 +910,21 @@ object Types { /** Is this type a legal type for member `sym1` that overrides another * member `sym2` of type `that`? This is the same as `<:<`, except that - * if `matchLoosely` evaluates to true the types `=> T` and `()T` are seen - * as overriding each other. + * @param matchLoosely if true the types `=> T` and `()T` are seen as overriding each other. + * @param checkClassInfo if true we check that ClassInfos are within bounds of abstract types */ - final def overrides(that: Type, matchLoosely: => Boolean)(implicit ctx: Context): Boolean = { + final def overrides(that: Type, matchLoosely: => Boolean, checkClassInfo: Boolean = true)(implicit ctx: Context): Boolean = { def widenNullary(tp: Type) = tp match { case tp @ MethodType(Nil) => tp.resultType case _ => tp } - ((this.widenExpr frozen_<:< that.widenExpr) || - matchLoosely && { - val this1 = widenNullary(this) - val that1 = widenNullary(that) - ((this1 `ne` this) || (that1 `ne` that)) && this1.overrides(that1, matchLoosely = false) - } - ) + this.isInstanceOf[ClassInfo] && !checkClassInfo + || (this.widenExpr frozen_<:< that.widenExpr) + || matchLoosely && { + val this1 = widenNullary(this) + val that1 = widenNullary(that) + ((this1 `ne` this) || (that1 `ne` that)) && this1.overrides(that1, false, checkClassInfo) + } } /** Is this type close enough to that type so that members diff --git a/tests/neg/i7526.scala b/tests/neg/i7526.scala new file mode 100644 index 000000000000..146e8850995d --- /dev/null +++ b/tests/neg/i7526.scala @@ -0,0 +1,18 @@ +type Tr[-I, +O, +A] = I => (O, A) + +trait NetApi with + type Comp + +trait NetDB extends NetApi with + class Comp + +trait NetHelper extends NetApi + +def compQ(name: => String) + : (given n: NetApi) => Tr[Nothing, n.Comp, n.Comp] = ??? + +object net extends NetDB with NetHelper +import net._ +given n: NetApi = net + +val q: Tr[Nothing, Comp, Comp] = compQ("???") // error Found: Tr[Nothing, ?1.Comp, ?1.Comp] Required: Tr[Nothing, net.Comp, net.Comp] \ No newline at end of file diff --git a/tests/neg/override-inner-class.scala b/tests/neg/override-inner-class.scala new file mode 100644 index 000000000000..2ae9a5b3108c --- /dev/null +++ b/tests/neg/override-inner-class.scala @@ -0,0 +1,5 @@ +class C + type T >: String <: Any + +class D extends C + class T // error From aebc8b468d7f3e51b8fe2b15eba95d9dff93c1aa Mon Sep 17 00:00:00 2001 From: Olivier Blanvillain Date: Fri, 6 Dec 2019 16:29:39 +0100 Subject: [PATCH 2/2] Update compiler/src/dotty/tools/dotc/core/Types.scala Co-Authored-By: Guillaume Martres --- compiler/src/dotty/tools/dotc/core/Types.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/core/Types.scala b/compiler/src/dotty/tools/dotc/core/Types.scala index dbc1acfd2903..ee004f9c89dd 100644 --- a/compiler/src/dotty/tools/dotc/core/Types.scala +++ b/compiler/src/dotty/tools/dotc/core/Types.scala @@ -918,7 +918,7 @@ object Types { case tp @ MethodType(Nil) => tp.resultType case _ => tp } - this.isInstanceOf[ClassInfo] && !checkClassInfo + !checkClassInfo && this.isInstanceOf[ClassInfo] || (this.widenExpr frozen_<:< that.widenExpr) || matchLoosely && { val this1 = widenNullary(this)