diff options
-rw-r--r-- | src/compiler/scala/tools/nsc/settings/ScalaSettings.scala | 4 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 14 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Types.scala | 14 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/tpe/TypeComparers.scala | 18 | ||||
-rw-r--r-- | test/files/neg/t7834neg.check | 41 | ||||
-rw-r--r-- | test/files/neg/t7834neg.scala | 76 | ||||
-rw-r--r-- | test/files/pos/t7834.scala | 6 | ||||
-rw-r--r-- | test/files/run/t7801.check | 11 | ||||
-rw-r--r-- | test/files/run/t7801.scala | 12 |
9 files changed, 178 insertions, 18 deletions
diff --git a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala index 32f0571e83..b37d0ba283 100644 --- a/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/ScalaSettings.scala @@ -182,7 +182,6 @@ trait ScalaSettings extends AbsScalaSettings */ val Ydocdebug = BooleanSetting("-Ydoc-debug", "Trace all scaladoc activity.") val Yidedebug = BooleanSetting("-Yide-debug", "Generate, validate and output trees using the interactive compiler.") - val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") val Yissuedebug = BooleanSetting("-Yissue-debug", "Print stack traces when a context issues an error.") val YmacrodebugLite = BooleanSetting("-Ymacro-debug-lite", "Trace essential macro-related activities.") val YmacrodebugVerbose = BooleanSetting("-Ymacro-debug-verbose", "Trace all macro-related activities: compilation, generation of synthetics, classloading, expansion, exceptions.") @@ -193,6 +192,9 @@ trait ScalaSettings extends AbsScalaSettings val Ypatmatdebug = BooleanSetting("-Ypatmat-debug", "Trace pattern matching translation.") val Yquasiquotedebug = BooleanSetting("-Yquasiquote-debug", "Trace quasiquote-related activities.") + // TODO 2.12 Remove + val Yinferdebug = BooleanSetting("-Yinfer-debug", "Trace type inference and implicit search.") withDeprecationMessage("Use -Ytyper-debug") enabling(List(Ytyperdebug)) + /** Groups of Settings. */ val future = BooleanSetting("-Xfuture", "Turn on future language features.") enabling futureSettings diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index d3a0ffb744..d58ea09386 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/src/reflect/scala/reflect/internal/Symbols.scala @@ -1435,6 +1435,13 @@ trait Symbols extends api.Symbols { self: SymbolTable => assert(isCompilerUniverse) if (infos == null || runId(infos.validFrom) == currentRunId) { infos + } else if (isPackageClass) { + // SI-7801 early phase package scopes are mutated in new runs (Namers#enterPackage), so we have to + // discard transformed infos, rather than just marking them as from this run. + val oldest = infos.oldest + oldest.validFrom = validTo + this.infos = oldest + oldest } else { val prev1 = adaptInfos(infos.prev) if (prev1 ne infos.prev) prev1 @@ -1444,10 +1451,7 @@ trait Symbols extends api.Symbols { self: SymbolTable => _validTo = period(currentRunId, pid) phase = phaseWithId(pid) - val info1 = ( - if (isPackageClass) infos.info - else adaptToNewRunMap(infos.info) - ) + val info1 = adaptToNewRunMap(infos.info) if (info1 eq infos.info) { infos.validFrom = validTo infos @@ -3473,6 +3477,8 @@ trait Symbols extends api.Symbols { self: SymbolTable => "TypeHistory(" + phaseOf(validFrom)+":"+runId(validFrom) + "," + info + "," + prev + ")" def toList: List[TypeHistory] = this :: ( if (prev eq null) Nil else prev.toList ) + + def oldest: TypeHistory = if (prev == null) this else prev.oldest } // ----- Hoisted closures and convenience methods, for compile time reductions ------- 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 +} diff --git a/test/files/run/t7801.check b/test/files/run/t7801.check new file mode 100644 index 0000000000..d72060c684 --- /dev/null +++ b/test/files/run/t7801.check @@ -0,0 +1,11 @@ +Type in expressions to have them evaluated. +Type :help for more information. + +scala> val g: scala.reflect.internal.SymbolTable = null; import g.abort +g: scala.reflect.internal.SymbolTable = null +import g.abort + +scala> class C(val a: Any) extends AnyVal +defined class C + +scala> diff --git a/test/files/run/t7801.scala b/test/files/run/t7801.scala new file mode 100644 index 0000000000..3a3cc97a51 --- /dev/null +++ b/test/files/run/t7801.scala @@ -0,0 +1,12 @@ +import scala.tools.partest.ReplTest + +// was crashing due to a subtle interaction of the Namer entering packages into +// enclosing packages by mutating the scope in place without invalidating later +// entries in the enclosing package class symbols type history. +// +// Sadly, I couldn't whittle the test case down further. +object Test extends ReplTest { + override def code = """val g: scala.reflect.internal.SymbolTable = null; import g.abort + |class C(val a: Any) extends AnyVal""".stripMargin + +} |