aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Odersky <odersky@gmail.com>2014-11-24 18:49:20 +0100
committerMartin Odersky <odersky@gmail.com>2014-11-24 19:01:22 +0100
commit48f78cd66df9f6cd31201ba79b02891a99f1dfbe (patch)
tree41b9dde3dc7937140548deba81acc213fc33bf30
parent642c5e4500abfc5cef51eee7ed0a98930a24312f (diff)
downloaddotty-48f78cd66df9f6cd31201ba79b02891a99f1dfbe.tar.gz
dotty-48f78cd66df9f6cd31201ba79b02891a99f1dfbe.tar.bz2
dotty-48f78cd66df9f6cd31201ba79b02891a99f1dfbe.zip
Simpler cycle detection
Turns out that the last commit was a red herring. None of the hoops it jumped though was necessary. Instead there was a bug in isRef which caused `&` to erroneously compute T & Int as Int. The bug was that we always approximated alias types by their high bound. But in the present case, this leads to errors because U gets 'bounds >: Nothing <: Any', but it was still an alias type (i.e. its Deferred flag is not set). The fix dereferences aliases only if their info is a TypeAlias.
-rw-r--r--src/dotty/tools/dotc/core/StdNames.scala2
-rw-r--r--src/dotty/tools/dotc/core/Types.scala7
-rw-r--r--src/dotty/tools/dotc/typer/Namer.scala29
-rw-r--r--tests/pos/prefix.scala5
4 files changed, 20 insertions, 23 deletions
diff --git a/src/dotty/tools/dotc/core/StdNames.scala b/src/dotty/tools/dotc/core/StdNames.scala
index e17b655f9..8393eb56f 100644
--- a/src/dotty/tools/dotc/core/StdNames.scala
+++ b/src/dotty/tools/dotc/core/StdNames.scala
@@ -304,8 +304,6 @@ object StdNames {
val ArrayAnnotArg: N = "ArrayAnnotArg"
val Constant: N = "Constant"
val ConstantType: N = "ConstantType"
- val DummyHi: N = "DummyHi"
- val DummyLo: N = "DummyLo"
val ExistentialTypeTree: N = "ExistentialTypeTree"
val Flag : N = "Flag"
val Ident: N = "Ident"
diff --git a/src/dotty/tools/dotc/core/Types.scala b/src/dotty/tools/dotc/core/Types.scala
index 33d108540..feee05d86 100644
--- a/src/dotty/tools/dotc/core/Types.scala
+++ b/src/dotty/tools/dotc/core/Types.scala
@@ -104,9 +104,10 @@ object Types {
*/
def isRef(sym: Symbol)(implicit ctx: Context): Boolean = stripTypeVar match {
case this1: TypeRef =>
- val thissym = this1.symbol
- if (thissym.isAliasType) this1.info.bounds.hi.isRef(sym)
- else thissym eq sym
+ this1.info match { // see comment in Namers/typeDefSig
+ case TypeBounds(lo, hi) if lo eq hi => hi.isRef(sym)
+ case _ => this1.symbol eq sym
+ }
case this1: RefinedType =>
// make sure all refinements are type arguments
this1.parent.isRef(sym) && this.argInfos.nonEmpty
diff --git a/src/dotty/tools/dotc/typer/Namer.scala b/src/dotty/tools/dotc/typer/Namer.scala
index 7490e88e9..86376356c 100644
--- a/src/dotty/tools/dotc/typer/Namer.scala
+++ b/src/dotty/tools/dotc/typer/Namer.scala
@@ -673,7 +673,17 @@ class Namer { typer: Typer =>
def typeDefSig(tdef: TypeDef, sym: Symbol)(implicit ctx: Context): Type = {
completeParams(tdef.tparams)
- setDummyInfo(sym)
+ sym.info = TypeBounds.empty
+ // Temporarily set info of defined type T to ` >: Nothing <: Any.
+ // This is done to avoid cyclic reference errors for F-bounds.
+ // This is subtle: `sym` has now an empty TypeBounds, but is not automatically
+ // made an abstract type. If it had been made an abstract type, it would count as an
+ // abstract type of its enclosing class, which might make that class an invalid
+ // prefix. I verified this would lead to an error when compiling io.ClassPath.
+ // A distilled version is in pos/prefix.scala.
+ //
+ // The scheme critically relies on an implementation detail of isRef, which
+ // inspects a TypeRef's info, instead of simply dealiasing alias types.
val tparamSyms = tdef.tparams map symbolOfTree
val isDerived = tdef.rhs.isInstanceOf[untpd.DerivedTypeTree]
val toParameterize = tparamSyms.nonEmpty && !isDerived
@@ -690,21 +700,4 @@ class Namer { typer: Typer =>
sym.info = NoCompleter
checkNonCyclic(sym, unsafeInfo, reportErrors = true)
}
-
- /** Temporarily set info of defined type T to
- *
- * T >: dummyLo <: dummyHi
- * type dummyLo, dummyHi
- *
- * This is done to avoid cyclic reference errors for F-bounds.
- * The type is intentionally chosen so that it cannot possibly be
- * elided when taking a union or intersection.
- */
- private def setDummyInfo(sym: Symbol)(implicit ctx: Context): Unit = {
- def dummyBound(name: TypeName) =
- ctx.newSymbol(sym.owner, name, Synthetic | Deferred, TypeBounds.empty)
- val dummyLo = dummyBound(tpnme.DummyLo)
- val dummyHi = dummyBound(tpnme.DummyHi)
- sym.info = TypeBounds(TypeRef(NoPrefix, dummyLo), TypeRef(NoPrefix, dummyHi))
- }
} \ No newline at end of file
diff --git a/tests/pos/prefix.scala b/tests/pos/prefix.scala
new file mode 100644
index 000000000..e18eaa50f
--- /dev/null
+++ b/tests/pos/prefix.scala
@@ -0,0 +1,5 @@
+
+abstract class ClassPath {
+ type AnyClassRep = ClassPath#ClassRep
+ type ClassRep
+}