Skip to content

Fix #1802: Make sure errors are not swept under the carpet #1817

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

Merged
merged 2 commits into from
Dec 18, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -1024,7 +1024,7 @@ class DottyBackendInterface(outputDirectory: AbstractFile, val superCallsMap: Ma
case JavaArrayType(elem) => elem
case _ =>
ctx.error(s"JavaSeqArray with type ${field.tpe} reached backend: $field", field.pos)
ErrorType
UnspecifiedErrorType
}
def _2: List[Tree] = field.elems
}
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/backend/jvm/scalaPrimitives.scala
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import dotty.tools.dotc.ast.tpd._
import dotty.tools.dotc.core.Names.TermName
import dotty.tools.dotc.core.StdNames
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Types.{JavaArrayType, ErrorType, Type}
import dotty.tools.dotc.core.Types.{JavaArrayType, UnspecifiedErrorType, Type}

import scala.collection.{ mutable, immutable }

Expand Down Expand Up @@ -73,7 +73,7 @@ class DottyPrimitives(ctx: Context) {
case JavaArrayType(el) => el
case _ =>
ctx.error(s"expected Array $tpe")
ErrorType
UnspecifiedErrorType
}

code match {
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/ast/Trees.scala
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ object Trees {
* type. (Overridden by empty trees)
*/
def withType(tpe: Type)(implicit ctx: Context): ThisTree[Type] = {
if (tpe == ErrorType) assert(ctx.reporter.errorsReported)
if (tpe.isInstanceOf[ErrorType]) assert(ctx.reporter.errorsReported)
withTypeUnchecked(tpe)
}

Expand Down
11 changes: 6 additions & 5 deletions compiler/src/dotty/tools/dotc/core/SymDenotations.scala
Original file line number Diff line number Diff line change
Expand Up @@ -1937,12 +1937,12 @@ object SymDenotations {
/** A completer for missing references */
class StubInfo() extends LazyType {

def initializeToDefaults(denot: SymDenotation)(implicit ctx: Context) = {
def initializeToDefaults(denot: SymDenotation, errMsg: => String)(implicit ctx: Context) = {
denot.info = denot match {
case denot: ClassDenotation =>
ClassInfo(denot.owner.thisType, denot.classSymbol, Nil, EmptyScope)
case _ =>
ErrorType
new ErrorType(errMsg)
}
denot.privateWithin = NoSymbol
}
Expand All @@ -1954,13 +1954,14 @@ object SymDenotations {
if (file != null) (s" in $file", file.toString)
else ("", "the signature")
val name = ctx.fresh.setSetting(ctx.settings.debugNames, true).nameString(denot.name)
ctx.error(
def errMsg =
i"""bad symbolic reference. A signature$location
|refers to $name in ${denot.owner.showKind} ${denot.owner.showFullName} which is not available.
|It may be completely missing from the current classpath, or the version on
|the classpath might be incompatible with the version used when compiling $src.""")
|the classpath might be incompatible with the version used when compiling $src."""
ctx.error(errMsg)
if (ctx.debug) throw new Error()
initializeToDefaults(denot)
initializeToDefaults(denot, errMsg)
}
}

Expand Down
7 changes: 5 additions & 2 deletions compiler/src/dotty/tools/dotc/core/Symbols.scala
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import NameOps._
import ast.tpd.Tree
import ast.TreeTypeMap
import Constants.Constant
import reporting.diagnostic.Message
import Denotations.{ Denotation, SingleDenotation, MultiDenotation }
import collection.mutable
import io.AbstractFile
Expand Down Expand Up @@ -290,9 +291,11 @@ trait Symbols { this: Context =>
*/
def newSkolem(tp: Type) = newSymbol(defn.RootClass, nme.SKOLEM, SyntheticArtifact | Permanent, tp)

def newErrorSymbol(owner: Symbol, name: Name) =
def newErrorSymbol(owner: Symbol, name: Name, msg: => Message) = {
val errType = new ErrorType(msg)
newSymbol(owner, name, SyntheticArtifact,
if (name.isTypeName) TypeAlias(ErrorType) else ErrorType)
if (name.isTypeName) TypeAlias(errType) else errType)
}

/** Map given symbols, subjecting their attributes to the mappings
* defined in the given TreeTypeMap `ttmap`.
Expand Down
4 changes: 2 additions & 2 deletions compiler/src/dotty/tools/dotc/core/TypeComparer.scala
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
case ConstantType(v1) => v1.value == v2.value
case _ => secondTry(tp1, tp2)
}
case ErrorType =>
case _: FlexType =>
true
case _ =>
secondTry(tp1, tp2)
Expand Down Expand Up @@ -341,7 +341,7 @@ class TypeComparer(initctx: Context) extends DotClass with ConstraintHandling {
false
}
joinOK || isSubType(tp11, tp2) && isSubType(tp12, tp2)
case ErrorType =>
case _: FlexType =>
true
case _ =>
thirdTry(tp1, tp2)
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/core/TypeErasure.scala
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ object TypeErasure {
tp.paramTypes.forall(isErasedType) && isErasedType(tp.resultType)
case tp @ ClassInfo(pre, _, parents, decls, _) =>
isErasedType(pre) && parents.forall(isErasedType) //&& decls.forall(sym => isErasedType(sym.info)) && isErasedType(tp.selfType)
case NoType | NoPrefix | WildcardType | ErrorType | SuperType(_, _) =>
case NoType | NoPrefix | WildcardType | _: ErrorType | SuperType(_, _) =>
true
case _ =>
false
Expand Down Expand Up @@ -398,7 +398,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
tp.derivedClassInfo(NoPrefix, parents, erasedDecls, erasedRef(tp.selfType))
// can't replace selftype by NoType because this would lose the sourceModule link
}
case NoType | NoPrefix | ErrorType | JavaArrayType(_) =>
case NoType | NoPrefix | _: ErrorType | JavaArrayType(_) =>
tp
case tp: WildcardType if wildcardOK =>
tp
Expand Down Expand Up @@ -506,7 +506,7 @@ class TypeErasure(isJava: Boolean, semiEraseVCs: Boolean, isConstructor: Boolean
if (inst.exists) sigName(inst) else tpnme.Uninstantiated
case tp: TypeProxy =>
sigName(tp.underlying)
case ErrorType | WildcardType =>
case _: ErrorType | WildcardType =>
tpnme.WILDCARD
case tp: WildcardType =>
sigName(tp.optBounds)
Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/core/TypeOps.scala
Original file line number Diff line number Diff line change
Expand Up @@ -433,7 +433,7 @@ trait TypeOps { this: Context => // TODO: Make standalone object.
formals = formals.updated(name, tp1.typeParamNamed(name))
}
normalizeToRef(tp1)
case ErrorType =>
case _: ErrorType =>
defn.AnyType
case AnnotatedType(tpe, _) =>
normalizeToRef(tpe)
Expand Down
30 changes: 19 additions & 11 deletions compiler/src/dotty/tools/dotc/core/Types.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,10 @@ import SymDenotations._
import Decorators._
import Denotations._
import Periods._
import util.Positions.Position
import util.Positions.{Position, NoPosition}
import util.Stats._
import util.{DotClass, SimpleMap}
import reporting.diagnostic.Message
import ast.tpd._
import ast.TreeTypeMap
import printing.Texts._
Expand Down Expand Up @@ -176,7 +177,7 @@ object Types {

/** Is this type produced as a repair for an error? */
final def isError(implicit ctx: Context): Boolean = stripTypeVar match {
case ErrorType => true
case _: ErrorType => true
case tp => (tp.typeSymbol is Erroneous) || (tp.termSymbol is Erroneous)
}

Expand Down Expand Up @@ -387,8 +388,8 @@ object Types {
tp.decls.denotsNamed(name).filterExcluded(excluded).toDenot(NoPrefix)
case tp: TypeProxy =>
tp.underlying.findDecl(name, excluded)
case ErrorType =>
ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name)
case err: ErrorType =>
ctx.newErrorSymbol(classSymbol orElse defn.RootClass, name, err.msg)
case _ =>
NoDenotation
}
Expand Down Expand Up @@ -453,8 +454,8 @@ object Types {
go(tp.join)
case tp: JavaArrayType =>
defn.ObjectType.findMember(name, pre, excluded)
case ErrorType =>
ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name)
case err: ErrorType =>
ctx.newErrorSymbol(pre.classSymbol orElse defn.RootClass, name, err.msg)
case _ =>
NoDenotation
}
Expand Down Expand Up @@ -1497,7 +1498,7 @@ object Types {
(lastDefRunId != sym.defRunId) ||
(lastDefRunId == NoRunId)
} ||
(lastSymbol.infoOrCompleter == ErrorType ||
(lastSymbol.infoOrCompleter.isInstanceOf[ErrorType] ||
sym.owner != lastSymbol.owner &&
(sym.owner.derivesFrom(lastSymbol.owner) ||
selfTypeOf(sym).derivesFrom(lastSymbol.owner) ||
Expand Down Expand Up @@ -2693,7 +2694,7 @@ object Types {
protected def checkInst(implicit ctx: Context): this.type = {
def check(tycon: Type): Unit = tycon.stripTypeVar match {
case tycon: TypeRef if !tycon.symbol.isClass =>
case _: PolyParam | ErrorType | _: WildcardType =>
case _: PolyParam | _: ErrorType | _: WildcardType =>
case _: PolyType =>
assert(args.exists(_.isInstanceOf[TypeBounds]), s"unreduced type apply: $this")
case tycon: AnnotatedType =>
Expand Down Expand Up @@ -3251,12 +3252,19 @@ object Types {
override def computeHash = hashSeed
}

abstract class ErrorType extends UncachedGroundType with ValueType
/** A common superclass of `ErrorType` and `TryDynamicCallSite`. Instances of this
* class are at the same time subtypes and supertypes of every other type.
*/
abstract class FlexType extends UncachedGroundType with ValueType
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you add some documentation on the meaning of FlexType?


class ErrorType(_msg: => Message) extends FlexType {
val msg = _msg
}

object ErrorType extends ErrorType
object UnspecifiedErrorType extends ErrorType("unspecified error")

/* Type used to track Select nodes that could not resolve a member and their qualifier is a scala.Dynamic. */
object TryDynamicCallType extends ErrorType
object TryDynamicCallType extends FlexType

/** Wildcard type, possibly with bounds */
abstract case class WildcardType(optBounds: Type) extends CachedGroundType with TermType {
Expand Down
3 changes: 2 additions & 1 deletion compiler/src/dotty/tools/dotc/parsing/JavaParsers.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ object JavaParsers {
if (skipIt)
skip()
}
def errorTypeTree = TypeTree().withType(ErrorType) withPos Position(in.offset)

def errorTypeTree = TypeTree().withType(UnspecifiedErrorType) withPos Position(in.offset)

// --------- tree building -----------------------------

Expand Down
5 changes: 4 additions & 1 deletion compiler/src/dotty/tools/dotc/printing/Formatting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,10 @@ object Formatting {
case tpe: Type =>
tpe.exists && !tpe.isErroneous
case sym: Symbol if sym.isCompleted =>
sym.info != ErrorType && sym.info != TypeAlias(ErrorType) && sym.info.exists
sym.info match {
case _: ErrorType | TypeAlias(_: ErrorType) | NoType => false
case _ => true
}
case _ => true
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/printing/PlainPrinter.scala
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ class PlainPrinter(_ctx: Context) extends Printer {
changePrec(AndPrec) { toText(tp1) ~ " & " ~ toText(tp2) }
case OrType(tp1, tp2) =>
changePrec(OrPrec) { toText(tp1) ~ " | " ~ toText(tp2) }
case ErrorType =>
case _: ErrorType =>
"<error>"
case tp: WildcardType =>
if (tp.optBounds.exists) "(?" ~ toTextRHS(tp.bounds) ~ ")" else "?"
Expand Down
6 changes: 3 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Applications.scala
Original file line number Diff line number Diff line change
Expand Up @@ -561,7 +561,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
var typedArgs = typedArgBuf.toList
def app0 = cpy.Apply(app)(normalizedFun, typedArgs) // needs to be a `def` because typedArgs can change later
val app1 =
if (!success) app0.withType(ErrorType)
if (!success) app0.withType(UnspecifiedErrorType)
else {
if (!sameSeq(args, orderedArgs)) {
// need to lift arguments to maintain evaluation order in the
Expand Down Expand Up @@ -654,7 +654,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
}

fun1.tpe match {
case ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(ErrorType)
case err: ErrorType => untpd.cpy.Apply(tree)(fun1, tree.args).withType(err)
case TryDynamicCallType => typedDynamicApply(tree, pt)
case _ =>
tryEither {
Expand Down Expand Up @@ -918,7 +918,7 @@ trait Applications extends Compatibility { self: Typer with Dynamic =>
case tp =>
val unapplyErr = if (tp.isError) unapplyFn else notAnExtractor(unapplyFn)
val typedArgsErr = args mapconserve (typed(_, defn.AnyType))
cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType ErrorType
cpy.UnApply(tree)(unapplyErr, Nil, typedArgsErr) withType unapplyErr.tpe
}
}

Expand Down
5 changes: 2 additions & 3 deletions compiler/src/dotty/tools/dotc/typer/Checking.scala
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ object Checking {
def checkBounds(args: List[tpd.Tree], poly: PolyType)(implicit ctx: Context): Unit =
checkBounds(args, poly.paramBounds, _.substParams(poly, _))

/** Check applied type trees for well-formedness. This means
/** Check applied type trees for well-formedness. This means
* - all arguments are within their corresponding bounds
* - if type is a higher-kinded application with wildcard arguments,
* check that it or one of its supertypes can be reduced to a normal application.
Expand Down Expand Up @@ -237,8 +237,7 @@ object Checking {
catch {
case ex: CyclicReference =>
if (reportErrors) {
ctx.error(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos)
ErrorType
errorType(i"illegal cyclic reference: ${checker.where} ${checker.lastChecked} of $sym refers back to the type itself", sym.pos)
}
else info
}
Expand Down
11 changes: 5 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/Dynamic.scala
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.StdNames._
import dotty.tools.dotc.core.Types._
import dotty.tools.dotc.core.Decorators._
import ErrorReporting._

object Dynamic {
def isDynamicMethod(name: Name): Boolean =
Expand Down Expand Up @@ -41,10 +42,9 @@ trait Dynamic { self: Typer with Applications =>
def isNamedArg(arg: untpd.Tree): Boolean = arg match { case NamedArg(_, _) => true; case _ => false }
val args = tree.args
val dynName = if (args.exists(isNamedArg)) nme.applyDynamicNamed else nme.applyDynamic
if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args)) {
ctx.error("applyDynamicNamed does not support passing a vararg parameter", tree.pos)
tree.withType(ErrorType)
} else {
if (dynName == nme.applyDynamicNamed && untpd.isWildcardStarArgList(args))
errorTree(tree, "applyDynamicNamed does not support passing a vararg parameter")
else {
def namedArgTuple(name: String, arg: untpd.Tree) = untpd.Tuple(List(Literal(Constant(name)), arg))
def namedArgs = args.map {
case NamedArg(argName, arg) => namedArgTuple(argName.toString, arg)
Expand Down Expand Up @@ -89,8 +89,7 @@ trait Dynamic { self: Typer with Applications =>
case TypeApply(Select(qual, name), targs) if !isDynamicMethod(name) =>
typedDynamicAssign(qual, name, targs)
case _ =>
ctx.error("reassignment to val", tree.pos)
tree.withType(ErrorType)
errorTree(tree, "reassignment to val")
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/src/dotty/tools/dotc/typer/ErrorReporting.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ object ErrorReporting {

def errorType(msg: => Message, pos: Position)(implicit ctx: Context): ErrorType = {
ctx.error(msg, pos)
ErrorType
new ErrorType(msg)
}

def cyclicErrorMsg(ex: CyclicReference)(implicit ctx: Context) = {
Expand Down
11 changes: 5 additions & 6 deletions compiler/src/dotty/tools/dotc/typer/TypeAssigner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -189,9 +189,8 @@ trait TypeAssigner {
val where = if (ctx.owner.exists) s" from ${ctx.owner.enclosingClass}" else ""
val whyNot = new StringBuffer
alts foreach (_.isAccessibleFrom(pre, superAccess, whyNot))
if (!tpe.isError)
ctx.error(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos)
ErrorType
if (tpe.isError) tpe
else errorType(ex"$what cannot be accessed as a member of $pre$where.$whyNot", pos)
}
}
else if (d.symbol is TypeParamAccessor)
Expand All @@ -215,17 +214,17 @@ trait TypeAssigner {
else if (site.derivesFrom(defn.DynamicClass) && !Dynamic.isDynamicMethod(name)) {
TryDynamicCallType
} else {
if (!site.isErroneous) {
if (site.isErroneous) UnspecifiedErrorType
else {
def kind = if (name.isTypeName) "type" else "value"
def addendum =
if (site.derivesFrom(defn.DynamicClass)) "\npossible cause: maybe a wrong Dynamic method signature?"
else ""
ctx.error(
errorType(
if (name == nme.CONSTRUCTOR) ex"$site does not have a constructor"
else NotAMember(site, name, kind),
pos)
}
ErrorType
}
}

Expand Down
Loading