Skip to content

DO NOT merge: integrate fuzzing fixes #4489

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 23 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
3c8545b
Prevent position errors on Ident(nme.ERROR)
Blaisorblade May 1, 2018
ad04880
Revert parts of "Prevent position errors on Ident(nme.ERROR)"
Blaisorblade May 2, 2018
83610bb
Alternative fix
Blaisorblade May 2, 2018
78afe8a
Simplify fix
Blaisorblade May 2, 2018
3418413
Detect and flag deep findMember recursions
odersky Apr 25, 2018
29d39a1
Add test for cyclic references involving members of inherited and int…
odersky Apr 26, 2018
fb8ad7d
Add test
odersky Apr 26, 2018
3899e42
Detect more cyclic references
odersky Apr 26, 2018
7f49fae
Rearrange TypeError exceptions
odersky Apr 26, 2018
ebe1d1c
Further refactoring of stackoverflow handling
odersky Apr 26, 2018
9bb6cfe
Fix CyclicError reporting
odersky Apr 26, 2018
e85e516
Fix context for cyclic error message
odersky Apr 26, 2018
4bef48f
Fix^2 context in CyclicReference toMessage
odersky Apr 26, 2018
4828fe5
Fix error annotation in test
odersky Apr 26, 2018
d977e78
MergeError refactorings
odersky Apr 27, 2018
7d3927b
Add `append` method to Message
odersky Apr 27, 2018
c3b9ff7
Also catch TypeErrors in all transforms
odersky Apr 27, 2018
ec460a1
rearrange try-catch in MegaPhase
odersky Apr 27, 2018
703ae4e
Merge branch 'fix-#4368' into tmp-fuzzing-test
Blaisorblade May 7, 2018
d6e8e79
Merge branch 'fix-positioned-error-ident-failure' into tmp-fuzzing-test
Blaisorblade May 7, 2018
ae297ae
Band-aid fix for ClassCastException on malformed programs
Blaisorblade May 8, 2018
357b201
Much better, still band-aidy, fix
Blaisorblade May 8, 2018
9d339cb
Merge branch 'fix-classcastexception' into tmp-fuzzing-test
Blaisorblade May 8, 2018
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 0 additions & 5 deletions compiler/src/dotty/tools/dotc/config/Config.scala
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,6 @@ object Config {
*/
final val LogPendingFindMemberThreshold = 9

/** Maximal number of outstanding recursive calls to findMember before backing out
* when findMemberLimit is set.
*/
final val PendingFindMemberLimit = LogPendingFindMemberThreshold * 4

/** When in IDE, turn StaleSymbol errors into warnings instead of crashing */
final val ignoreStaleInIDE = true
}
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/config/ScalaSettings.scala
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class ScalaSettings extends Settings.SettingGroup {
val Xhelp = BooleanSetting("-X", "Print a synopsis of advanced options.")
val XnoForwarders = BooleanSetting("-Xno-forwarders", "Do not generate static forwarders in mirror classes.")
val XminImplicitSearchDepth = IntSetting("-Xmin-implicit-search-depth", "Set number of levels of implicit searches undertaken before checking for divergence.", 5)
val xmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 32)
val XmaxInlines = IntSetting("-Xmax-inlines", "Maximal number of successive inlines", 32)
val XmaxClassfileName = IntSetting("-Xmax-classfile-name", "Maximum filename length for generated classes", 255, 72 to 255)
val Xmigration = VersionSetting("-Xmigration", "Warn about constructs whose behavior may have changed since version.")
val Xprint = PhasesSetting("-Xprint", "Print out program after")
Expand Down
44 changes: 12 additions & 32 deletions compiler/src/dotty/tools/dotc/core/Denotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -332,21 +332,8 @@ object Denotations {
}

/** Handle merge conflict by throwing a `MergeError` exception */
private def mergeConflict(tp1: Type, tp2: Type, that: Denotation)(implicit ctx: Context): Type = {
def showSymbol(sym: Symbol): String = if (sym.exists) sym.showLocated else "[unknown]"
def showType(tp: Type) = tp match {
case ClassInfo(_, cls, _, _, _) => cls.showLocated
case bounds: TypeBounds => i"type bounds $bounds"
case _ => tp.show
}
val msg =
s"""cannot merge
| ${showSymbol(this.symbol)} of type ${showType(tp1)} and
| ${showSymbol(that.symbol)} of type ${showType(tp2)}
"""
if (true) throw new MergeError(msg, tp1, tp2)
else throw new Error(msg) // flip condition for debugging
}
private def mergeConflict(tp1: Type, tp2: Type, that: Denotation)(implicit ctx: Context): Type =
throw new MergeError(this.symbol, that.symbol, tp1, tp2, NoPrefix)

/** Merge parameter names of lambda types. If names in corresponding positions match, keep them,
* otherwise generate new synthetic names.
Expand Down Expand Up @@ -537,8 +524,7 @@ object Denotations {
else if (pre.widen.classSymbol.is(Scala2x) || ctx.scala2Mode)
info1 // follow Scala2 linearization -
// compare with way merge is performed in SymDenotation#computeMembersNamed
else
throw new MergeError(s"${ex.getMessage} as members of type ${pre.show}", ex.tp1, ex.tp2)
else throw new MergeError(ex.sym1, ex.sym2, ex.tp1, ex.tp2, pre)
}
new JointRefDenotation(sym, jointInfo, denot1.validFor & denot2.validFor)
}
Expand Down Expand Up @@ -1136,21 +1122,15 @@ object Denotations {
def doubleDefError(denot1: Denotation, denot2: Denotation, pre: Type = NoPrefix)(implicit ctx: Context): Nothing = {
val sym1 = denot1.symbol
val sym2 = denot2.symbol
def fromWhere = if (pre == NoPrefix) "" else i"\nwhen seen as members of $pre"
val msg =
if (denot1.isTerm)
i"""cannot merge
| $sym1: ${sym1.info} and
| $sym2: ${sym2.info};
|they are both defined in ${sym1.owner} but have matching signatures
| ${denot1.info} and
| ${denot2.info}$fromWhere"""
else
i"""cannot merge
| $sym1 ${denot1.info}
| $sym2 ${denot2.info}
|they are conflicting definitions$fromWhere"""
throw new MergeError(msg, denot2.info, denot2.info)
if (denot1.isTerm)
throw new MergeError(sym1, sym2, sym1.info, sym2.info, pre) {
override def addendum(implicit ctx: Context) =
i"""
|they are both defined in ${sym1.owner} but have matching signatures
| ${denot1.info} and
| ${denot2.info}${super.addendum}"""
}
else throw new MergeError(sym1, sym2, denot1.info, denot2.info, pre)
}

// --- Overloaded denotations and predenotations -------------------------------------------------
Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/core/TypeApplications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -168,7 +168,7 @@ class TypeApplications(val self: Type) extends AnyVal {
* any type parameter that is-rebound by the refinement.
*/
final def typeParams(implicit ctx: Context): List[TypeParamInfo] = /*>|>*/ track("typeParams") /*<|<*/ {
self match {
try self match {
case self: TypeRef =>
val tsym = self.symbol
if (tsym.isClass) tsym.typeParams
Expand All @@ -193,6 +193,9 @@ class TypeApplications(val self: Type) extends AnyVal {
case _ =>
Nil
}
catch {
case ex: Throwable => handleRecursive("type parameters of", self.show, ex)
}
}

/** If `self` is a higher-kinded type, its type parameters, otherwise Nil */
Expand Down
9 changes: 7 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,11 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
protected def isSubType(tp1: Type, tp2: Type, a: ApproxState): Boolean = {
val saved = approx
this.approx = a
try recur(tp1, tp2) finally this.approx = saved
try recur(tp1, tp2)
catch {
case ex: Throwable => handleRecursive("subtype", i"$tp1 <:< $tp2", ex, weight = 2)
}
finally this.approx = saved
}

protected def isSubType(tp1: Type, tp2: Type): Boolean = isSubType(tp1, tp2, NoApprox)
Expand Down Expand Up @@ -161,7 +165,8 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
try {
pendingSubTypes += p
firstTry
} finally {
}
finally {
pendingSubTypes -= p
}
}
Expand Down
147 changes: 147 additions & 0 deletions compiler/src/dotty/tools/dotc/core/TypeErrors.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
package dotty.tools
package dotc
package core

import util.common._
import Types._
import Symbols._
import Flags._
import Names._
import Contexts._
import SymDenotations._
import Denotations._
import Decorators._
import reporting.diagnostic.Message
import reporting.diagnostic.messages._
import ast.untpd
import config.Printers.cyclicErrors

class TypeError(msg: String) extends Exception(msg) {
def this() = this("")
def toMessage(implicit ctx: Context): Message = getMessage
}

class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name]) extends TypeError {
override def toMessage(implicit ctx: Context): Message =
i"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}"
}

class MissingType(pre: Type, name: Name) extends TypeError {
private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
case pre: ThisType if pre.cls.givenSelfType.exists =>
i"\nor the self type of $pre might not contain all transitive dependencies"
case _ => ""
}

override def toMessage(implicit ctx: Context): Message = {
if (ctx.debug) printStackTrace()
i"""cannot resolve reference to type $pre.$name
|the classfile defining the type might be missing from the classpath${otherReason(pre)}"""
}
}

class RecursionOverflow(val op: String, details: => String, previous: Throwable, val weight: Int) extends TypeError {

def explanation = s"$op $details"

private def recursions: List[RecursionOverflow] = {
val nested = previous match {
case previous: RecursionOverflow => previous.recursions
case _ => Nil
}
this :: nested
}

def opsString(rs: List[RecursionOverflow])(implicit ctx: Context): String = {
val maxShown = 20
if (rs.lengthCompare(maxShown) > 0)
i"""${opsString(rs.take(maxShown / 2))}
| ...
|${opsString(rs.takeRight(maxShown / 2))}"""
else
(rs.map(_.explanation): List[String]).mkString("\n ", "\n| ", "")
}

override def toMessage(implicit ctx: Context): Message = {
val mostCommon = recursions.groupBy(_.op).toList.maxBy(_._2.map(_.weight).sum)._2.reverse
s"""Recursion limit exceeded.
|Maybe there is an illegal cyclic reference?
|If that's not the case, you could also try to increase the stacksize using the -Xss JVM option.
|A recurring operation is (inner to outer):
|${opsString(mostCommon)}""".stripMargin
}

override def fillInStackTrace(): Throwable = this
override def getStackTrace() = previous.getStackTrace()
}

object handleRecursive {
def apply(op: String, details: => String, exc: Throwable, weight: Int = 1): Nothing = exc match {
case _: RecursionOverflow =>
throw new RecursionOverflow(op, details, exc, weight)
case _ =>
var e = exc
while (e != null && !e.isInstanceOf[StackOverflowError]) e = e.getCause
if (e != null) throw new RecursionOverflow(op, details, e, weight)
else throw exc
}
}

class CyclicReference private (val denot: SymDenotation) extends TypeError {

override def toMessage(implicit ctx: Context) = {

def errorMsg(cx: Context): Message =
if (cx.mode is Mode.InferringReturnType) {
cx.tree match {
case tree: untpd.DefDef if !tree.tpt.typeOpt.exists =>
OverloadedOrRecursiveMethodNeedsResultType(tree.name)
case tree: untpd.ValDef if !tree.tpt.typeOpt.exists =>
RecursiveValueNeedsResultType(tree.name)
case _ =>
errorMsg(cx.outer)
}
}
else CyclicReferenceInvolving(denot)

val cycleSym = denot.symbol
if (cycleSym.is(Implicit, butNot = Method) && cycleSym.owner.isTerm)
CyclicReferenceInvolvingImplicit(cycleSym)
else
errorMsg(ctx)
}
}

object CyclicReference {
def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = {
val ex = new CyclicReference(denot)
if (!(ctx.mode is Mode.CheckCyclic)) {
cyclicErrors.println(ex.getMessage)
for (elem <- ex.getStackTrace take 200)
cyclicErrors.println(elem.toString)
}
ex
}
}

class MergeError(val sym1: Symbol, val sym2: Symbol, val tp1: Type, val tp2: Type, prefix: Type) extends TypeError {

private def showSymbol(sym: Symbol)(implicit ctx: Context): String =
if (sym.exists) sym.showLocated else "[unknown]"

private def showType(tp: Type)(implicit ctx: Context) = tp match {
case ClassInfo(_, cls, _, _, _) => cls.showLocated
case _ => tp.show
}

protected def addendum(implicit ctx: Context) =
if (prefix `eq` NoPrefix) "" else i"\nas members of type $prefix"

override def toMessage(implicit ctx: Context): Message = {
if (ctx.debug) printStackTrace()
i"""cannot merge
| ${showSymbol(sym1)} of type ${showType(tp1)} and
| ${showSymbol(sym2)} of type ${showType(tp2)}$addendum
"""
}
}
6 changes: 0 additions & 6 deletions compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -326,10 +326,4 @@ trait TypeOps { this: Context => // TODO: Make standalone object.

object TypeOps {
@sharable var track = false // !!!DEBUG

/** When a property with this key is set in a context, it limits the number
* of recursive member searches. If the limit is reached, findMember returns
* NoDenotation.
*/
val findMemberLimit = new Property.Key[Unit]
}
62 changes: 12 additions & 50 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import util.Positions.{Position, NoPosition}
import util.Stats._
import util.{DotClass, SimpleIdentitySet}
import reporting.diagnostic.Message
import reporting.diagnostic.messages.CyclicReferenceInvolving
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
Expand All @@ -32,10 +31,9 @@ import Uniques._
import collection.{mutable, Seq}
import config.Config
import annotation.tailrec
import Flags.FlagSet
import language.implicitConversions
import scala.util.hashing.{ MurmurHash3 => hashing }
import config.Printers.{core, typr, cyclicErrors}
import config.Printers.{core, typr}
import java.lang.ref.WeakReference

object Types {
Expand Down Expand Up @@ -665,19 +663,22 @@ object Types {
}

val recCount = ctx.findMemberCount
if (recCount >= Config.LogPendingFindMemberThreshold) {
if (ctx.property(TypeOps.findMemberLimit).isDefined &&
ctx.findMemberCount > Config.PendingFindMemberLimit)
return NoDenotation
if (recCount >= Config.LogPendingFindMemberThreshold)
ctx.pendingMemberSearches = name :: ctx.pendingMemberSearches
}
ctx.findMemberCount = recCount + 1
//assert(ctx.findMemberCount < 20)
try go(this)
catch {
case ex: Throwable =>
core.println(i"findMember exception for $this member $name, pre = $pre")
throw ex // DEBUG
core.println(s"findMember exception for $this member $name, pre = $pre, recCount = $recCount")

def showPrefixSafely(pre: Type)(implicit ctx: Context): String = pre.stripTypeVar match {
case pre: TermRef => i"${pre.termSymbol.name}."
case pre: TypeRef => i"${pre.typeSymbol.name}#"
case pre: TypeProxy => showPrefixSafely(pre.underlying)
case _ => if (pre.typeSymbol.exists) i"${pre.typeSymbol.name}#" else "."
}

handleRecursive("find-member", i"${showPrefixSafely(pre)}$name", ex)
}
finally {
if (recCount >= Config.LogPendingFindMemberThreshold)
Expand Down Expand Up @@ -4553,45 +4554,6 @@ object Types {
def apply(pre: Type, name: Name)(implicit ctx: Context): Boolean = true
}

// ----- Exceptions -------------------------------------------------------------

class TypeError(msg: String) extends Exception(msg)

class MalformedType(pre: Type, denot: Denotation, absMembers: Set[Name])
extends TypeError(
s"malformed type: $pre is not a legal prefix for $denot because it contains abstract type member${if (absMembers.size == 1) "" else "s"} ${absMembers.mkString(", ")}")

class MissingType(pre: Type, name: Name)(implicit ctx: Context) extends TypeError(
i"""cannot resolve reference to type $pre.$name
|the classfile defining the type might be missing from the classpath${otherReason(pre)}""") {
if (ctx.debug) printStackTrace()
}

private def otherReason(pre: Type)(implicit ctx: Context): String = pre match {
case pre: ThisType if pre.cls.givenSelfType.exists =>
i"\nor the self type of $pre might not contain all transitive dependencies"
case _ => ""
}

class CyclicReference private (val denot: SymDenotation)
extends TypeError(s"cyclic reference involving $denot") {
def toMessage(implicit ctx: Context) = CyclicReferenceInvolving(denot)
}

object CyclicReference {
def apply(denot: SymDenotation)(implicit ctx: Context): CyclicReference = {
val ex = new CyclicReference(denot)
if (!(ctx.mode is Mode.CheckCyclic)) {
cyclicErrors.println(ex.getMessage)
for (elem <- ex.getStackTrace take 200)
cyclicErrors.println(elem.toString)
}
ex
}
}

class MergeError(msg: String, val tp1: Type, val tp2: Type) extends TypeError(msg)

// ----- Debug ---------------------------------------------------------

@sharable var debugTrace = false
Expand Down
Loading