From 92bd4a75eb546565df903793fe52b35c9159d1d6 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Wed, 11 Sep 2013 10:17:56 -0700 Subject: SI-7834 Type equivalence of C.this and C.super. Foo.this.x and Foo.super.x were roughly unrelated in the eyes of isSubType. I implemented conformance as described in the comment: This is looking for situations such as B.this.x.type <:< B.super.x.type. If it's a ThisType on the lhs and a SuperType on the right, and they originate in the same class, and the 'x' in the ThisType has in its override chain the 'x' in the SuperType, then the types conform. I think this is overly conservative but it's way ahead of where it was. --- src/reflect/scala/reflect/internal/Types.scala | 14 ++-- .../scala/reflect/internal/tpe/TypeComparers.scala | 18 +++-- test/files/neg/t7834neg.check | 41 ++++++++++++ test/files/neg/t7834neg.scala | 76 ++++++++++++++++++++++ test/files/pos/t7834.scala | 6 ++ 5 files changed, 142 insertions(+), 13 deletions(-) create mode 100644 test/files/neg/t7834neg.check create mode 100644 test/files/neg/t7834neg.scala create mode 100644 test/files/pos/t7834.scala diff --git a/src/reflect/scala/reflect/internal/Types.scala b/src/reflect/scala/reflect/internal/Types.scala index ca01a4b8e3..ad9001ca4e 100644 --- a/src/reflect/scala/reflect/internal/Types.scala +++ b/src/reflect/scala/reflect/internal/Types.scala @@ -4013,15 +4013,11 @@ trait Types * type selections with the same name of equal (as determined by `=:=`) prefixes are * considered equal in regard to `=:=`. */ - def beginsWithTypeVarOrIsRefined(tp: Type): Boolean = tp match { - case SingleType(pre, sym) => - !(sym hasFlag PACKAGE) && beginsWithTypeVarOrIsRefined(pre) - case tv@TypeVar(_, constr) => - !tv.instValid || beginsWithTypeVarOrIsRefined(constr.inst) - case RefinedType(_, _) => - true - case _ => - false + def isEligibleForPrefixUnification(tp: Type): Boolean = tp match { + case SingleType(pre, sym) => !(sym hasFlag PACKAGE) && isEligibleForPrefixUnification(pre) + case tv@TypeVar(_, constr) => !tv.instValid || isEligibleForPrefixUnification(constr.inst) + case RefinedType(_, _) => true + case _ => false } def isErrorOrWildcard(tp: Type) = (tp eq ErrorType) || (tp eq WildcardType) diff --git a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala index d8b3b04d0e..152c689c56 100644 --- a/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala +++ b/src/reflect/scala/reflect/internal/tpe/TypeComparers.scala @@ -35,8 +35,10 @@ trait TypeComparers { private var subsametypeRecursions: Int = 0 - private def isUnifiable(pre1: Type, pre2: Type) = - (beginsWithTypeVarOrIsRefined(pre1) || beginsWithTypeVarOrIsRefined(pre2)) && (pre1 =:= pre2) + private def isUnifiable(pre1: Type, pre2: Type) = ( + (isEligibleForPrefixUnification(pre1) || isEligibleForPrefixUnification(pre2)) + && (pre1 =:= pre2) + ) /** Returns true iff we are past phase specialize, * sym1 and sym2 are two existential skolems with equal names and bounds, @@ -64,7 +66,6 @@ trait TypeComparers { (sym1.name == sym2.name) && isUnifiable(pre1, pre2) ) - def isDifferentType(tp1: Type, tp2: Type): Boolean = try { subsametypeRecursions += 1 undoLog undo { // undo type constraints that arise from operations in this block @@ -207,6 +208,7 @@ trait TypeComparers { } case _ => false } + /* Those false cases certainly are ugly. There's a proposed SIP to deuglify it. * https://docs.google.com/a/improving.org/document/d/1onPrzSqyDpHScc9PS_hpxJwa3FlPtthxw-bAuuEe8uA */ @@ -334,6 +336,14 @@ trait TypeComparers { (tparams1 corresponds tparams2)(cmp) && (sub1(res1) <:< sub2(res2)) } } + // This is looking for situations such as B.this.x.type <:< B.super.x.type. + // If it's a ThisType on the lhs and a SuperType on the right, and they originate + // in the same class, and the 'x' in the ThisType has in its override chain + // the 'x' in the SuperType, then the types conform. + private def isThisAndSuperSubtype(tp1: Type, tp2: Type): Boolean = (tp1, tp2) match { + case (SingleType(ThisType(lpre), v1), SingleType(SuperType(ThisType(rpre), _), v2)) => (lpre == rpre) && (v1.overrideChain contains v2) + case _ => false + } // @assume tp1.isHigherKinded || tp2.isHigherKinded def isHKSubType(tp1: Type, tp2: Type, depth: Depth): Boolean = { @@ -359,7 +369,7 @@ trait TypeComparers { def retry(lhs: Type, rhs: Type) = ((lhs ne tp1) || (rhs ne tp2)) && isSubType(lhs, rhs, depth) if (isSingleType(tp1) && isSingleType(tp2) || isConstantType(tp1) && isConstantType(tp2)) - return (tp1 =:= tp2) || retry(tp1.underlying, tp2) + return (tp1 =:= tp2) || isThisAndSuperSubtype(tp1, tp2) || retry(tp1.underlying, tp2) if (tp1.isHigherKinded || tp2.isHigherKinded) return isHKSubType(tp1, tp2, depth) diff --git a/test/files/neg/t7834neg.check b/test/files/neg/t7834neg.check new file mode 100644 index 0000000000..569df4b8ce --- /dev/null +++ b/test/files/neg/t7834neg.check @@ -0,0 +1,41 @@ +t7834neg.scala:48: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.super.q.type + x1 = x2 // fail + ^ +t7834neg.scala:50: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.super.q.type + x2 = x1 // fail + ^ +t7834neg.scala:53: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.this.q.type + x3 = x1 // fail + ^ +t7834neg.scala:54: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.this.q.type + x3 = x2 // fail + ^ +t7834neg.scala:69: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.super.q.type + x1 = super[S2].q // fail + ^ +t7834neg.scala:71: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.super.q.type + x2 = super[S1].q // fail + ^ +t7834neg.scala:74: error: type mismatch; + found : C.super.q.type (with underlying type M1) + required: C.this.q.type + x3 = super[S1].q // fail + ^ +t7834neg.scala:75: error: type mismatch; + found : C.super.q.type (with underlying type M2) + required: C.this.q.type + x3 = super[S2].q // fail + ^ +8 errors found diff --git a/test/files/neg/t7834neg.scala b/test/files/neg/t7834neg.scala new file mode 100644 index 0000000000..d35a84eadd --- /dev/null +++ b/test/files/neg/t7834neg.scala @@ -0,0 +1,76 @@ +class M1 +class M2 extends M1 +class M3 extends M2 + +trait S1 { val q = new M1 ; val q1: q.type = q } +trait S2 { val q = new M2 ; val q2: q.type = q } + +class B extends S1 with S2 { + override val q = new M3 + val q3: q.type = q + + var x1: B.super[S1].q1.type = null + var x2: B.super[S2].q2.type = null + var x3: B.this.q3.type = null + + x1 = x1 + x1 = x2 + x1 = x3 + x2 = x1 + x2 = x2 + x2 = x3 + x3 = x1 + x3 = x2 + x3 = x3 + + x1 = q1 + x1 = q2 + x1 = q3 + x2 = q1 + x2 = q2 + x2 = q3 + x3 = q1 + x3 = q2 + x3 = x3 +} + +class C extends S1 with S2 { + override val q = new M3 + val q3: q.type = q + + // x1's type and x2's type are incompatible + // x3's is assignable to x1 or x2, but not vice versa + var x1: C.super[S1].q.type = null + var x2: C.super[S2].q.type = null + var x3: C.this.q.type = null + + x1 = x1 + x1 = x2 // fail + x1 = x3 + x2 = x1 // fail + x2 = x2 + x2 = x3 + x3 = x1 // fail + x3 = x2 // fail + x3 = x3 + + x1 = q1 + x1 = q2 + x1 = q3 + x2 = q1 + x2 = q2 + x2 = q3 + x3 = q1 + x3 = q2 + x3 = x3 + + x1 = q + x1 = super[S1].q + x1 = super[S2].q // fail + x2 = q + x2 = super[S1].q // fail + x2 = super[S2].q + x3 = q + x3 = super[S1].q // fail + x3 = super[S2].q // fail +} diff --git a/test/files/pos/t7834.scala b/test/files/pos/t7834.scala new file mode 100644 index 0000000000..fc9a0aa09d --- /dev/null +++ b/test/files/pos/t7834.scala @@ -0,0 +1,6 @@ +class S { val q = "" } + +class B extends S { + val x1: B.super.q.type = q + val x2: B.this.q.type = q +} -- cgit v1.2.3