Permalink
Browse files

Fix for SI-3718.

And for a bunch of other tickets where we unleash a stack
trace rather than printing a sensible error message.  But
SI-3718 is a continuations plugin crash, now a reasonable
if somewhat vague error.
  • Loading branch information...
paulp committed May 3, 2012
1 parent 264cef8 commit 58f6a1346093db2f407879246884d480ff8d7904
@@ -639,6 +639,7 @@ trait StdNames {
val lang: NameType = "lang"
val length: NameType = "length"
val lengthCompare: NameType = "lengthCompare"
val liftedTree: NameType = "liftedTree"
val `macro` : NameType = "macro"
val macroThis : NameType = "_this"
val macroContext : NameType = "c"
@@ -50,14 +50,18 @@ abstract class SymbolTable extends api.Universe
/** Override with final implementation for inlining. */
def debuglog(msg: => String): Unit = if (settings.debug.value) log(msg)
def debugwarn(msg: => String): Unit = if (settings.debug.value) Console.err.println(msg)
def throwableAsString(t: Throwable): String = "" + t
/** Prints a stack trace if -Ydebug or equivalent was given, otherwise does nothing. */
def debugStack(t: Throwable): Unit = debugwarn(throwableAsString(t))
/** Overridden when we know more about what was happening during a failure. */
def supplementErrorMessage(msg: String): String = msg
private[scala] def printCaller[T](msg: String)(result: T) = {
Console.err.println(msg + ": " + result)
Console.err.println("Called from:")
(new Throwable).getStackTrace.drop(2).take(15).foreach(Console.err.println)
Console.err.println("%s: %s\nCalled from: %s".format(msg, result,
(new Throwable).getStackTrace.drop(2).take(15).mkString("\n")))
result
}
@@ -6730,6 +6730,11 @@ trait Types extends api.Types { self: SymbolTable =>
case TypeRef(_, sym, _) => !isPrimitiveValueClass(sym)
case _ => false
}
// Add serializable to a list of parents, unless one of them already is
def addSerializable(ps: Type*): List[Type] = (
if (ps exists (_ <:< SerializableClass.tpe)) ps.toList
else (ps :+ SerializableClass.tpe).toList
)
def objToAny(tp: Type): Type =
if (!phase.erasedTypes && tp.typeSymbol == ObjectClass) AnyClass.tpe
@@ -243,7 +243,7 @@ class Global(var currentSettings: Settings, var reporter: Reporter) extends Symb
}
def logThrowable(t: Throwable): Unit = globalError(throwableAsString(t))
def throwableAsString(t: Throwable): String =
override def throwableAsString(t: Throwable): String =
if (opt.richExes) Exceptional(t).force().context()
else util.stackTraceString(t)
@@ -72,45 +72,54 @@ abstract class UnCurry extends InfoTransform
}
class UnCurryTransformer(unit: CompilationUnit) extends TypingTransformer(unit) {
private var needTryLift = false
private var inPattern = false
private var needTryLift = false
private var inPattern = false
private var inConstructorFlag = 0L
private val byNameArgs = new mutable.HashSet[Tree]
private val noApply = new mutable.HashSet[Tree]
private val newMembers = mutable.ArrayBuffer[Tree]()
private val repeatedParams = mutable.Map[Symbol, List[ValDef]]()
private val byNameArgs = mutable.HashSet[Tree]()
private val noApply = mutable.HashSet[Tree]()
private val newMembers = mutable.ArrayBuffer[Tree]()
private val repeatedParams = mutable.Map[Symbol, List[ValDef]]()
@inline private def withInPattern[T](value: Boolean)(body: => T): T = {
inPattern = value
try body
finally inPattern = !value
}
private def newFunction0(body: Tree): Tree = {
val result = localTyper.typedPos(body.pos)(Function(Nil, body)).asInstanceOf[Function]
log("Change owner from %s to %s in %s".format(currentOwner, result.symbol, result.body))
result.body changeOwner (currentOwner -> result.symbol)
transformFunction(result)
}
private lazy val serialVersionUIDAnnotation =
AnnotationInfo(SerialVersionUIDAttr.tpe, List(Literal(Constant(0))), List())
private var nprinted = 0
override def transform(tree: Tree): Tree = try { //debug
postTransform(mainTransform(tree))
} catch {
case ex: Throwable =>
if (nprinted < 10) {
Console.println("exception when traversing " + tree)
nprinted += 1
}
throw ex
}
// I don't have a clue why I'm catching TypeErrors here, but it's better
// than spewing stack traces at end users for internal errors. Examples
// which hit at this point should not be hard to come by, but the immediate
// motivation can be seen in continuations-neg/t3718.
override def transform(tree: Tree): Tree = (
try postTransform(mainTransform(tree))
catch { case ex: TypeError =>
unit.error(ex.pos, ex.msg)
debugStack(ex)
EmptyTree
}
)
/* Is tree a reference `x` to a call by name parameter that needs to be converted to
* x.apply()? Note that this is not the case if `x` is used as an argument to another
* call by name parameter.
*/
def isByNameRef(tree: Tree): Boolean =
tree.isTerm && tree.hasSymbol &&
isByNameParamType(tree.symbol.tpe) &&
!byNameArgs(tree)
def isByNameRef(tree: Tree) = (
tree.isTerm
&& !byNameArgs(tree)
&& tree.hasSymbolWhich(s => isByNameParamType(s.tpe))
)
/** Uncurry a type of a tree node.
* This function is sensitive to whether or not we are in a pattern -- when in a pattern
@@ -241,10 +250,10 @@ abstract class UnCurry extends InfoTransform
// only get here when running under -Xoldpatmat
synthPartialFunction(fun)
case _ =>
val parents =
if (isFunctionType(fun.tpe)) List(abstractFunctionForFunctionType(fun.tpe), SerializableClass.tpe)
else List(ObjectClass.tpe, fun.tpe, SerializableClass.tpe)
val parents = (
if (isFunctionType(fun.tpe)) addSerializable(abstractFunctionForFunctionType(fun.tpe))
else addSerializable(ObjectClass.tpe, fun.tpe)
)
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
@@ -282,7 +291,7 @@ abstract class UnCurry extends InfoTransform
val (formals, restpe) = (targs.init, targs.last)
val anonClass = fun.symbol.owner newAnonymousFunctionClass(fun.pos, inConstructorFlag) addAnnotation serialVersionUIDAnnotation
val parents = List(appliedType(AbstractPartialFunctionClass, targs: _*), SerializableClass.tpe)
val parents = addSerializable(appliedType(AbstractPartialFunctionClass, targs: _*))
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
// duplicate before applyOrElseMethodDef is run so that it does not mess up our trees and label symbols (we have a fresh set)
@@ -409,22 +418,27 @@ abstract class UnCurry extends InfoTransform
else if (tp.bounds.hi ne tp) getClassTag(tp.bounds.hi)
else localTyper.TyperErrorGen.MissingClassTagError(tree, tp)
}
def traversableClassTag(tpe: Type): Tree = {
(tpe baseType TraversableClass).typeArgs match {
case targ :: _ => getClassTag(targ)
case _ => EmptyTree
}
}
afterUncurry {
localTyper.typedPos(pos) {
Apply(gen.mkAttributedSelect(tree, toArraySym),
List(getClassTag(tree.tpe.baseType(TraversableClass).typeArgs.head)))
gen.mkMethodCall(tree, toArraySym, Nil, List(traversableClassTag(tree.tpe)))
}
}
}
var suffix: Tree =
if (treeInfo isWildcardStarArgList args) {
val Typed(tree, _) = args.last;
val Typed(tree, _) = args.last
if (isJava)
if (tree.tpe.typeSymbol == ArrayClass) tree
else sequenceToArray(tree)
else
if (tree.tpe.typeSymbol isSubClass TraversableClass) tree // @PP: I suspect this should be SeqClass
if (tree.tpe.typeSymbol isSubClass SeqClass) tree
else arrayToSequence(tree, varargsElemType)
}
else {
@@ -447,22 +461,18 @@ abstract class UnCurry extends InfoTransform
val args1 = if (isVarArgTypes(formals)) transformVarargs(formals.last.typeArgs.head) else args
map2(formals, args1) { (formal, arg) =>
if (!isByNameParamType(formal)) {
if (!isByNameParamType(formal))
arg
} else if (isByNameRef(arg)) {
else if (isByNameRef(arg)) {
byNameArgs += arg
arg setType functionType(List(), arg.tpe)
} else {
if (opt.verboseDebug) {
val posstr = arg.pos.source.path + ":" + arg.pos.line
val permstr = if (fun.isPrivate) "private" else "notprivate"
log("byname | %s | %s | %s".format(posstr, fun.fullName, permstr))
}
val result = localTyper.typed(
Function(Nil, arg) setPos arg.pos).asInstanceOf[Function]
new ChangeOwnerTraverser(currentOwner, result.symbol).traverse(arg)
transformFunction(result)
arg setType functionType(Nil, arg.tpe)
}
else {
log("byname | %s | %s | %s".format(
arg.pos.source.path + ":" + arg.pos.line, fun.fullName,
if (fun.isPrivate) "private" else "")
)
newFunction0(arg)
}
}
}
@@ -709,12 +719,12 @@ abstract class UnCurry extends InfoTransform
case Apply(Apply(fn, args), args1) =>
treeCopy.Apply(tree, fn, args ::: args1)
case Ident(name) =>
assert(name != tpnme.WILDCARD_STAR)
assert(name != tpnme.WILDCARD_STAR, tree)
applyUnary()
case Select(_, _) | TypeApply(_, _) =>
applyUnary()
case ret @ Return(expr) if (isNonLocalReturn(ret)) =>
debuglog("non local return in "+ret.symbol+" from "+currentOwner.enclMethod)
case ret @ Return(expr) if isNonLocalReturn(ret) =>
log("non-local return from %s to %s".format(currentOwner.enclMethod, ret.symbol))
atPos(ret.pos)(nonLocalReturnThrow(expr, ret.symbol))
case TypeTree() =>
tree
@@ -762,10 +772,10 @@ abstract class UnCurry extends InfoTransform
)
}
val reps = repeatedParams(dd.symbol)
val rpsymbols = reps.map(_.symbol).toSet
val theTyper = typer.atOwner(dd, currentClass)
val flatparams = flatdd.vparamss.head
val reps = repeatedParams(dd.symbol)
val rpsymbols = reps.map(_.symbol).toSet
val theTyper = typer.atOwner(dd, currentClass)
val flatparams = flatdd.vparamss.head
// create the type
val forwformals = flatparams map {
@@ -1616,10 +1616,16 @@ trait Typers extends Modes with Adaptations with Taggings {
val clazz = mdef.symbol.moduleClass
val typedMods = typedModifiers(mdef.mods)
assert(clazz != NoSymbol, mdef)
val noSerializable = (
(linkedClass eq NoSymbol)
|| linkedClass.isErroneous
|| !linkedClass.isSerializable
|| clazz.isSerializable
)
val impl1 = typerReportAnyContextErrors(context.make(mdef.impl, clazz, newScope)) {
_.typedTemplate(mdef.impl, {
parentTypes(mdef.impl) ++ (
if (linkedClass == NoSymbol || !linkedClass.isSerializable || clazz.isSerializable) Nil
if (noSerializable) Nil
else {
clazz.makeSerializable()
List(TypeTree(SerializableClass.tpe) setPos clazz.pos.focus)
@@ -2293,11 +2299,11 @@ trait Typers extends Modes with Adaptations with Taggings {
// need to duplicate the cases before typing them to generate the apply method, or the symbols will be all messed up
val casesTrue = if (isPartial) cases map (c => deriveCaseDef(c)(x => TRUE_typed).duplicate) else Nil
// println("casesTrue "+ casesTrue)
def parentsPartial(targs: List[Type]) = List(appliedType(AbstractPartialFunctionClass.typeConstructor, targs), SerializableClass.tpe)
def parentsPartial(targs: List[Type]) = addSerializable(appliedType(AbstractPartialFunctionClass.typeConstructor, targs))
def applyMethod = {
// rig the show so we can get started typing the method body -- later we'll correct the infos...
anonClass setInfo ClassInfoType(List(ObjectClass.tpe, pt, SerializableClass.tpe), newScope, anonClass)
anonClass setInfo ClassInfoType(addSerializable(ObjectClass.tpe, pt), newScope, anonClass)
val methodSym = anonClass.newMethod(nme.apply, tree.pos, if(isPartial) (FINAL | OVERRIDE) else FINAL)
val paramSyms = mkParams(methodSym)
val selector = mkSel(paramSyms)
@@ -2313,10 +2319,10 @@ trait Typers extends Modes with Adaptations with Taggings {
val resTp = match_.tpe
val methFormals = paramSyms map (_.tpe)
val parents =
val parents = (
if (isPartial) parentsPartial(List(methFormals.head, resTp))
else List(abstractFunctionType(methFormals, resTp), SerializableClass.tpe)
else addSerializable(abstractFunctionType(methFormals, resTp))
)
anonClass setInfo ClassInfoType(parents, newScope, anonClass)
methodSym setInfoAndEnter MethodType(paramSyms, resTp)
@@ -110,8 +110,7 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with
transExpr(body, None, ext)
}
debuglog("anf result "+body1)
debuglog("result is of type "+body1.tpe)
debuglog("anf result "+body1+"\nresult is of type "+body1.tpe)
treeCopy.Function(ff, transformValDefs(vparams), body1)
}
@@ -142,7 +141,6 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with
transExpr(tree, None, None)
case _ =>
if (hasAnswerTypeAnn(tree.tpe)) {
if (!cpsAllowed)
unit.error(tree.pos, "cps code not allowed here / " + tree.getClass + " / " + tree)
@@ -357,7 +355,20 @@ abstract class SelectiveANFTransform extends PluginComponent with Transform with
List(expr)
)
)
return ((stms, call))
// This is today's sick/meaningless heuristic for spotting breakdown so
// we don't proceed until stack traces start draping themselves over everything.
// If there are wildcard types in the tree and B == Nothing, something went wrong.
// (I thought WildcardTypes would be enough, but nope. 'reset0 { 0 }' has them.)
//
// Code as simple as reset((_: String).length)
// will crash meaninglessly without this check. See SI-3718.
//
// TODO - obviously this should be done earlier, differently, or with
// a more skilled hand. Most likely, all three.
if ((b.typeSymbol eq NothingClass) && call.tpe.exists(_ eq WildcardType))
unit.error(tree.pos, "cannot cps-transform malformed (possibly in shift/reset placement) expression")
else
return ((stms, call))
}
catch {
case ex:TypeError =>
@@ -0,0 +1,4 @@
t3718.scala:2: error: cannot cps-transform malformed (possibly in shift/reset placement) expression
scala.util.continuations.reset((_: Any).##)
^
one error found
@@ -0,0 +1,3 @@
object Test {
scala.util.continuations.reset((_: Any).##)
}

0 comments on commit 58f6a13

Please sign in to comment.