diff options
author | Paul Phillips <paulp@improving.org> | 2012-08-02 10:07:19 -0700 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2012-08-11 20:27:46 -0700 |
commit | a55788e275faed91cb9223686a3aef6ee54213a8 (patch) | |
tree | 4ac92fc704d7e06edb38680114321309c84dbd5b | |
parent | 4459e5abb2b765cbdca34eb19c5d8b705203f771 (diff) | |
download | scala-a55788e275faed91cb9223686a3aef6ee54213a8.tar.gz scala-a55788e275faed91cb9223686a3aef6ee54213a8.tar.bz2 scala-a55788e275faed91cb9223686a3aef6ee54213a8.zip |
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.
-rw-r--r-- | src/compiler/scala/tools/nsc/Global.scala | 10 | ||||
-rw-r--r-- | src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala | 34 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/SymbolTable.scala | 4 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/Symbols.scala | 41 | ||||
-rw-r--r-- | src/reflect/scala/reflect/internal/pickling/UnPickler.scala | 8 | ||||
-rw-r--r-- | test/files/neg/t5148.check | 5 |
6 files changed, 74 insertions, 28 deletions
diff --git a/src/compiler/scala/tools/nsc/Global.scala b/src/compiler/scala/tools/nsc/Global.scala index 2b0f082051..c60a3c941f 100644 --- a/src/compiler/scala/tools/nsc/Global.scala +++ b/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. diff --git a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala b/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala index 10a946c318..005d26e119 100644 --- a/src/compiler/scala/tools/nsc/transform/SpecializeTypes.scala +++ b/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`. diff --git a/src/reflect/scala/reflect/internal/SymbolTable.scala b/src/reflect/scala/reflect/internal/SymbolTable.scala index c564a93b62..761dcc0534 100644 --- a/src/reflect/scala/reflect/internal/SymbolTable.scala +++ b/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") diff --git a/src/reflect/scala/reflect/internal/Symbols.scala b/src/reflect/scala/reflect/internal/Symbols.scala index 161d04b6ff..9311a74f18 100644 --- a/src/reflect/scala/reflect/internal/Symbols.scala +++ b/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 diff --git a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala b/src/reflect/scala/reflect/internal/pickling/UnPickler.scala index 55746f414b..9234ccca7b 100644 --- a/src/reflect/scala/reflect/internal/pickling/UnPickler.scala +++ b/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) + } } } } diff --git a/test/files/neg/t5148.check b/test/files/neg/t5148.check index 96eb1fd364..6edfdf2b1e 100644 --- a/test/files/neg/t5148.check +++ b/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 |