Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 5b5083d

Browse files
better crash handling
1 parent 3ef1e73 commit 5b5083d

File tree

21 files changed

+244
-49
lines changed

21 files changed

+244
-49
lines changed

‎compiler/src/dotty/tools/backend/jvm/BTypes.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -256,7 +256,7 @@ abstract class BTypes {
256256
*/
257257
final def maxValueType(other: BType): BType = {
258258

259-
def uncomparable: Nothing = thrownewAssertionError(s"Cannot compute maxValueType: $this, $other")
259+
def uncomparable: Nothing = ctx.implode(s"Cannot compute maxValueType: $this, $other")
260260

261261
if (!other.isPrimitive && !other.isNothingType) uncomparable
262262

‎compiler/src/dotty/tools/backend/sjs/JSCodeGen.scala‎

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2368,10 +2368,10 @@ class JSCodeGen()(using genCtx: Context) {
23682368
// Construct inline class definition
23692369

23702370
val jsClassCaptures = originalClassDef.jsClassCaptures.getOrElse {
2371-
thrownewAssertionError(s"no class captures for anonymous JS class at $pos")
2371+
ctx.implode(s"no class captures for anonymous JS class at $pos")
23722372
}
23732373
val js.JSConstructorDef(_, ctorParams, ctorRestParam, ctorBody) = constructor.getOrElse {
2374-
thrownewAssertionError("No ctor found")
2374+
ctx.implode("No ctor found")
23752375
}
23762376
assert(ctorParams.isEmpty && ctorRestParam.isEmpty,
23772377
s"non-empty constructor params for anonymous JS class at $pos")
@@ -2396,17 +2396,17 @@ class JSCodeGen()(using genCtx: Context) {
23962396

23972397
val memberDefinitions0 = instanceMembers.toList.map {
23982398
case fdef: js.FieldDef =>
2399-
thrownewAssertionError("unexpected FieldDef")
2399+
ctx.implode("unexpected FieldDef")
24002400

24012401
case fdef: js.JSFieldDef =>
24022402
implicit val pos = fdef.pos
24032403
js.Assign(js.JSSelect(selfRef, fdef.name), jstpe.zeroOf(fdef.ftpe))
24042404

24052405
case mdef: js.MethodDef =>
2406-
thrownewAssertionError("unexpected MethodDef")
2406+
ctx.implode("unexpected MethodDef")
24072407

24082408
case cdef: js.JSConstructorDef =>
2409-
thrownewAssertionError("unexpected JSConstructorDef")
2409+
ctx.implode("unexpected JSConstructorDef")
24102410

24112411
case mdef: js.JSMethodDef =>
24122412
implicit val pos = mdef.pos
@@ -2773,7 +2773,7 @@ class JSCodeGen()(using genCtx: Context) {
27732773
if (from == to || from == jstpe.NothingType) {
27742774
value
27752775
} else if (from == jstpe.BooleanType || to == jstpe.BooleanType) {
2776-
thrownewAssertionError(s"Invalid genConversion from $from to $to")
2776+
ctx.implode(s"Invalid genConversion from $from to $to")
27772777
} else {
27782778
def intValue = (from: @unchecked) match {
27792779
case jstpe.IntType => value
@@ -3134,7 +3134,7 @@ class JSCodeGen()(using genCtx: Context) {
31343134
case value :: Nil =>
31353135
genSelectSet(genExpr(jsName), value)
31363136
case _ =>
3137-
thrownewAssertionError(s"property methods should have 0 or 1 non-varargs arguments at $pos")
3137+
ctx.implode(s"property methods should have 0 or 1 non-varargs arguments at $pos")
31383138
}
31393139

31403140
case JSCallingConvention.BracketAccess =>
@@ -3144,7 +3144,7 @@ class JSCodeGen()(using genCtx: Context) {
31443144
case keyArg :: valueArg :: Nil =>
31453145
genSelectSet(keyArg, valueArg)
31463146
case _ =>
3147-
thrownewAssertionError(s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments at $pos")
3147+
ctx.implode(s"@JSBracketAccess methods should have 1 or 2 non-varargs arguments at $pos")
31483148
}
31493149

31503150
case JSCallingConvention.BracketCall =>
@@ -3238,7 +3238,7 @@ class JSCodeGen()(using genCtx: Context) {
32383238
s"Trying to call the super constructor of Object in a non-native JS class at $pos")
32393239
genApplyMethod(genReceiver, sym, genScalaArgs)
32403240
} else if (sym.isClassConstructor) {
3241-
thrownewAssertionError(
3241+
ctx.implode(
32423242
s"calling a JS super constructor should have happened in genPrimaryJSClassCtor at $pos")
32433243
} else if (sym.owner.isNonNativeJSClass && !sym.isJSExposed) {
32443244
// Reroute to the static method
@@ -3358,7 +3358,7 @@ class JSCodeGen()(using genCtx: Context) {
33583358

33593359
clauses = clauses.reverse
33603360
val defaultClause = optDefaultClause.getOrElse {
3361-
thrownewAssertionError("No elseClause in pattern match")
3361+
ctx.implode("No elseClause in pattern match")
33623362
}
33633363

33643364
/* Builds a `js.Match`, but simplifies it to a `js.If` if there is only

‎compiler/src/dotty/tools/backend/sjs/JSExportsGen.scala‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
176176
js.TopLevelMethodExportDef(info.moduleID, methodDef)
177177

178178
case Property =>
179-
thrownewAssertionError("found top-level exported property")
179+
ctx.implode("found top-level exported property")
180180

181181
case Field =>
182182
val sym = checkSingleField(tups)
@@ -222,7 +222,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
222222
js.JSFieldDef(flags, name, irTpe)
223223

224224
case kind =>
225-
thrownewAssertionError(s"unexpected static export kind: $kind")
225+
ctx.implode(s"unexpected static export kind: $kind")
226226
}
227227
}).toList
228228
}
@@ -743,7 +743,7 @@ final class JSExportsGen(jsCodeGen: JSCodeGen)(using Context) {
743743
val modAccessor = outer.info.allMembers.find { denot =>
744744
denot.symbol.is(Module) && denot.name.unexpandedName == name
745745
}.getOrElse {
746-
thrownewAssertionError(i"could not find module accessor for ${targetSym.fullName} at $pos")
746+
ctx.implode(i"could not find module accessor for ${targetSym.fullName} at $pos")
747747
}.symbol
748748

749749
val receiver = captures.head

‎compiler/src/dotty/tools/dotc/Driver.scala‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import dotty.tools.FatalError
44
import config.CompilerCommand
55
import core.Comments.{ContextDoc, ContextDocstrings}
66
import core.Contexts._
7+
import util.Implosion
78
import core.{MacroClassLoader, TypeError}
89
import dotty.tools.dotc.ast.Positioned
910
import dotty.tools.io.AbstractFile
@@ -35,14 +36,11 @@ class Driver {
3536
run.compile(files)
3637
finish(compiler, run)
3738
catch
38-
case ex: FatalError =>
39-
report.error(ex.getMessage.nn) // signals that we should fail compilation.
40-
case ex: TypeError =>
41-
println(s"${ex.toMessage} while compiling ${files.map(_.path).mkString(", ")}")
42-
throw ex
39+
case ex: Implosion =>
40+
// All handling related to the Implosion is done during creation, so we can swallow this
4341
case ex: Throwable =>
44-
println(s"$ex while compiling ${files.map(_.path).mkString(", ")}")
45-
throw ex
42+
ctx.lateImplode(ex) //this should never happen except in the case of `FatalError`s
43+
4644
ctx.reporter
4745

4846
protected def finish(compiler: Compiler, run: Run)(using Context): Unit =

‎compiler/src/dotty/tools/dotc/Run.scala‎

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,10 +179,15 @@ class Run(comp: Compiler, ictx: Context) extends ImplicitRunInfo with Constraint
179179
compileSources(sources)
180180
catch
181181
case NonFatal(ex) =>
182-
if units.nonEmpty then report.echo(i"exception occurred while compiling $units%, %")
183-
else report.echo(s"exception occurred while compiling ${files.map(_.name).mkString(", ")}")
184-
throw ex
185-
182+
try
183+
if units.nonEmpty then ctx.lateImplode(i"exception occurred while compiling $units%, %")
184+
else ctx.lateImplode(s"exception occurred while compiling ${files.map(_.name).mkString(", ")}")
185+
catch
186+
case _: Implosion =>
187+
188+
case _ : Implosion =>
189+
// All handling related to the Implosion is done during creation, so we can swallow this
190+
186191
/** TODO: There's a fundamental design problem here: We assemble phases using `fusePhases`
187192
* when we first build the compiler. But we modify them with -Yskip, -Ystop
188193
* on each run. That modification needs to either transform the tree structure,

‎compiler/src/dotty/tools/dotc/cc/CaptureSet.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,7 +690,7 @@ object CaptureSet:
690690
upper.isAlwaysEmpty || upper.isConst && upper.elems.size == 1 && upper.elems.contains(r1)
691691
if variance > 0 || isExact then upper
692692
else if variance < 0 then CaptureSet.empty
693-
else assert(false, i"trying to add $upper from $r via ${tm.getClass} in a non-variant setting")
693+
else ctx.implode( i"trying to add $upper from $r via ${tm.getClass} in a non-variant setting")
694694

695695
/** Apply `f` to each element in `xs`, and join result sets with `++` */
696696
def mapRefs(xs: Refs, f: CaptureRef => CaptureSet)(using Context): CaptureSet =

‎compiler/src/dotty/tools/dotc/core/ContextOps.scala‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ import ast.untpd
88

99
/** Extension methods for contexts where we want to keep the ctx.<methodName> syntax */
1010
object ContextOps:
11-
1211
extension (ctx: Context)
1312

1413
/** Enter symbol into current class, if current class is owner of current context,

‎compiler/src/dotty/tools/dotc/core/Contexts.scala‎

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import Scopes._
1313
import Uniques._
1414
import ast.Trees._
1515
import ast.untpd
16-
import util.{NoSource, SimpleIdentityMap, SourceFile, HashSet, ReusableInstance}
16+
import util.{NoSource, SimpleIdentityMap, SourceFile, HashSet, ReusableInstance, Implosion}
1717
import typer.{Implicits, ImportInfo, SearchHistory, SearchRoot, TypeAssigner, Typer, Nullables}
1818
import inlines.Inliner
1919
import Nullables._
@@ -454,6 +454,37 @@ object Contexts {
454454
def freshOver(outer: Context): FreshContext =
455455
FreshContext(base).init(outer, this).setTyperState(this.typerState)
456456

457+
/** Crash the compiler with a `Throwable` in a controlled way, reporting useful info
458+
* and asking users to submit a crash report.
459+
* This helps users by providing information they can use to work around the crash
460+
* and helps compiler maintainers by capturing important information about the crash.
461+
* With the exception of `ControlThrowable`s, this method should be used instead of
462+
* throwing exceptions whenever a context is available.
463+
*
464+
* instead of:
465+
* `throw Exception("foo")`
466+
* use:
467+
* `ctx.implode(Exception("foo"))`
468+
*/
469+
inline def implode(inline cause: Throwable): Nothing =
470+
Implosion(cause)(using thiscontext)
471+
472+
/**
473+
* Crash the compiler with a message in a controlled way
474+
*/
475+
inline def implode(inline msg: Any): Nothing =
476+
implode(AssertionError(msg))
477+
478+
/**
479+
* A fallback for when an exception has been thrown without using `implode`
480+
* This will capture context from this context which may be a parent of the context the actual exception was thrown in.
481+
*/
482+
def lateImplode(cause: Throwable): Nothing =
483+
Implosion(Exception("Context was thrown away, this crash report may not be accurate", (cause)))(using ctx)
484+
485+
def lateImplode(msg: Any): Nothing =
486+
lateImplode(AssertionError(msg))
487+
457488
final def withOwner(owner: Symbol): Context =
458489
if (owner ne this.owner) fresh.setOwner(owner) else this
459490

‎compiler/src/dotty/tools/dotc/core/Phases.scala‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import ast.{tpd, untpd}
2222
import scala.annotation.internal.sharable
2323
import scala.util.control.NonFatal
2424

25+
26+
2527
object Phases {
2628

2729
inline def phaseOf(id: PhaseId)(using Context): Phase =
@@ -322,9 +324,10 @@ object Phases {
322324
units.map { unit =>
323325
val unitCtx = ctx.fresh.setPhase(this.start).setCompilationUnit(unit).withRootImports
324326
try run(using unitCtx)
325-
catch case ex: Throwable =>
326-
println(s"$ex while running $phaseName on $unit")
327-
throw ex
327+
catch
328+
case NonFatal(ex) =>
329+
unitCtx.lateImplode(Exception(i"$ex while running $phaseName on $unit", ex))
330+
328331
unitCtx.compilationUnit
329332
}
330333

‎compiler/src/dotty/tools/dotc/core/TypeOps.scala‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -250,7 +250,7 @@ object TypeOps:
250250
}
251251

252252
def mergeRefinedOrApplied(tp1: Type, tp2: Type): Type = {
253-
def fail = thrownewAssertionError(i"Failure to join alternatives $tp1 and $tp2")
253+
def fail = ctx.implode(i"Failure to join alternatives $tp1 and $tp2")
254254
def fallback = tp2 match
255255
case AndType(tp21, tp22) =>
256256
mergeRefinedOrApplied(tp1, tp21) & mergeRefinedOrApplied(tp1, tp22)

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /