Convention for error reporting and aborting
dvdvgt opened this issue · comments
It would be nice to have some agreed upon convention on when to use which kind of error reporting:
effekt/effekt/shared/src/main/scala/effekt/util/Messages.scala
Lines 115 to 134 in 430369f
When should something
abort
? When should something panic
? When to just report
an error?
Related to this, what is the contract of calling a run
function of phase? Should the caller check whether the previous phase reported an error by check if it returned None
and then abort
/panic
, or rather should each phase (callee) handle their own errors appropriately and in turn do not return Option[A]
but just A
?
class SomePhase extends Phase[A, B] {
def run(...)(using ctx: Context): Option[B] = {
// ...
ctx.report(...)
None
}
}
// or rather
class SomePhase1 extends Phase[A, B] {
def run(...)(using ctx: Context): B = {
// ...
ctx.abort(...)
}
}
Given that in Phase
, we have
def apply(input: In)(using C: Context): Option[Out] = try {
run(input)
} catch {
case FatalPhaseError(msg) =>
C.report(msg)
None
}
what about we change
-def run(input: In)(using C: Context): Option[Out]
+def run(input: In)(using C: Context): Out // can throw FatalPhaseError using `abort`.
?
The convention then would be to always use abort
to immediately abort execution of a phase without returning a value.
error
is typically used if we eventually abort a phase, but want to collect multiple errors. This mode of usage could also be "formalized" somewhere. In Typer for instance we are using the idiom
if (Context.messaging.hasErrors) {
None
} else {
Some(Typechecked(source, tree, mod))
}
that could be used elsewhere as well.
Namer always seems to use abort
and return Some(NameResolved(source, tree, mod))
, which doesn't give multiple resolved errors. Many of these abort
s could become error
s.