From ec5eaee3ec27b614c3ffe0496a755623c912cfdd Mon Sep 17 00:00:00 2001 From: Jason Zaugg Date: Wed, 10 Apr 2013 11:52:01 +0200 Subject: SI-7345 More refactoring and documentation in Contexts - Use `firstError match {}` rather than `if (c.hasErrors) c.firstError ..` - Convert implementation comments to doc comments - Add new doc comments - Refactor `Context#make` for better readability. - Note a few TODOs. - Roughly delineate the internal structure of Contexts with comments. - Combine `mode` and `state` methods into `contextMode`. - Move `isNameInScope` closer to its compadres. - Improving alignment, tightening access. --- .../scala/reflect/macros/runtime/Typers.scala | 7 +- .../scala/tools/nsc/typechecker/Contexts.scala | 260 ++++++++++++++------- .../scala/tools/nsc/typechecker/Implicits.scala | 40 ++-- .../scala/tools/nsc/typechecker/Infer.scala | 19 +- .../scala/tools/nsc/typechecker/Typers.scala | 38 +-- .../scala/tools/reflect/ToolBoxFactory.scala | 4 +- 6 files changed, 238 insertions(+), 130 deletions(-) (limited to 'src') diff --git a/src/compiler/scala/reflect/macros/runtime/Typers.scala b/src/compiler/scala/reflect/macros/runtime/Typers.scala index 99b934787d..79833d155e 100644 --- a/src/compiler/scala/reflect/macros/runtime/Typers.scala +++ b/src/compiler/scala/reflect/macros/runtime/Typers.scala @@ -54,11 +54,10 @@ trait Typers { wrapper(universe.analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos)) match { case failure if failure.tree.isEmpty => macroLogVerbose("implicit search has failed. to find out the reason, turn on -Xlog-implicits") - if (context.hasErrors) { - val err = context.firstError - throw new TypecheckException(err.errPos, err.errMsg) + context.firstError match { + case Some(err) => throw new TypecheckException(err.errPos, err.errMsg) + case None => universe.EmptyTree } - universe.EmptyTree case success => success.tree } diff --git a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala index 22cda50591..4b9d3fda01 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Contexts.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Contexts.scala @@ -111,37 +111,58 @@ trait Contexts { self: Analyzer => class Context private[typechecker] { var unit: CompilationUnit = NoCompilationUnit - var tree: Tree = _ // Tree associated with this context - var owner: Symbol = NoSymbol // The current owner - var scope: Scope = _ // The current scope - var outer: Context = _ // The next outer context - var enclClass: Context = _ // The next outer context whose tree is a - // template or package definition - @inline final def savingEnclClass[A](c: Context)(a: => A): A = { + /** Tree associated with this context */ + var tree: Tree = _ + /** The current owner */ + var owner: Symbol = NoSymbol + /** The current scope */ + var scope: Scope = _ + /** The next outer context */ + var outer: Context = _ + /** The next outer context whose tree is a template or package definition */ + var enclClass: Context = _ + + @inline private def savingEnclClass[A](c: Context)(a: => A): A = { val saved = enclClass enclClass = c try a finally enclClass = saved } - private var mode: ContextMode = ContextMode.DefaultMode + /** A bitmask containing all the boolean flags in a context, e.g. are implicit views enabled */ + var contextMode: ContextMode = ContextMode.DefaultMode + + /** Update all modes in `mask` to `value` */ def update(mask: ContextMode, value: Boolean) { - mode = mode.set(value, mask) + contextMode = contextMode.set(value, mask) } + + /** Set all modes in the mask `enable` to true, and all in `disable` to false. */ def set(enable: ContextMode = NOmode, disable: ContextMode = NOmode): this.type = { - mode = mode.set(true, enable).set(false, disable) - this + contextMode = contextMode.set(true, enable).set(false, disable) + this } - def apply(mask: ContextMode): Boolean = mode.inAll(mask) - var enclMethod: Context = _ // The next outer context whose tree is a method - var variance: Variance = Variance.Invariant // Variance relative to enclosing class - private var _undetparams: List[Symbol] = List() // Undetermined type parameters, - // not inherited to child contexts + /** Is this context in all modes in the given `mask`? */ + def apply(mask: ContextMode): Boolean = contextMode.inAll(mask) + + /** The next outer context whose tree is a method */ + var enclMethod: Context = _ + + /** Variance relative to enclosing class */ + var variance: Variance = Variance.Invariant + + /** Undetermined type parameters. Not inherited to child contexts */ + private var _undetparams: List[Symbol] = List() + var depth: Int = 0 - var imports: List[ImportInfo] = List() // currently visible imports - var openImplicits: List[(Type,Tree)] = List() // types for which implicit arguments - // are currently searched - // for a named application block (Tree) the corresponding NamedApplyInfo + + /** The currently visible imports */ + var imports: List[ImportInfo] = List() + + /** Types for which implicit arguments are currently searched */ + var openImplicits: List[(Type,Tree)] = List() + + /* For a named application block (`Tree`) the corresponding `NamedApplyInfo`. */ var namedApplyBlockInfo: Option[(Tree, NamedApplyInfo)] = None var prefix: Type = NoPrefix @@ -151,9 +172,6 @@ trait Contexts { self: Analyzer => def returnsSeen = this(ReturnsSeen) def inSelfSuperCall_=(value: Boolean) = this(SelfSuperCall) = value def inSelfSuperCall = this(SelfSuperCall) - - var diagnostic: List[String] = Nil // these messages are printed when issuing an error - def implicitsEnabled_=(value: Boolean) = this(ImplicitsEnabled) = value def implicitsEnabled = this(ImplicitsEnabled) def macrosEnabled_=(value: Boolean) = this(MacrosEnabled) = value @@ -165,45 +183,61 @@ trait Contexts { self: Analyzer => def retyping_=(value: Boolean) = this(ReTyping) = value def retyping = this(ReTyping) - var savedTypeBounds: List[(Symbol, Type)] = List() // saved type bounds - // for type parameters which are narrowed in a GADT + /** These messages are printed when issuing an error */ + var diagnostic: List[String] = Nil + + /** Saved type bounds for type parameters which are narrowed in a GADT. */ + var savedTypeBounds: List[(Symbol, Type)] = List() + /** Indentation level, in columns, for output under -Ytyper-debug */ var typingIndentLevel: Int = 0 def typingIndent = " " * typingIndentLevel - var _reportBuffer: ReportBuffer = new ReportBuffer - def reportBuffer = _reportBuffer - + /** The next enclosing context (potentially `this`) that is owned by a class or method */ def enclClassOrMethod: Context = if ((owner eq NoSymbol) || (owner.isClass) || (owner.isMethod)) this else outer.enclClassOrMethod + /** The next enclosing context (potentially `this`) that has a `CaseDef` as a tree */ def enclosingCaseDef = nextEnclosing(_.tree.isInstanceOf[CaseDef]) + def undetparamsString = if (undetparams.isEmpty) "" else undetparams.mkString("undetparams=", ", ", "") - def undetparams = _undetparams + /** Undetermined type parameters. See `Infer#{inferExprInstance, adjustTypeArgs}`. */ + def undetparams: List[Symbol] = _undetparams def undetparams_=(ps: List[Symbol]) = { _undetparams = ps } - def extractUndetparams() = { + /** Return and clear the undetermined type parameters */ + def extractUndetparams(): List[Symbol] = { val tparams = undetparams undetparams = List() tparams } - def errBuffer = reportBuffer.errors - // TODO use Option[Error] this rather then `if (c.hasErrors) c.firstError._` - def firstError: AbsTypeError = reportBuffer.firstError - def hasErrors = reportBuffer.hasErrors - def hasWarnings = reportBuffer.hasWarnings + // + // Error reporting policies and buffer. + // - def state: ContextMode = mode - def restoreState(state0: ContextMode) = mode = state0 + private var _reportBuffer: ReportBuffer = new ReportBuffer + /** A buffer for errors and warnings, used with `this.bufferErrors == true` */ + def reportBuffer = _reportBuffer + /** Discard the current report buffer, and replace with an empty one */ + def useFreshReportBuffer() = _reportBuffer = new ReportBuffer + /** Discard the current report buffer, and replace with `other` */ + def restoreReportBuffer(other: ReportBuffer) = _reportBuffer = other + + /** The first error, if any, in the report buffer */ + def firstError: Option[AbsTypeError] = reportBuffer.firstErrorOpt + /** The first warning, if any, in the report buffer */ + def firstWarning: Option[(Position, String)] = reportBuffer.firstWarningOpt + /** Does the report buffer contain any errors? */ + def hasErrors = reportBuffer.hasErrors def reportErrors = this(ReportErrors) def bufferErrors = this(BufferErrors) def ambiguousErrors = this(AmbiguousErrors) - def throwErrors = mode.inNone(ReportErrors | BufferErrors) + def throwErrors = contextMode.inNone(ReportErrors | BufferErrors) def setReportErrors(): Unit = set(enable = ReportErrors | AmbiguousErrors, disable = BufferErrors) def setBufferErrors(): Unit = {set(enable = BufferErrors, disable = ReportErrors | AmbiguousErrors); warnIfBufferNotClean()} @@ -215,24 +249,34 @@ trait Contexts { self: Analyzer => devWarning("When entering the buffer state, context has to be clean. Current buffer: " + reportBuffer.errors) } + // TODO SI-7345 refactor this part of the API. + + /** Append the given errors to the report buffer */ def updateBuffer(errors: Traversable[AbsTypeError]) = reportBuffer ++= errors + /** Clear all errors from the report buffer */ def flushBuffer() { reportBuffer.clearAllErrors() } + /** Return and clear all errors from the report buffer */ def flushAndReturnBuffer(): immutable.Seq[AbsTypeError] = { val current = reportBuffer.errors reportBuffer.clearAllErrors() current } + /** Return and clear all warnings from the report buffer */ def flushAndReturnWarningsBuffer(): immutable.Seq[(Position, String)] = { val current = reportBuffer.warnings reportBuffer.clearAllWarnings() current } + // + // Temporary mode adjustment + // + private def withMode[T](enabled: ContextMode = NOmode, disabled: ContextMode = NOmode)(op: => T): T = { - val saved = mode + val saved = contextMode set(enabled, disabled) try op - finally mode = saved + finally contextMode = saved } def withImplicitsEnabled[T](op: => T): T = withMode(enabled = ImplicitsEnabled)(op) @@ -241,19 +285,17 @@ trait Contexts { self: Analyzer => def withMacrosEnabled[T](op: => T): T = withMode(enabled = MacrosEnabled)(op) def withMacrosDisabled[T](op: => T): T = withMode(disabled = MacrosEnabled)(op) + // + // Child Context Creation + // + + /** + * Construct a child context. The parent and child will share the report buffer. + * Compare with `makeSilent`, in which the child has a fresh report buffer. + */ def make(tree: Tree = tree, owner: Symbol = owner, scope: Scope = scope, imports: List[ImportInfo] = imports, unit: CompilationUnit = unit): Context = { - - val c = new Context - c.unit = unit - c.tree = tree - c.owner = owner - c.scope = scope - c.outer = this - - c.restoreState(this.state) // note: ConstructorSuffix conditionally overwritten below. - val isTemplateOrPackage = tree match { case _: Template | _: PackageDef => true case _ => false @@ -262,28 +304,49 @@ trait Contexts { self: Analyzer => case _: DefDef => true case _ => false } - - c.prefix = - if (isTemplateOrPackage) c.owner.thisType - else if (c.owner != this.owner && c.isLocal) NoPrefix + val sameOwner = owner == this.owner + val sameScope = scope == this.scope + val prefixInChild = + if (isTemplateOrPackage) owner.thisType + else if (!sameOwner && owner.isTerm) NoPrefix else prefix - c.enclClass = if (isTemplateOrPackage) c else enclClass + + // The blank canvas + val c = new Context + + // Fields that are directly propagated + c.unit = unit + c.tree = tree + c.owner = owner + c.scope = scope + c.variance = variance + c.imports = imports + c.diagnostic = diagnostic + c.typingIndentLevel = typingIndentLevel + c.openImplicits = openImplicits + c.contextMode = contextMode // note: ConstructorSuffix, a bit within `mode`, is conditionally overwritten below. + c._reportBuffer = reportBuffer + + // Fields that may take on a different value in the child + c.outer = this + c.prefix = prefixInChild + c.enclClass = if (isTemplateOrPackage) c else enclClass c(ConstructorSuffix) = !isTemplateOrPackage && c(ConstructorSuffix) - c.enclMethod = if (isDefDef) c else enclMethod - - c.variance = this.variance - c.depth = this.depth + (if (scope == this.scope) 0 else 1) - c.imports = imports - c.diagnostic = this.diagnostic - c.typingIndentLevel = typingIndentLevel - c.openImplicits = this.openImplicits - // Usually, the parent and child contexts share the report buffer. Exception: `makeSilent` will use a fresh buffer. - c._reportBuffer = this.reportBuffer + c.enclMethod = if (isDefDef) c else enclMethod + c.depth = depth + (if (sameScope) 0 else 1) + registerContext(c.asInstanceOf[analyzer.Context]) debuglog("[context] ++ " + c.unit + " / " + tree.summaryString) c } + def make(tree: Tree, owner: Symbol, scope: Scope): Context = + // TODO SI-7345 Moving this optimization into the main overload of `make` causes all tests to fail. + // even if it is extened to check that `unit == this.unit`. Why is this? + if (tree == this.tree && owner == this.owner && scope == this.scope) this + else make(tree, owner, scope, imports, unit) + + /** Make a child context that includes `imp` in the list of visible imports */ def makeNewImport(imp: Import): Context = { val impInfo = new ImportInfo(imp, depth) if (settings.lint && imp.pos.isDefined) // pos.isDefined excludes java.lang/scala/Predef imports @@ -292,14 +355,11 @@ trait Contexts { self: Analyzer => make(imp, imports = impInfo :: imports) } - def make(tree: Tree, owner: Symbol, scope: Scope): Context = - // TODO Moving this optimization into the main overload of `make` causes all tests to fail. Why? - if (tree == this.tree && owner == this.owner && scope == this.scope) this - else make(tree, owner, scope, imports, unit) - + /** Make a child context that represents a new nested scope */ def makeNewScope(tree: Tree, owner: Symbol): Context = make(tree, owner, newNestedScope(scope)) + /** Make a child context that buffers errors and warnings into a fresh report buffer. */ def makeSilent(reportAmbiguousErrors: Boolean, newtree: Tree = tree): Context = { val c = make(newtree) c.setBufferErrors() @@ -308,6 +368,7 @@ trait Contexts { self: Analyzer => c } + /** Make a silent child context does not allow implicits. Used to prevent chaining of implicit views. */ def makeImplicit(reportAmbiguousErrors: Boolean) = { val c = makeSilent(reportAmbiguousErrors) c(ImplicitsEnabled | EnrichmentEnabled) = false @@ -327,7 +388,7 @@ trait Contexts { self: Analyzer => def makeConstructorContext = { val baseContext = enclClass.outer.nextEnclosing(!_.tree.isInstanceOf[Template]) val argContext = baseContext.makeNewScope(tree, owner) - argContext.restoreState(state) + argContext.contextMode = contextMode argContext.inSelfSuperCall = true def enterElems(c: Context) { def enterLocalElems(e: ScopeEntry) { @@ -347,6 +408,10 @@ trait Contexts { self: Analyzer => argContext } + // + // Error and warning issuance + // + private def addDiagString(msg: String) = { val ds = if (diagnostic.isEmpty) "" @@ -367,12 +432,14 @@ trait Contexts { self: Analyzer => else throw new TypeError(err.errPos, err.errMsg) } + /** Issue/buffer/throw the given type error according to the current mode for error reporting. */ def issue(err: AbsTypeError) { issueCommon(err) { case _ if reportErrors => unitError(err.errPos, addDiagString(err.errMsg)) } } + /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ def issueAmbiguousError(pre: Type, sym1: Symbol, sym2: Symbol, err: AbsTypeError) { issueCommon(err) { case _ if ambiguousErrors => if (!pre.isErroneous && !sym1.isErroneous && !sym2.isErroneous) @@ -380,23 +447,25 @@ trait Contexts { self: Analyzer => } } + /** Issue/buffer/throw the given implicit ambiguity error according to the current mode for error reporting. */ def issueAmbiguousError(err: AbsTypeError) { issueCommon(err) { case _ if ambiguousErrors => unitError(err.errPos, addDiagString(err.errMsg)) } } - // TODO remove + /** Issue/throw the given `err` according to the current mode for error reporting. */ def error(pos: Position, err: Throwable) = if (reportErrors) unitError(pos, addDiagString(err.getMessage())) else throw err + /** Issue/throw the given error message according to the current mode for error reporting. */ def error(pos: Position, msg: String) = { val msg1 = addDiagString(msg) if (reportErrors) unitError(pos, msg1) else throw new TypeError(pos, msg1) } - def warning(pos: Position, msg: String): Unit = warning(pos, msg, force = false) - def warning(pos: Position, msg: String, force: Boolean) { + /** Issue/throw the given error message according to the current mode for error reporting. */ + def warning(pos: Position, msg: String, force: Boolean = false) { if (reportErrors || force) unit.warning(pos, msg) else if (bufferErrors) reportBuffer += (pos -> msg) } @@ -404,8 +473,6 @@ trait Contexts { self: Analyzer => /** Is the owning symbol of this context a term? */ final def isLocal: Boolean = owner.isTerm - def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess - // nextOuter determines which context is searched next for implicits // (after `this`, which contributes `newImplicits` below.) In // most cases, it is simply the outer context: if we're owned by @@ -433,11 +500,15 @@ trait Contexts { self: Analyzer => override def toString = "Context(%s@%s unit=%s scope=%s errors=%b, reportErrors=%b, throwErrors=%b)".format( owner.fullName, tree.shortClass, unit, scope.##, hasErrors, reportErrors, throwErrors ) - /** Is `sub` a subclass of `base` or a companion object of such a subclass? - */ - def isSubClassOrCompanion(sub: Symbol, base: Symbol) = + + // + // Accessibility checking + // + + /** Is `sub` a subclass of `base` or a companion object of such a subclass? */ + private def isSubClassOrCompanion(sub: Symbol, base: Symbol) = sub.isNonBottomSubClass(base) || - sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) + sub.isModuleClass && sub.linkedClassOfClass.isNonBottomSubClass(base) /** Return the closest enclosing context that defines a subclass of `clazz` * or a companion object thereof, or `NoContext` if no such context exists. @@ -449,8 +520,7 @@ trait Contexts { self: Analyzer => c } - /** Is `sym` accessible as a member of `pre` in current context? - */ + /** Is `sym` accessible as a member of `pre` in current context? */ def isAccessible(sym: Symbol, pre: Type, superAccess: Boolean = false): Boolean = { lastAccessCheckDetails = "" // Console.println("isAccessible(%s, %s, %s)".format(sym, pre, superAccess)) @@ -530,6 +600,10 @@ trait Contexts { self: Analyzer => } } + // + // Type bound management + // + def pushTypeBounds(sym: Symbol) { sym.info match { case tb: TypeBounds => if (!tb.isEmptyBounds) log(s"Saving $sym info=$tb") @@ -566,6 +640,10 @@ trait Contexts { self: Analyzer => } } + // + // Implicit collection + // + private var implicitsCache: List[List[ImplicitInfo]] = null private var implicitsRunId = NoRunId @@ -654,6 +732,10 @@ trait Contexts { self: Analyzer => implicitsCache } + // + // Imports and symbol lookup + // + /** It's possible that seemingly conflicting identifiers are * identifiably the same after type normalization. In such cases, * allow compilation to proceed. A typical example is: @@ -763,6 +845,8 @@ trait Contexts { self: Analyzer => ) } + def isNameInScope(name: Name) = lookupSymbol(name, _ => true).isSuccess + /** Find the symbol of a simple name starting from this context. * All names are filtered through the "qualifies" predicate, * the search continuing as long as no qualifying name is found. @@ -999,6 +1083,10 @@ trait Contexts { self: Analyzer => this } + def clearAll(): this.type = { + clearAllErrors(); clearAllWarnings(); + } + def clearAllErrors(): this.type = { errorBuffer.clear() this @@ -1016,9 +1104,11 @@ trait Contexts { self: Analyzer => this } - def hasErrors = errorBuffer.nonEmpty - def hasWarnings = warningBuffer.nonEmpty - def firstError = errorBuffer.head + def hasErrors = errorBuffer.nonEmpty + def hasWarnings = warningBuffer.nonEmpty + def firstError = errorBuffer.head + def firstErrorOpt = errorBuffer.headOption + def firstWarningOpt = warningBuffer.headOption } class ImportInfo(val tree: Import, val depth: Int) { diff --git a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala index f7f6970c14..b0c8baae20 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Implicits.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Implicits.scala @@ -584,10 +584,11 @@ trait Implicits { ) case _ => fallback } - if (context.hasErrors) { - val err = context.firstError - log("implicit adapt failed: " + err.errMsg) - return fail(err.errMsg) + context.firstError match { // using match rather than foreach to avoid non local return. + case Some(err) => + log("implicit adapt failed: " + err.errMsg) + return fail(err.errMsg) + case None => } if (Statistics.canEnable) Statistics.incCounter(typedImplicits) @@ -610,7 +611,7 @@ trait Implicits { } if (context.hasErrors) - fail("hasMatchingSymbol reported error: " + context.firstError.errMsg) + fail("hasMatchingSymbol reported error: " + context.firstError.get.errMsg) else if (isLocal && !hasMatchingSymbol(itree1)) fail("candidate implicit %s is shadowed by %s".format( info.sym.fullLocationString, itree1.symbol.fullLocationString)) @@ -633,8 +634,11 @@ trait Implicits { // #2421: check that we correctly instantiated type parameters outside of the implicit tree: checkBounds(itree2, NoPrefix, NoSymbol, undetParams, targs, "inferred ") - if (context.hasErrors) - return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + context.firstError.errMsg) + context.firstError match { + case Some(err) => + return fail("type parameters weren't correctly instantiated outside of the implicit tree: " + err.errMsg) + case None => + } // filter out failures from type inference, don't want to remove them from undetParams! // we must be conservative in leaving type params in undetparams @@ -669,13 +673,14 @@ trait Implicits { case t => t } - if (context.hasErrors) - fail("typing TypeApply reported errors for the implicit tree: " + context.firstError.errMsg) - else { - val result = new SearchResult(itree2, subst) - if (Statistics.canEnable) Statistics.incCounter(foundImplicits) - printInference("[success] found %s for pt %s".format(result, ptInstantiated)) - result + context.firstError match { + case Some(err) => + fail("typing TypeApply reported errors for the implicit tree: " + err.errMsg) + case None => + val result = new SearchResult(itree2, subst) + if (Statistics.canEnable) Statistics.incCounter(foundImplicits) + printInference("[success] found %s for pt %s".format(result, ptInstantiated)) + result } } else fail("incompatible: %s does not match expected type %s".format(itree2.tpe, ptInstantiated)) @@ -1086,11 +1091,10 @@ trait Implicits { try { val tree1 = typedPos(pos.focus)(arg) - if (context.hasErrors) { - val err = context.firstError - processMacroExpansionError(err.errPos, err.errMsg) + context.firstError match { + case Some(err) => processMacroExpansionError(err.errPos, err.errMsg) + case None => new SearchResult(tree1, EmptyTreeTypeSubstituter) } - else new SearchResult(tree1, EmptyTreeTypeSubstituter) } catch { case ex: TypeError => processMacroExpansionError(ex.pos, ex.msg) diff --git a/src/compiler/scala/tools/nsc/typechecker/Infer.scala b/src/compiler/scala/tools/nsc/typechecker/Infer.scala index 8d7830897d..8c87cf51d3 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Infer.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Infer.scala @@ -1531,14 +1531,15 @@ trait Infer extends Checkable { } } + /** @return true if the `expr` evaluates to true within a silent Context that incurs no errors */ @inline private def inSilentMode(context: Context)(expr: => Boolean): Boolean = { - val oldState = context.state + val savedContextMode = context.contextMode context.setBufferErrors() val res = expr - val contextWithErrors = context.hasErrors - context.flushBuffer() - context.restoreState(oldState) - res && !contextWithErrors + val contextHadErrors = context.hasErrors + context.reportBuffer.clearAll() + context.contextMode = savedContextMode + res && !contextHadErrors } // Checks against the name of the parameter and also any @deprecatedName. @@ -1636,7 +1637,7 @@ trait Infer extends Checkable { */ def tryTwice(infer: Boolean => Unit): Unit = { if (context.implicitsEnabled) { - val saved = context.state + val savedContextMode = context.contextMode var fallback = false context.setBufferErrors() // We cache the current buffer because it is impossible to @@ -1650,17 +1651,17 @@ trait Infer extends Checkable { context.withImplicitsDisabled(infer(false)) if (context.hasErrors) { fallback = true - context.restoreState(saved) + context.contextMode = savedContextMode context.flushBuffer() infer(true) } } catch { case ex: CyclicReference => throw ex case ex: TypeError => // recoverable cyclic references - context.restoreState(saved) + context.contextMode = savedContextMode if (!fallback) infer(true) else () } finally { - context.restoreState(saved) + context.contextMode = savedContextMode context.updateBuffer(errorsToRestore) } } diff --git a/src/compiler/scala/tools/nsc/typechecker/Typers.scala b/src/compiler/scala/tools/nsc/typechecker/Typers.scala index f57352b11e..3736380954 100644 --- a/src/compiler/scala/tools/nsc/typechecker/Typers.scala +++ b/src/compiler/scala/tools/nsc/typechecker/Typers.scala @@ -492,8 +492,16 @@ trait Typers extends Adaptations with Tags { @inline final def typerReportAnyContextErrors[T](c: Context)(f: Typer => T): T = { val res = f(newTyper(c)) - if (c.hasErrors) - context.issue(c.firstError) + c.firstError match { + case Some(err) => + context.issue(err) + c.reportBuffer.warnings foreach { + case (pos, msg) => context.warning(pos, msg) + } + // Seemingly we should perform `c.reportBuffer.clearAll()` here. But + // `context` might share the ReportBuffer with `c`. + case None => + } res } @@ -687,17 +695,18 @@ trait Typers extends Adaptations with Tags { context.undetparams = context1.undetparams context.savedTypeBounds = context1.savedTypeBounds context.namedApplyBlockInfo = context1.namedApplyBlockInfo - if (context1.hasErrors) { - stopStats() - SilentTypeError(context1.firstError) - } else { - // If we have a successful result, emit any warnings it created. - if (context1.hasWarnings) { - context1.flushAndReturnWarningsBuffer() foreach { - case (pos, msg) => unit.warning(pos, msg) + context1.firstError match { + case Some(err) => + stopStats() + SilentTypeError(err) + case None => + // If we have a successful result, emit any warnings it created. + if (context1.reportBuffer.hasWarnings) { + context1.flushAndReturnWarningsBuffer() foreach { + case (pos, msg) => unit.warning(pos, msg) + } } - } - SilentResultValue(result) + SilentResultValue(result) } } else { assert(context.bufferErrors || isPastTyper, "silent mode is not available past typer") @@ -1174,7 +1183,10 @@ trait Typers extends Adaptations with Tags { val silentContext = context.makeImplicit(context.ambiguousErrors) val res = newTyper(silentContext).typed( new ApplyImplicitView(coercion, List(tree)) setPos tree.pos, mode, pt) - if (silentContext.hasErrors) context.issue(silentContext.firstError) else return res + silentContext.firstError match { + case Some(err) => context.issue(err) + case None => return res + } } } } diff --git a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala index 132dd251db..66dda2b530 100644 --- a/src/compiler/scala/tools/reflect/ToolBoxFactory.scala +++ b/src/compiler/scala/tools/reflect/ToolBoxFactory.scala @@ -184,7 +184,9 @@ abstract class ToolBoxFactory[U <: JavaUniverse](val u: U) { factorySelf => analyzer.inferImplicit(tree, pt, reportAmbiguous = true, isView = isView, context = context, saveAmbiguousDivergent = !silent, pos = pos) match { case failure if failure.tree.isEmpty => trace("implicit search has failed. to find out the reason, turn on -Xlog-implicits: ")(failure.tree) - if (context.hasErrors) throw ToolBoxError("reflective implicit search has failed: %s".format(context.firstError.errMsg)) + context.firstError foreach { err => + throw ToolBoxError("reflective implicit search has failed: %s".format(err.errMsg)) + } EmptyTree case success => success.tree -- cgit v1.2.3