Branch: refs/heads/main
Revision: 475fa20
Author: vasslitvinov
Link: Unavailable
Log Message:
Merge pull request #19291 from vasslitvinov/iterator-error-handling
Fixes to error handling with iterators
This PR improves the handling of errors within a "halting" context,
i.e., when the throwing expression - either a throw
statement or a
call of a throwing function - occurs outside of a "try" context, i.e.,
a try
or try!
block or else in a function that throws
. It makes
the behavior more consistent with the "immediate" option in
#19388. For example, an error thrown in a "halting" context in an
iterator is now reported as "uncaught" in the iterator, not in the
loop over the iterator. An error thrown in a "halting" context in a
forall statement is reported as "uncaught" where it is thrown, not at
the forall statement. The PR fixes some bugs and resolves #19378.
IMPLEMENTATION DETAILS
A key change is to tune up decision making in
ErrorHandlingVisitor::enterCallExpr() where it chooses between
propagating the thrown error to the function's error_out, to an
enclosing try block, or halding immediately.
-
Calls to throwing functions in a halting context are lowered during
thelowerErrorHandling
pass by adding calls to
chpl_uncaught_error
, notchpl_propagate_error
, as it used to be. -
Calls to
_waitEndCount()
are a special case, as this function
propagates errors from the associated task functions. They used to
be lowered in a halting context always by adding calls
chpl_propagate_error
, like all other throwing functions. This PR
changes that to call:- chpl_uncaught_error() when the corresponding source code
appears lexically within a procedure, or - chpl_propagate_error() when it is within an iterator.
Background:
chpl_uncaught_error
always causes the program to
halt.chpl_propagate_error
also always causes the program to halt,
however it may be replaced during lowerIterators / iterator inlining
to propagating the exception to a now-enclosing try block or
throwing function. - chpl_uncaught_error() when the corresponding source code
-
Task functions that correspond to task constructs that, in the
source code, occur in a halting context, are marked with
FLAG_OUTSIDE_TRY
. This is easiest to do before the task constructs
get converted to task functions, so I put it in createTaskFunctions. -
The new
FLAG_FORALL_BREAK_LABEL
helps find the forall statement's
error handler just like FLAG_ERROR_LABEL helps find other handlers
in handleChplPropagateErrorCall(), i.e., when lowering calls to
chpl_propagate_error() / gChplPropagateError. These are inserted in
lowerErrorHandling() to pass error handling information to
lowerIterators(), where they are lowered away. The behavior of
handleChplPropagateErrorCall() remains unchanged unless we are
lowering a forall statement. This reduces the chance of introducing
unwanted behavior. -
The changes to the tests in
test/parallel/forall/task-private-vars/throwing-init-exprs/
are aimed to preserve the previously-observed behavior so that their
(numerous) .good files do not need to change. -
I added comments based on what I learned.
FUTURE WORK
(f1) Clarify the error handling section of the language spec
Error Handling — Chapel Documentation 1.26
that the relationship between a thrown error and its try/try! block is
lexical, not dynamic. "Lexical" is mentioned in one case and omitted
in another case. This PR and this writeup assume this is the desired
semantics.
(f2) Have a try
block within a parallel iterator handle only the
exceptions thrown within its lexical scope, not those thrown in the
loop body. For example:
proc myIter(param tag) where tag == iterKind.standalone {
try {
coforall ... {
functionThatThrows();
yield ...;
}
}
}
When this iterator is invoked in a forall statement under the current
implementation, the above try-block will handle both the exceptions
thrown by functionThatThrows() and those thrown in the loop body of
the forall.
(f3) Report correct line numbers when an error is handled or not
handled in an iterator that is inlined. For example:
proc myIter(param tag) where tag == iterKind.standalone {
...
functionThatThrows(); // assume no surrounding 'try'
...
}
When this iterator is invoked in a forall statement and the program
reports during execution that an exception is thrown at line NNN and
uncaught at line MMM, the line MMM points into the forall statement,
whereas it should point into myIter()
.
This problem is due to the inlining-based implementation of loops,
whereby the line numbers associated with the iterator code are
replaced with the loop's line number upon inlining the iterator into
the loop. For the same reason if we were stepping through the iterator
code in the debugger, it would be pointing at the loop line number,
not the iterator line numbers. Iterator line numbers can be observed
in both of these scenarios if compiling with --preserve-inlined-line-numbers.
(f4) Make it a compile-time error to have something thrown in a
defer
statement -- until such time that we figure out how to make
the program behavior reasonable (and implement it). Currently it may
be a runtime error or might even propagate the error upstream.
r: @dlongnecke-cray
Modified Files:
A test/errhandling/parallel/forall-iterator-catches-1.bad
A test/errhandling/parallel/forall-iterator-catches-1.chpl
A test/errhandling/parallel/forall-iterator-catches-1.future
A test/errhandling/parallel/forall-iterator-catches-1.good
A test/errhandling/parallel/forall-iterator-catches-2.chpl
A test/errhandling/parallel/forall-iterator-catches-2.good
A test/errhandling/parallel/forall-iterator-throws-prototype.chpl
A test/errhandling/parallel/forall-iterator-throws-prototype.good
A test/errhandling/parallel/forall-throws-prototype.chpl
A test/errhandling/parallel/forall-throws-prototype.good
R test/errhandling/parallel/forall-in-function-throws.bad
R test/errhandling/parallel/forall-in-function-throws.future
M compiler/dyno/include/chpl/uast/PragmaList.h
M compiler/include/passes.h
M compiler/passes/createTaskFunctions.cpp
M compiler/passes/errorHandling.cpp
M compiler/resolution/lowerForalls.cpp
M compiler/resolution/lowerIterators.cpp
M test/parallel/forall/task-private-vars/throwing-init-exprs/int-no-tlvy.chpl
M test/parallel/forall/task-private-vars/throwing-init-exprs/int-tpvs.chpl
M test/parallel/forall/task-private-vars/throwing-init-exprs/nonpod-tpvs.good
M test/parallel/forall/task-private-vars/throwing-init-exprs/pod-no-tlvy.chpl
M test/parallel/forall/task-private-vars/throwing-init-exprs/pod-tpvs.chpl
Compare: https://github.com/chapel-lang/chapel/compare/f4fd786c0e49...475fa208f4bd