Permalink
Browse files

More resilience to missing classes.

The situation (I don't know how to make partest test this) is

  package s
  class A ; class S { def f(): A = ??? }

If one compiles this and removes A.class, should references to
class S cause the compiler to explode eagerly and fail to load S,
or explode lazily if and when it needs to know something about A?
This patch takes us from the former strategy to the latter.

Review by @xeno-by.
  • Loading branch information...
1 parent 4459e5a commit a55788e275faed91cb9223686a3aef6ee54213a8 @paulp paulp committed Aug 2, 2012
View
10 src/compiler/scala/tools/nsc/Global.scala
@@ -220,11 +220,11 @@ class Global(var currentSettings: Settings, var reporter: Reporter)
// not deprecated yet, but a method called "error" imported into
// nearly every trait really must go. For now using globalError.
- def error(msg: String) = globalError(msg)
- def globalError(msg: String) = reporter.error(NoPosition, msg)
- def inform(msg: String) = reporter.echo(msg)
- def warning(msg: String) =
- if (opt.fatalWarnings) globalError(msg)
+ def error(msg: String) = globalError(msg)
+ def inform(msg: String) = reporter.echo(msg)
+ override def globalError(msg: String) = reporter.error(NoPosition, msg)
+ override def warning(msg: String) =
+ if (settings.fatalWarnings.value) globalError(msg)
else reporter.warning(NoPosition, msg)
// Getting in front of Predef's asserts to supplement with more info.
View
34 src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala
@@ -452,7 +452,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
def survivingParams(params: List[Symbol], env: TypeEnv) =
params filter {
p =>
- !p.isSpecialized ||
+ !p.isSpecialized ||
!env.contains(p) ||
!isPrimitiveValueType(env(p))
}
@@ -506,16 +506,16 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* was both already used for a map and mucho long. So "sClass" is the
* specialized subclass of "clazz" throughout this file.
*/
-
+
// SI-5545: Eliminate classes with the same name loaded from the bytecode already present - all we need to do is
// to force .info on them, as their lazy type will be evaluated and the symbols will be eliminated. Unfortunately
// evaluating the info after creating the specialized class will mess the specialized class signature, so we'd
- // better evaluate it before creating the new class symbol
+ // better evaluate it before creating the new class symbol
val clazzName = specializedName(clazz, env0).toTypeName
- val bytecodeClazz = clazz.owner.info.decl(clazzName)
+ val bytecodeClazz = clazz.owner.info.decl(clazzName)
// debuglog("Specializing " + clazz + ", but found " + bytecodeClazz + " already there")
bytecodeClazz.info
-
+
val sClass = clazz.owner.newClass(clazzName, clazz.pos, (clazz.flags | SPECIALIZED) & ~CASE)
def cloneInSpecializedClass(member: Symbol, flagFn: Long => Long, newName: Name = null) =
@@ -762,7 +762,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
}
-
+
val subclasses = specializations(clazz.info.typeParams) filter satisfiable
subclasses foreach {
env =>
@@ -1006,7 +1006,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
* Fails if such an environment cannot be found.
*
* If `strict` is true, a UnifyError is thrown if unification is impossible.
- *
+ *
* If `tparams` is true, then the methods tries to unify over type params in polytypes as well.
*/
private def unify(tp1: Type, tp2: Type, env: TypeEnv, strict: Boolean, tparams: Boolean = false): TypeEnv = (tp1, tp2) match {
@@ -1185,7 +1185,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
|| specializedTypeVars(t1).nonEmpty
|| specializedTypeVars(t2).nonEmpty)
}
-
+
env forall { case (tvar, tpe) =>
matches(tvar.info.bounds.lo, tpe) && matches(tpe, tvar.info.bounds.hi) || {
if (warnings)
@@ -1201,7 +1201,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
}
-
+
def satisfiabilityConstraints(env: TypeEnv): Option[TypeEnv] = {
val noconstraints = Some(emptyEnv)
def matches(tpe1: Type, tpe2: Type): Option[TypeEnv] = {
@@ -1232,7 +1232,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
} with typechecker.Duplicators {
private val (castfrom, castto) = casts.unzip
private object CastMap extends SubstTypeMap(castfrom.toList, castto.toList)
-
+
class BodyDuplicator(_context: Context) extends super.BodyDuplicator(_context) {
override def castType(tree: Tree, pt: Type): Tree = {
// log(" expected type: " + pt)
@@ -1249,9 +1249,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
ntree
}
}
-
+
protected override def newBodyDuplicator(context: Context) = new BodyDuplicator(context)
-
+
}
/** A tree symbol substituter that substitutes on type skolems.
@@ -1359,7 +1359,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
}
}
}
-
+
def reportError[T](body: =>T)(handler: TypeError => T): T =
try body
catch {
@@ -1396,7 +1396,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
else None
} else None
}
-
+
curTree = tree
tree match {
case Apply(Select(New(tpt), nme.CONSTRUCTOR), args) =>
@@ -1570,7 +1570,7 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
})
debuglog("created special overload tree " + t)
debuglog("created " + t)
- reportError {
+ reportError {
localTyper.typed(t)
} {
_ => super.transform(tree)
@@ -1629,9 +1629,9 @@ abstract class SpecializeTypes extends InfoTransform with TypingTransformers {
super.transform(tree)
}
}
-
+
/** Duplicate the body of the given method `tree` to the new symbol `source`.
- *
+ *
* Knowing that the method can be invoked only in the `castmap` type environment,
* this method will insert casts for all the expressions of types mappend in the
* `castmap`.
View
4 src/reflect/scala/reflect/internal/SymbolTable.scala
@@ -43,7 +43,9 @@ abstract class SymbolTable extends macros.Universe
lazy val treeBuild = gen
def log(msg: => AnyRef): Unit
- def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg))
+ def warning(msg: String): Unit = Console.err.println(msg)
+ def globalError(msg: String): Unit = abort(msg)
+ def abort(msg: String): Nothing = throw new FatalError(supplementErrorMessage(msg))
@deprecated("Give us a reason", "2.10.0")
def abort(): Nothing = abort("unknown error")
View
41 src/reflect/scala/reflect/internal/Symbols.scala
@@ -392,6 +392,16 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
case x: TermName => newErrorValue(x)
}
+ /** Creates a placeholder symbol for when a name is encountered during
+ * unpickling for which there is no corresponding classfile. This defers
+ * failure to the point when that name is used for something, which is
+ * often to the point of never.
+ */
+ def newStubSymbol(name: Name): Symbol = name match {
+ case n: TypeName => new StubClassSymbol(this, n)
+ case _ => new StubTermSymbol(this, name.toTermName)
+ }
+
@deprecated("Use the other signature", "2.10.0")
def newClass(pos: Position, name: TypeName): Symbol = newClass(name, pos)
@deprecated("Use the other signature", "2.10.0")
@@ -2998,6 +3008,37 @@ trait Symbols extends api.Symbols { self: SymbolTable =>
|| info.parents.exists(_.typeSymbol hasTransOwner sym)
)
}
+ trait StubSymbol extends Symbol {
+ protected def stubWarning = {
+ val from = if (associatedFile == null) "" else s" - referenced from ${associatedFile.canonicalPath}"
+ s"$kindString $nameString$locationString$from (a classfile may be missing)"
+ }
+ private def fail[T](alt: T): T = {
+ // Avoid issuing lots of redundant errors
+ if (!hasFlag(IS_ERROR)) {
+ globalError(s"bad symbolic reference to " + stubWarning)
+ if (settings.debug.value)
+ (new Throwable).printStackTrace
+
+ this setFlag IS_ERROR
+ }
+ alt
+ }
+ // This one doesn't call fail because SpecializeTypes winds up causing
+ // isMonomorphicType to be called, which calls this, which would fail us
+ // in all the scenarios we're trying to keep from failing.
+ override def originalInfo = NoType
+ override def associatedFile = owner.associatedFile
+ override def info = fail(NoType)
+ override def rawInfo = fail(NoType)
+ override def companionSymbol = fail(NoSymbol)
+
+ locally {
+ debugwarn("creating stub symbol for " + stubWarning)
+ }
+ }
+ class StubClassSymbol(owner0: Symbol, name0: TypeName) extends ClassSymbol(owner0, owner0.pos, name0) with StubSymbol
+ class StubTermSymbol(owner0: Symbol, name0: TermName) extends TermSymbol(owner0, owner0.pos, name0) with StubSymbol
trait FreeSymbol extends Symbol {
def origin: String
View
8 src/reflect/scala/reflect/internal/pickling/UnPickler.scala
@@ -230,9 +230,11 @@ abstract class UnPickler /*extends reflect.generic.UnPickler*/ {
fromName(nme.expandedName(name.toTermName, owner)) orElse {
// (3) Try as a nested object symbol.
nestedObjectSymbol orElse {
- // (4) Otherwise, fail.
- //System.err.println("missing "+name+" in "+owner+"/"+owner.id+" "+owner.info.decls)
- adjust(errorMissingRequirement(name, owner))
+ // (4) Call the mirror's "missing" hook.
+ adjust(mirrorThatLoaded(owner).missingHook(owner, name)) orElse {
+ // (5) Create a stub symbol to defer hard failure a little longer.
+ owner.newStubSymbol(name)
+ }
}
}
}
View
5 test/files/neg/t5148.check
@@ -1,2 +1,3 @@
-error: bad reference while unpickling Imports.class: term memberHandlers not found in scala.tools.nsc.interpreter.IMain
-one error found
+error: bad symbolic reference to value global in class IMain - referenced from t5148.scala (a classfile may be missing)
+error: bad symbolic reference to value memberHandlers in class IMain - referenced from t5148.scala (a classfile may be missing)
+two errors found

0 comments on commit a55788e

Please sign in to comment.