The library developer should be able to handle specific types of Panics while passing through others (#3344)

Implements https://www.pivotaltracker.com/story/show/181569176

Also ensures that Dataflow Errors have proper stack traces (earlier they did not point at the right location).
This commit is contained in:
Radosław Waśko 2022-03-18 17:57:06 +01:00 committed by GitHub
parent 466f1e29c3
commit cc7333812d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
51 changed files with 676 additions and 216 deletions

View File

@ -71,6 +71,8 @@
also compute mode, percentile, minimum, maximum.][3318]
- [Implemented `Text.location_of` and `Text.location_of_all` methods.][3324]
- [Replaced `Table.group_by` with `Table.aggregate`][3339]
- [Implemented `Panic.catch` and helper functions for handling errors. Added a
type parameter to `Panic.recover` to recover specific types of errors.][3344]
[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
@ -113,6 +115,7 @@
[3324]: https://github.com/enso-org/enso/pull/3324
[3327]: https://github.com/enso-org/enso/pull/3327
[3339]: https://github.com/enso-org/enso/pull/3339
[3344]: https://github.com/enso-org/enso/pull/3344
#### Enso Compiler

View File

@ -38,10 +38,8 @@ from_pairs contents =
example_parse = Json.parse '{ "a": 1 }'
parse : Text -> Json ! Parse_Error
parse json_text =
r = Panic.recover (Internal.parse_helper json_text)
r.catch e-> case e of
Polyglot_Error err -> Error.throw (Parse_Error err.getMessage)
p -> Panic.throw p
Panic.catch Polyglot_Error (Internal.parse_helper json_text) caught_panic->
Error.throw (Parse_Error caught_panic.payload.cause.getMessage)
## Represents a JSON structure.
type Json
@ -104,7 +102,7 @@ type Json
json.into format
into : Any -> Any ! Marshalling_Error
into type_descriptor =
Panic.recover (Internal.into_helper type_descriptor this)
Panic.recover Any (Internal.into_helper type_descriptor this)
## Returns this Json object.

View File

@ -4,6 +4,7 @@ polyglot java import java.lang.Long
polyglot java import java.lang.Double
polyglot java import java.lang.Math
polyglot java import java.lang.String
polyglot java import java.lang.NumberFormatException
## ALIAS Inverse Sine
@ -268,7 +269,7 @@ Number.to_json = Json.Number this
Integer.parse "20220216"
Integer.parse : Text -> Text -> Integer ! Parse_Error
Integer.parse text (radix=10) =
Panic.recover (Long.parseLong text radix) . catch _->
Panic.catch NumberFormatException (Long.parseLong text radix) _->
Error.throw (Parse_Error text)
## ALIAS From Text
@ -285,7 +286,7 @@ Integer.parse text (radix=10) =
Decimal.parse "7.6"
Decimal.parse : Text -> Decimal ! Parse_Error
Decimal.parse text =
Panic.recover (Double.parseDouble text) . catch _->
Panic.catch NumberFormatException (Double.parseDouble text) _->
Error.throw (Parse_Error text)
## UNSTABLE

View File

@ -261,7 +261,7 @@ type Matcher
Regex_Matcher
## PRIVATE
match_criteria_implementation matcher objects criteria reorder=False name_mapper=(x->x) on_problems=Report_Warning = Panic.recover <|
match_criteria_implementation matcher objects criteria reorder=False name_mapper=(x->x) on_problems=Report_Warning = Panic.recover Any <|
[matcher, objects, criteria, reorder, name_mapper, on_problems] . each Panic.rethrow
# match_matrix . at i . at j specifies whether objects.at i matches criteria.at j

View File

@ -120,7 +120,7 @@ type Engine
options_bitmask = here.from_enso_options all_options
unicode_regex = UnicodeRegex.new
maybe_java_pattern = Panic.recover <|
maybe_java_pattern = Panic.recover Any <|
Java_Pattern.compile (unicode_regex.transform expression) options_bitmask
internal_pattern = maybe_java_pattern.map_error case _ of
@ -617,7 +617,7 @@ type Match
match.group "letters"
group : Integer | Text -> Text | Nothing ! Regex.No_Such_Group_Error
group id =
Panic.recover (this.internal_match.group id) . map_error (here.handle_error _ id)
Panic.recover Any (this.internal_match.group id) . map_error (here.handle_error _ id)
## Gets a vector containing the results of _all_ of the capturing groups in
the pattern, replacing the value of groups that did not participate in
@ -703,7 +703,7 @@ type Match
match.start 0
start : Integer | Text -> Integer | Nothing ! Regex.No_Such_Group_Error
start id =
result = Panic.recover (this.internal_match.start id)
result = Panic.recover Any (this.internal_match.start id)
no_errors = result.map_error (here.handle_error _ id)
if no_errors == -1 then Nothing else no_errors
@ -731,7 +731,7 @@ type Match
match.end 0
end : Integer | Text -> Integer | Nothing ! Regex.No_Such_Group_Error
end id =
result = Panic.recover (this.internal_match.end id)
result = Panic.recover Any (this.internal_match.end id)
no_errors = result.map_error (here.handle_error _ id)
if no_errors == -1 then Nothing else no_errors
@ -854,7 +854,7 @@ type Option
- opts: The enso-side options to configure the regex.
from_enso_options : Vector.Vector (Option | Global_Option.Option) -> Integer
from_enso_options opts =
java_flags = Panic.recover <| opts.flat_map case _ of
java_flags = Panic.recover Any <| opts.flat_map case _ of
Literal_Pattern -> [Java_Pattern.LITERAL]
Unix_Lines -> [Java_Pattern.UNIX_LINES]
Global_Option.Case_Insensitive -> [Java_Pattern.CASE_INSENSITIVE]

View File

@ -56,7 +56,7 @@ now = Time ZonedDateTime.now
example_new = Time.new 1986 8 5
new : Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Integer -> Zone -> Time ! Time_Error
new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) (zone = Zone.system) =
Panic.recover (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch e-> case e of
Panic.recover Any (Time (ZonedDateTime.of year month day hour minute second nanosecond zone.internal_zone_id)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time_Error err.getMessage)
x -> x
@ -141,7 +141,7 @@ new year (month = 1) (day = 1) (hour = 0) (minute = 0) (second = 0) (nanosecond
Date.parse "06 of May 2020 at 04:30AM" "dd 'of' MMMM yyyy 'at' hh:mma"
parse : Text -> Text | Nothing -> Locale -> Time ! Time_Error
parse text pattern=Nothing locale=Locale.default =
result = Panic.recover <| case pattern of
result = Panic.recover Any <| case pattern of
Nothing -> Time_Utils.parse_time text
Text -> Time_Utils.parse_time_format text pattern locale.java_locale
_ -> Panic.throw (Time_Error "An invalid pattern was provided.")

View File

@ -60,7 +60,7 @@ today = here.now
new : Integer -> Integer -> Integer -> Date ! Time.Time_Error
new year (month = 1) (day = 1) =
Panic.recover (Date (LocalDate.of year month day)) . catch e-> case e of
Panic.recover Any (Date (LocalDate.of year month day)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
@ -129,7 +129,7 @@ new year (month = 1) (day = 1) =
Time.Time_Error _ -> Date.new 2000 1 1
parse : Text -> (Text | Nothing) -> Date ! Time.Time_Error
parse text pattern=Nothing =
result = Panic.recover <| case pattern of
result = Panic.recover Any <| case pattern of
Nothing -> LocalDate.parse text
Text -> LocalDate.parse text (DateTimeFormatter.ofPattern pattern)
_ -> Panic.throw (Time.Time_Error "An invalid pattern was provided.")

View File

@ -48,7 +48,7 @@ now = Time_Of_Day LocalTime.now
example_epoch = Time_Of_Day.new hour=9 minute=30
new : Integer -> Integer -> Integer -> Integer -> Time_Of_Day ! Time.Time_Error
new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
Panic.recover (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch e-> case e of
Panic.recover Any (Time_Of_Day (LocalTime.of hour minute second nanosecond)) . catch e-> case e of
Polyglot_Error err -> Error.throw (Time.Time_Error err.getMessage)
x -> x
@ -115,7 +115,7 @@ new (hour = 0) (minute = 0) (second = 0) (nanosecond = 0) =
example_parse = Time_Of_Day.parse "4:30AM" "h:mma"
parse : Text -> Text | Nothing -> Locale -> Time_Of_Day ! Time.Time_Error
parse text pattern=Nothing locale=Locale.default =
result = Panic.recover <| case pattern of
result = Panic.recover Any <| case pattern of
Nothing -> LocalTime.parse text
Text ->
formatter = DateTimeFormatter.ofPattern pattern

View File

@ -233,7 +233,7 @@ type Vector
[0, 1, 2].sum
sum : Any ! (Empty_Error | No_Such_Method_Error)
sum =
result = Panic.recover <| this.reduce (+)
result = Panic.recover Any <| this.reduce (+)
result.map_error x->case x of
No_Such_Method_Error _ _ -> x
Empty_Error -> x
@ -938,7 +938,7 @@ type Vector
More details on the HashCode / HashMap ticket https://www.pivotaltracker.com/story/show/181027272.
recovered = Panic.recover
recovered = Panic.recover Any
builder = here.new_builder
this.fold Map.empty existing->
item->

View File

@ -1,6 +1,7 @@
from Standard.Base import all
import Standard.Base.Runtime.Extensions as Runtime_Extensions
import Standard.Builtins
from Standard.Base.Runtime.Extensions as Runtime_Extensions import Stack_Trace_Element
## ADVANCED
UNSTABLE
@ -125,14 +126,31 @@ Error.map_error f = this.catch (x -> Error.throw (f x))
## ADVANCED
UNSTABLE
Returns the attached stack trace of the error. The ordering of the resulting
vector is such that the top stack frame is the first element.
Error.get_stack_trace : Vector.Vector Stack_Trace_Element
Error.get_stack_trace =
prim_stack = this.primitive_get_stack_trace
Returns the attached stack trace of the given throwable. Can be used to get
an Enso friendly stack trace from native Java exceptions.
The ordering of the resulting vector is such that the top stack frame is the
first element.
Panic.get_attached_stack_trace : Caught_Panic | Throwable -> Vector.Vector Stack_Trace_Element
Panic.get_attached_stack_trace error =
throwable = case error of
Caught_Panic _ internal_original_exception -> internal_original_exception
throwable -> throwable
prim_stack = Panic.primitive_get_attached_stack_trace throwable
stack_with_prims = Vector.Vector prim_stack
stack_with_prims.map Runtime_Extensions.wrap_primitive_stack_trace_element
## ADVANCED
UNSTABLE
Returns the attached stack trace of the error.
The ordering of the resulting vector is such that the top stack frame is the
first element.
Error.stack_trace : Vector.Vector Stack_Trace_Element
Error.stack_trace =
Panic.get_attached_stack_trace this
## Checks if `this` is an error.
> Example
@ -156,3 +174,95 @@ Error.is_error = True
example_rethrow = Panic.rethrow Examples.throw_error
Panic.rethrow : (Any ! Any) -> Any
Panic.rethrow value = value.catch Panic.throw
## Returns the stack trace of the caught panic.
Caught_Panic.stack_trace : Vector.Vector Stack_Trace_Element
Caught_Panic.stack_trace =
Panic.get_attached_stack_trace this
## Executes the provided action and if a panic matching the provided type was
thrown, calls the provided callback.
If action executes successfully, the result of `Panic.catch` is the result of
that action. Otherwise, if a matching panic is thrown from within the action,
the result is obtained by calling the provided handler callback. Any
non-matching panics are forwarded without changes.
Arguments:
- panic_type: The expected panic type. It can either be an Enso type or a
Java class. If the Java class is provided, `Polyglot_Error` containing a
Java exception of this class will be matched.
- action: The code to execute that potentially panics.
- handler: The callback to handle the panics. The callback will be provided
with a `Caught_Panic` instance encapsulating the `payload` of the caught
panic and its stacktrace.
> Example
Handling a specific type of panic.
Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "Oh no!" Nothing)) error->
"Caught an `Illegal_Argument_Error`: "+error.payload.message
> Example
Handling any panic.
Panic.catch Any (Panic.throw (Illegal_Argument_Error "Oh no!" Nothing)) error->
"Caught some panic!"
> Example
Convert a string to an integer, catching the Java `NumberFormatException`
and converting it to a more Enso-friendly dataflow error.
polyglot java import java.lang.Long
polyglot java import java.lang.NumberFormatException
parse str =
Panic.catch NumberFormatException (Long.parseLong str) caught_panic->
Error.throw (Illegal_Argument_Error "The provided string is not a valid number: "+caught_panic.payload.cause.getMessage)
Panic.catch : Any -> Any -> (Caught_Panic -> Any) -> Any
Panic.catch panic_type ~action handler =
Panic.catch_primitive action caught_panic->
case Builtins.Meta.get_polyglot_language panic_type == "java" of
False -> case caught_panic.payload.is_a panic_type of
True -> handler caught_panic
False -> Panic.throw caught_panic
True -> case caught_panic.payload of
Polyglot_Error java_exception ->
case Java.is_instance java_exception panic_type of
True -> handler caught_panic
False -> Panic.throw caught_panic
_ -> Panic.throw caught_panic
## Executes the provided action and converts a possible panic matching any of
the provided types into a dataflow Error.
If action executes successfully, the result of `Panic.recover` is the result
of that action. Otherwise, if it panicked with a type matching one of the
expected error types, that panic is returned as a dataflow error. Unexpected
panics are passed through as-is. it is the panic that was thrown after
conversion to a dataflow error.
Arguments:
- expected_types: The types of expected panics which should be recovered.
This can either be a Vector of types or a single type.
- action: The code to execute that potentially panics.
> Example
Converting an expected panic to a dataflow error.
Panic.recover Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "Oh!" Nothing))
> Example
Converting one of many expected panic types to a dataflow error.
Panic.recover [Illegal_Argument_Error, Illegal_State_Error] (Panic.throw (Illegal_Argument_Error "Oh!" Nothing))
Panic.recover : (Vector.Vector Any | Any) -> Any -> Any
Panic.recover expected_types ~action =
types_to_check = case expected_types of
Vector.Vector _ -> expected_types
_ -> [expected_types]
Panic.catch Any action caught_panic->
is_matched = types_to_check.exists typ->
caught_panic.payload.is_a typ
case is_matched of
True -> caught_panic.convert_to_dataflow_error
False -> Panic.throw caught_panic

View File

@ -554,7 +554,7 @@ type Http
http.request req
request : Request -> Response ! Request_Error
request req =
response = Panic.recover <|
response = Panic.recover Any <|
body_publishers = HttpRequest.BodyPublishers
builder = HttpRequest.newBuilder
# set uri

View File

@ -26,7 +26,7 @@ import Standard.Base.System.File
example_new = Request.new Method.Post (Uri.parse "http://example.com")
new : Method -> (Text | Uri) -> Vector.Vector -> Request_Body -> Request
new method addr (headers = []) (body = Request_Body.Empty) =
Panic.recover (Request method (Internal.panic_on_error (addr.to_uri)) headers body) . catch Internal.recover_panic
Panic.recover Any (Request method (Internal.panic_on_error (addr.to_uri)) headers body) . catch Internal.recover_panic
## Create an Options request.

View File

@ -22,7 +22,7 @@ polyglot java import java.util.Optional
example_parse = Uri.parse "http://example.com"
parse : Text -> Uri ! Syntax_Error
parse text =
Panic.recover (Uri (Java_URI.create text)) . catch e-> case e of
Panic.recover Any (Uri (Java_URI.create text)) . catch e-> case e of
Polyglot_Error ex -> Error.throw (Syntax_Error ex.getMessage)
other -> Panic.throw other

View File

@ -817,7 +817,7 @@ rethrow_java file exception =
Utility method for running an action with Java exceptions mapping.
handle_java_exceptions file ~action =
err = Panic.recover action
err = Panic.recover Any action
r = err.catch (here.rethrow_java file)
r

View File

@ -96,7 +96,7 @@ type Connection
execute_update : Text | Sql.Statement -> Integer
execute_update query = here.wrap_sql_errors <|
Resource.bracket (this.prepare_statement query) .close stmt->
result = Panic.recover stmt.executeLargeUpdate
result = Panic.recover Any stmt.executeLargeUpdate
result.catch err-> case err of
Polyglot_Error exc ->
case Java.is_instance exc UnsupportedOperationException of
@ -115,7 +115,7 @@ type Connection
prepare_statement query =
go template holes=[] = Managed_Resource.with this.connection_resource java_connection->
stmt = java_connection.prepareStatement template
setup_error = Panic.recover <|
setup_error = Panic.recover Any <|
holes.map_with_index ix-> obj->
position = ix + 1
case obj.first of
@ -345,7 +345,7 @@ type Sql_Timeout_Error
- action: The computation to execute. This computation may throw SQL errors.
wrap_sql_errors : Any -> Any ! Error
wrap_sql_errors ~action =
result = Panic.recover action
result = Panic.recover Any action
result.catch err-> case err of
Polyglot_Error exc ->
transformed = if Java.is_instance exc SQLTimeoutException then Sql_Timeout_Error exc else

View File

@ -409,7 +409,7 @@ type Table
Arguments:
- index: The column to use as the index of the table.
set_index : Text | Column | Vector Text -> Table
set_index index = Panic.recover <|
set_index index = Panic.recover Any <|
new_index = (Helpers.unify_vector_singleton index).map (this.at >> .as_internal)
new_ctx = this.context.set_index new_index
new_cols = this.internal_columns.filter col->
@ -490,7 +490,7 @@ type Table
quality_ratio = table.at 'Rating' / table.at 'Price'
table.sort by=[quality_ratio, 'Rating']
sort : Text | Column | Order_Rule | Vector.Vector (Text | Column | Order_Rule) -> Sort_Order -> Boolean -> Table
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover <|
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover Any <|
order_to_ir = case _ of
Sort_Order.Ascending -> IR.Ascending
Sort_Order.Descending -> IR.Descending
@ -547,7 +547,7 @@ type Table
join : Table | Column -> Nothing | Text | Column | Vector (Text | Column) -> Boolean -> Text -> Text -> Table
join other on=Nothing drop_unmatched=False left_suffix='_left' right_suffix='_right' = case other of
Column _ _ _ _ _ -> this.join other.to_table on drop_unmatched left_suffix right_suffix
Table _ _ _ _ -> Panic.recover <|
Table _ _ _ _ -> Panic.recover Any <|
Panic.rethrow (Helpers.ensure_name_is_sane left_suffix && Helpers.ensure_name_is_sane right_suffix)
if left_suffix == right_suffix then
Panic.throw <| Illegal_State_Error "left_suffix must be different from right_suffix"
@ -627,7 +627,7 @@ type Table
- by: The column names on which to group. If this is not set, the index
will be used for grouping instead.
group : Vector Text | Text | Nothing -> Aggregate_Table
group by=Nothing = Panic.recover <|
group by=Nothing = Panic.recover Any <|
cols = case by of
Nothing ->
if this.context.meta_index.is_empty then Panic.throw <| Illegal_State_Error "Trying to group by an empty index." else

View File

@ -125,7 +125,7 @@ type No_Methods
## Returns a no_such_method_error as a value.
no_such_method : No_Such_Method_Error
no_such_method = Panic.recover No_Methods.frobnicate . catch
no_such_method = Panic.recover Any No_Methods.frobnicate . catch
## A simple error type for example purposes.
type My_Error message

View File

@ -38,7 +38,7 @@ read location flags=[] =
if flags.is_empty then Java_Codecs.READ_FLAG_EMPTY else
flags.map .to_integer . reduce (_.bit_or _)
_ -> flags.to_integer
Panic.recover (Image.Image (Java_Codecs.read path read_flags)) . catch e->
Panic.recover Any (Image.Image (Java_Codecs.read path read_flags)) . catch e->
case e of
Polyglot_Error _ -> Error.throw (File.Io_Error ('Failed to read the file at ' + path + '.'))
err -> Panic.throw err
@ -71,7 +71,7 @@ Image.Image.write location flags=[] =
Vector.Vector _ -> flags
_ -> [flags]
int_flags = Internal.mat_of_int (write_flags.flat_map x-> [x.to_integer, x.value])
Panic.recover (Java_Codecs.write path this.opencv_mat int_flags) . catch e->
Panic.recover Any (Java_Codecs.write path this.opencv_mat int_flags) . catch e->
case e of
Polyglot_Error _ -> Panic.throw (File.Io_Error ('Failed to write to the file at ' + path + '.'))
err -> Panic.throw err

View File

@ -153,7 +153,7 @@ type Image
image = Examples.image
image + (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
+ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
+ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.add _ _ _)) . catch Internal.core_op_handler
+ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.add _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -204,7 +204,7 @@ type Image
image = Examples.image
image - (Matrix.zeros rows=image.rows columns=image.columns channels=image.channels)
- : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
- value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.subtract _ _ _)) . catch Internal.core_op_handler
- value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.subtract _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -255,7 +255,7 @@ type Image
image = Examples.image
image * (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
* : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
* value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.multiply _ _ _)) . catch Internal.core_op_handler
* value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.multiply _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -306,7 +306,7 @@ type Image
image = Examples.image
image / (Matrix.ones rows=image.rows columns=image.columns channels=image.channels)
/ : (Number | Vector | Matrix) -> Image ! Matrix.Dimensions_Not_Equal
/ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Image.divide _ _ _)) . catch Internal.core_op_handler
/ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Image.divide _ _ _)) . catch Internal.core_op_handler
## UNSTABLE

View File

@ -210,7 +210,7 @@ type Matrix
example_plus = Examples.matrix + Examples.matrix
+ : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
+ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.add _ _ _)) . catch Internal.core_op_handler
+ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.add _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -249,7 +249,7 @@ type Matrix
example_minus = Examples.matrix - Examples.matrix
- : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
- value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.subtract _ _ _)) . catch Internal.core_op_handler
- value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.subtract _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -293,7 +293,7 @@ type Matrix
Multiply two matrices.
m * m
* : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
* value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.multiply _ _ _)) . catch Internal.core_op_handler
* value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.multiply _ _ _)) . catch Internal.core_op_handler
## UNSTABLE
@ -331,7 +331,7 @@ type Matrix
example_div = Examples.matrix / Examples.matrix
/ : (Number | Vector | Matrix) -> Matrix ! Dimensions_Not_Equal
/ value = Panic.recover (Internal.core_op this.opencv_mat value (Java_Matrix.divide _ _ _)) . catch Internal.core_op_handler
/ value = Panic.recover Any (Internal.core_op this.opencv_mat value (Java_Matrix.divide _ _ _)) . catch Internal.core_op_handler
## UNSTABLE

View File

@ -875,7 +875,7 @@ type Table
price_rule = Table.Order_Rule "price" comparator=comparator
table.sort by=price_rule
sort : Text | Column.Column | Order_Rule | Vector.Vector (Text | Column.Column | Order_Rule) -> Sort_Order -> Boolean -> Table
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover <|
sort by order=Sort_Order.Ascending missing_last=True = Panic.recover Any <|
rules = this.build_java_order_rules by order missing_last
fallback_cmp = here.comparator_to_java .compare_to
mask = OrderBuilder.buildOrderMask rules.to_array fallback_cmp

View File

@ -71,9 +71,9 @@ from_csv csv has_header=True prefix='C' =
case csv of
Text ->
input_stream = ByteArrayInputStream.new csv.utf_8.to_array
Panic.recover Table.Table (parser_inst.parse input_stream) . catch handle_error
Panic.recover Any Table.Table (parser_inst.parse input_stream) . catch handle_error
File.File _ ->
maybe_err = Panic.recover <| csv.with_input_stream [File.Option.Read] stream->
maybe_err = Panic.recover Any <| csv.with_input_stream [File.Option.Read] stream->
stream.with_java_stream java_stream->
Table.Table (parser_inst.parse java_stream)
maybe_err.catch handle_error

View File

@ -203,12 +203,14 @@ Error.should_fail_with matcher frames_to_skip=0 =
Test.expect_panic_with Examples.throw_panic Examples.My_Error
expect_panic_with : Any -> Any -> Assertion
expect_panic_with ~action matcher =
res = Panic.recover action
res = Panic.recover Any action
case res of
_ -> here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded.")
_ ->
loc = Meta.get_source_location 2
here.fail ("Expected a " + matcher.to_text + " to be thrown, but the action succeeded (at "+loc+").")
err = res.catch x->x
if err.is_a matcher then Nothing else
here.fail ("Unexpected error " + err.to_text + " thrown.")
here.fail ("Expected a " + matcher.to_text + ", but " + err.to_text + " was thrown instead.")
## Asserts that `this` value is equal to the expected value.
@ -606,7 +608,7 @@ type Assertion
- behavior: The behavior to execute.
run_spec : Any -> Assertion
run_spec ~behavior =
recovery = Panic.recover <|
recovery = Panic.recover Any <|
result = behavior
result.catch err-> Panic.throw (Finished_With_Error err result.get_stack_trace_text)
Nothing

View File

@ -89,7 +89,7 @@ Error.catch_ ~val = this.catch (_-> val)
## PRIVATE
recover_errors : Any -> Any
recover_errors ~body =
result = Panic.recover body
result = Panic.recover Any body
result.catch err->
Json.from_pairs [["error", err.to_display_text]] . to_text

View File

@ -0,0 +1,83 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.frame.VirtualFrame;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.dsl.Suspend;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.InvokeCallableNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.argument.CallArgumentInfo;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
@BuiltinMethod(
type = "Panic",
name = "catch_primitive",
description = "Executes an action if a panic was thrown, calls the provided callback.")
public abstract class CatchPanicNode extends Node {
private @Child InvokeCallableNode invokeCallableNode;
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
CatchPanicNode() {
this.invokeCallableNode =
InvokeCallableNode.build(
new CallArgumentInfo[] {new CallArgumentInfo()},
InvokeCallableNode.DefaultsExecutionMode.EXECUTE,
InvokeCallableNode.ArgumentsExecutionMode.PRE_EXECUTED);
this.invokeCallableNode.setTailStatus(BaseNode.TailStatus.TAIL_DIRECT);
}
static CatchPanicNode build() {
return CatchPanicNodeGen.create();
}
abstract Stateful execute(
VirtualFrame frame,
@MonadicState Object state,
Object _this,
@Suspend Object action,
Object handler);
@Specialization
Stateful doExecute(
VirtualFrame frame,
@MonadicState Object state,
Object _this,
Object action,
Object handler,
@Cached BranchProfile panicBranchProfile,
@Cached BranchProfile otherExceptionBranchProfile) {
try {
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.TAIL_DIRECT);
} catch (PanicException e) {
panicBranchProfile.enter();
Object payload = e.getPayload();
return executeCallback(frame, state, handler, payload, e);
} catch (AbstractTruffleException e) {
otherExceptionBranchProfile.enter();
Builtins builtins = Context.get(this).getBuiltins();
Object payload = builtins.error().makePolyglotError(e);
return executeCallback(frame, state, handler, payload, e);
}
}
private Stateful executeCallback(
VirtualFrame frame,
Object state,
Object handler,
Object payload,
AbstractTruffleException originalException) {
Builtins builtins = Context.get(this).getBuiltins();
Atom caughtPanic = builtins.caughtPanic().newInstance(payload, originalException);
return invokeCallableNode.execute(handler, frame, state, new Object[] {caughtPanic});
}
}

View File

@ -0,0 +1,47 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
@BuiltinMethod(
type = "Caught_Panic",
name = "convert_to_dataflow_error",
description = "Converts a Caught_Panic into a Dataflow Error")
public abstract class CaughtPanicConvertToDataflowErrorNode extends Node {
static CaughtPanicConvertToDataflowErrorNode build() {
return CaughtPanicConvertToDataflowErrorNodeGen.create();
}
abstract Stateful execute(@MonadicState Object state, Atom _this);
@Specialization
Stateful doExecute(
@MonadicState Object state,
Atom _this,
@CachedLibrary(limit = "5") InteropLibrary interopLibrary) {
Builtins builtins = Context.get(this).getBuiltins();
Object payload = _this.getFields()[0];
Object originalException = _this.getFields()[1];
if (interopLibrary.isException(originalException)) {
return new Stateful(
state, DataflowError.withTrace(payload, (AbstractTruffleException) originalException));
} else {
throw new PanicException(
builtins
.error()
.makeTypeError("Exception", originalException, "internal_original_exception"),
this);
}
}
}

View File

@ -0,0 +1,25 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.AcceptsError;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.node.expression.builtin.runtime.GetStackTraceNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.PanicException;
@BuiltinMethod(
type = "Panic",
name = "primitive_get_attached_stack_trace",
description = "Gets the stack trace attached to the throwable.")
public class GetAttachedStackTraceNode extends Node {
Array execute(Object _this, @AcceptsError Object error) {
if (error instanceof Throwable) {
return GetStackTraceNode.stackTraceToArray((Throwable) error);
} else {
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(builtins.error().makeTypeError("Throwable", error, "throwable"), this);
}
}
}

View File

@ -1,23 +0,0 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.nodes.Node;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.data.Array;
import org.enso.interpreter.runtime.error.DataflowError;
@BuiltinMethod(
type = "Error",
name = "primitive_get_stack_trace",
description = "Gets the stack trace of the error's origin.")
public class GetStackTraceNode extends Node {
Array execute(DataflowError _this) {
var elements = TruffleStackTrace.getStackTrace(_this);
var ret = new Array(elements.size());
for (int i = 0; i < elements.size(); i++) {
var element = elements.get(i);
ret.getItems()[i] = element.getGuestObject();
}
return ret;
}
}

View File

@ -1,54 +0,0 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.dsl.MonadicState;
import org.enso.interpreter.dsl.Suspend;
import org.enso.interpreter.node.BaseNode;
import org.enso.interpreter.node.callable.thunk.ThunkExecutorNode;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.error.DataflowError;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
@BuiltinMethod(
type = "Panic",
name = "recover",
description = "Executes an action and converts any Panic thrown by it into an Error")
public abstract class RecoverPanicNode extends Node {
private @Child ThunkExecutorNode thunkExecutorNode = ThunkExecutorNode.build();
private final BranchProfile unknownExceptionProfile = BranchProfile.create();
static RecoverPanicNode build() {
return RecoverPanicNodeGen.create();
}
abstract Stateful execute(@MonadicState Object state, Object _this, @Suspend Object action);
@Specialization
Stateful doExecute(
@MonadicState Object state,
Object _this,
Object action,
@CachedLibrary(limit = "5") InteropLibrary exceptions) {
try {
return thunkExecutorNode.executeThunk(action, state, BaseNode.TailStatus.NOT_TAIL);
} catch (PanicException e) {
return new Stateful(state, DataflowError.withTrace(e.getPayload(), e));
} catch (Throwable e) {
if (exceptions.isException(e)) {
return new Stateful(
state,
DataflowError.withTrace(
Context.get(this).getBuiltins().error().makePolyglotError(e), (AbstractTruffleException) e));
}
unknownExceptionProfile.enter();
throw e;
}
}
}

View File

@ -1,7 +1,17 @@
package org.enso.interpreter.node.expression.builtin.error;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Fallback;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.interop.InteropLibrary;
import com.oracle.truffle.api.interop.UnsupportedMessageException;
import com.oracle.truffle.api.library.CachedLibrary;
import com.oracle.truffle.api.nodes.Node;
import com.oracle.truffle.api.profiles.BranchProfile;
import org.enso.interpreter.dsl.BuiltinMethod;
import org.enso.interpreter.runtime.Context;
import org.enso.interpreter.runtime.builtin.Builtins;
import org.enso.interpreter.runtime.callable.atom.Atom;
import org.enso.interpreter.runtime.error.PanicException;
import org.enso.interpreter.runtime.state.Stateful;
@ -9,8 +19,63 @@ import org.enso.interpreter.runtime.state.Stateful;
type = "Panic",
name = "throw",
description = "Throws a new Panic with given payload.")
public class ThrowPanicNode extends Node {
Stateful execute(Object _this, Object payload) {
public abstract class ThrowPanicNode extends Node {
static ThrowPanicNode build() {
return ThrowPanicNodeGen.create();
}
abstract Stateful execute(Object _this, Object payload);
Context getContext() {
return Context.get(this);
}
@Specialization(guards = {"payload.getConstructor() == getContext().getBuiltins().caughtPanic()"})
Stateful doCaughtPanic(
Object _this,
Atom payload,
@CachedLibrary(limit = "5") InteropLibrary interopLibrary,
@Cached BranchProfile typeErrorProfile) {
// Note [Original Exception Type]
Object originalException = payload.getFields()[1];
if (interopLibrary.isException(originalException)) {
try {
throw interopLibrary.throwException(originalException);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException(
"Impossible, `isException` returned true for `originalException`.");
}
} else {
typeErrorProfile.enter();
Builtins builtins = Context.get(this).getBuiltins();
throw new PanicException(
builtins
.error()
.makeTypeError("Exception", originalException, "internal_original_exception"),
this);
}
}
@Specialization(guards = "interopLibrary.isException(payload)")
Stateful doOtherException(
Object _this, Object payload, @CachedLibrary(limit = "5") InteropLibrary interopLibrary) {
try {
throw interopLibrary.throwException(payload);
} catch (UnsupportedMessageException e) {
throw new IllegalStateException("Impossible, `isException` returned true for `payload`.");
}
}
@Fallback
Stateful doFallback(Object _this, Object payload) {
throw new PanicException(payload, this);
}
}
/* Note [Original Exception Type]
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* We can assume that the exception stored in a `Caught_Panic` atom is a
* subclass of a `AbstractTruffleException`, because the only place which
* constructs `Caught_Panic` puts a `PanicException` there or an
* `AbstractTruffleException` directly.
*/

View File

@ -14,7 +14,12 @@ public class GetStackTraceNode extends Node {
Array execute(Object _this) {
var exception = new PanicException(null, this);
TruffleStackTrace.fillIn(exception);
return stackTraceToArray(exception);
}
public static Array stackTraceToArray(Throwable exception) {
var elements = TruffleStackTrace.getStackTrace(exception);
if (elements == null) return new Array();
var ret = new Array(elements.size());
for (int i = 0; i < elements.size(); i++) {
var element = elements.get(i);

View File

@ -12,7 +12,9 @@ import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.debug.DebugBreakpointMethodGen;
import org.enso.interpreter.node.expression.builtin.debug.DebugEvalMethodGen;
import org.enso.interpreter.node.expression.builtin.error.CatchAnyMethodGen;
import org.enso.interpreter.node.expression.builtin.error.RecoverPanicMethodGen;
import org.enso.interpreter.node.expression.builtin.error.CatchPanicMethodGen;
import org.enso.interpreter.node.expression.builtin.error.CaughtPanicConvertToDataflowErrorMethodGen;
import org.enso.interpreter.node.expression.builtin.error.GetAttachedStackTraceMethodGen;
import org.enso.interpreter.node.expression.builtin.error.ThrowPanicMethodGen;
import org.enso.interpreter.node.expression.builtin.function.ExplicitCallFunctionMethodGen;
import org.enso.interpreter.node.expression.builtin.interop.java.AddToClassPathMethodGen;
@ -63,6 +65,7 @@ public class Builtins {
private final AtomConstructor function;
private final AtomConstructor nothing;
private final AtomConstructor panic;
private final AtomConstructor caughtPanic;
private final Bool bool;
private final DataflowError dataflowError;
@ -108,6 +111,12 @@ public class Builtins {
number = new Number(language, scope);
ordering = new Ordering(language, scope);
panic = new AtomConstructor("Panic", scope).initializeFields();
caughtPanic =
new AtomConstructor("Caught_Panic", scope)
.initializeFields(
new ArgumentDefinition(0, "payload", ArgumentDefinition.ExecutionMode.EXECUTE),
new ArgumentDefinition(
1, "internal_original_exception", ArgumentDefinition.ExecutionMode.EXECUTE));
polyglot = new Polyglot(language, scope);
resource = new Resource(language, scope);
system = new System(language, scope);
@ -138,6 +147,7 @@ public class Builtins {
scope.registerConstructor(io);
scope.registerConstructor(primIo);
scope.registerConstructor(panic);
scope.registerConstructor(caughtPanic);
scope.registerConstructor(state);
scope.registerConstructor(debug);
scope.registerConstructor(projectDescription);
@ -159,10 +169,16 @@ public class Builtins {
scope.registerMethod(
runtime, "no_inline_with_arg", NoInlineWithArgMethodGen.makeFunction(language));
scope.registerMethod(runtime, "gc", GCMethodGen.makeFunction(language));
scope.registerMethod(runtime, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
scope.registerMethod(
runtime, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
scope.registerMethod(panic, "throw", ThrowPanicMethodGen.makeFunction(language));
scope.registerMethod(panic, "recover", RecoverPanicMethodGen.makeFunction(language));
scope.registerMethod(panic, "catch_primitive", CatchPanicMethodGen.makeFunction(language));
scope.registerMethod(
panic,
"primitive_get_attached_stack_trace",
GetAttachedStackTraceMethodGen.makeFunction(language));
scope.registerMethod(caughtPanic, "convert_to_dataflow_error", CaughtPanicConvertToDataflowErrorMethodGen.makeFunction(language));
scope.registerMethod(any, "catch_primitive", CatchAnyMethodGen.makeFunction(language));
scope.registerMethod(state, "get", GetStateMethodGen.makeFunction(language));
@ -308,6 +324,11 @@ public class Builtins {
return polyglot;
}
/** @return the {@code Caught_Panic} atom constructor */
public AtomConstructor caughtPanic() {
return caughtPanic;
}
/** @return the container for ordering-related builtins */
public Ordering ordering() {
return ordering;

View File

@ -3,7 +3,6 @@ package org.enso.interpreter.runtime.builtin;
import org.enso.interpreter.Language;
import org.enso.interpreter.node.expression.builtin.error.CatchErrorMethodGen;
import org.enso.interpreter.node.expression.builtin.error.ErrorToTextMethodGen;
import org.enso.interpreter.node.expression.builtin.error.GetStackTraceMethodGen;
import org.enso.interpreter.node.expression.builtin.error.GetStackTraceTextMethodGen;
import org.enso.interpreter.node.expression.builtin.error.ThrowErrorMethodGen;
import org.enso.interpreter.runtime.callable.atom.AtomConstructor;
@ -18,8 +17,8 @@ public class DataflowError {
scope.registerConstructor(error);
scope.registerMethod(error, "throw", ThrowErrorMethodGen.makeFunction(language));
scope.registerMethod(error, "catch_primitive", CatchErrorMethodGen.makeFunction(language));
scope.registerMethod(error, "get_stack_trace_text", GetStackTraceTextMethodGen.makeFunction(language));
scope.registerMethod(error, "primitive_get_stack_trace", GetStackTraceMethodGen.makeFunction(language));
scope.registerMethod(
error, "get_stack_trace_text", GetStackTraceTextMethodGen.makeFunction(language));
scope.registerMethod(error, "to_text", ErrorToTextMethodGen.makeFunction(language));
}

View File

@ -1,6 +1,7 @@
package org.enso.interpreter.runtime.error;
import com.oracle.truffle.api.CompilerDirectives;
import com.oracle.truffle.api.TruffleStackTrace;
import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.exception.AbstractTruffleException;
@ -37,7 +38,9 @@ public class DataflowError extends AbstractTruffleException {
* @return a new dataflow error
*/
public static DataflowError withoutTrace(Object payload, Node location) {
return new DataflowError(payload, location);
DataflowError result = new DataflowError(payload, location);
TruffleStackTrace.fillIn(result);
return result;
}
/**

View File

@ -234,13 +234,6 @@ type Error
get_stack_trace_text : Text
get_stack_trace_text = @Builtin_Method "Error.get_stack_trace_text"
## PRIVATE
Returns a raw representation of the current execution stack trace.
You probably want `Error.get_stack_trace` instead.
primitive_get_stack_trace : Array
primitive_get_stack_trace = @Builtin_Method "Error.primitive_get_stack_trace"
## Converts an error to a corresponding textual representation.
> Example
@ -395,7 +388,7 @@ type Panic
program control flow.
Panics "bubble up" through the program until they reach either an
invocation of Panic.recover or the program's main method. An unhandled
invocation of Panic.recover Any or the program's main method. An unhandled
panic in main will terminate the program.
? Dataflow Errors or Panics
@ -407,31 +400,64 @@ type Panic
## Throws a new panic with the provided payload.
Arguments:
- payload: The contents of the panic to be thrown.
- payload: The contents of the panic to be thrown. If the payload is a
`Caught_Panic` or a raw Java exception, instead of throwing a new panic
with it as a payload, the original exception is rethrown, preserving
its stacktrace.
> Example
Throwing a panic containing the text "Oh no!".
Panic.throw "Oh no!"
> Example
Use together with `Panic.catch` to catch only specific types of errors
and rethrow any others, without affecting their stacktraces.
Panic.catch Any (Panic.throw "foo") caught_panic-> case caught_panic.payload of
Illegal_Argument_Error message _ -> "Illegal arguments were provided: "+message
other_panic -> Panic.throw other_panic
throw : Any -> Panic
throw payload = @Builtin_Method "Panic.throw"
## Executes the provided action and converts any panic thrown by it into
an Error.
## PRIVATE
Executes the provided action and if any panic was thrown, calls the
provided callback.
If action executes successfully, the result of Panic.recover is the
result of that action. Otherwise, it is the panic that was thrown after
conversion to a dataflow error.
If action executes successfully, the result of `Panic.catch Any` is the
result of that action. Otherwise, it is the result of the provided
handler callback, executed with the caught panic as its first argument.
Arguments:
- action: The code to execute that potentially panics.
- handler: The callback to handle any panics.
catch_primitive : Any -> (Caught_Panic -> Any) -> Any
catch_primitive ~action handler = @Builtin_Method "Panic.catch_primitive"
> Example
Handling a panic for a panicking action.
## PRIVATE
Panic.recover (Panic.throw "Oh no!")
recover : Any -> Any
recover ~action = @Builtin_Method "Panic.recover"
Returns a raw representation of the stack trace attached to the provided
throwable. It can be a dataflow error, a panic or a native Java exception.
You probably want `Panic.get_attached_stack_trace` instead.
primitive_get_attached_stack_trace : Throwable -> Array
primitive_get_attached_stack_trace throwable = @Builtin_Method "Panic.primitive_get_attached_stack_trace"
type Caught_Panic
## A wrapper for a caught panic.
Arguments:
- payload: the payload carried by the error.
- internal_original_exception (private): the original Java exception that is
the source of this panic. Only for internal use. To get the Java exception
from polyglot exceptions, match the `payload` on `Polyglot_Error` and
extract the Java object from there.
@Builtin_Type
type Caught_Panic payload internal_original_exception
## Converts this caught panic into a dataflow error containing the same
payload and stack trace.
convert_to_dataflow_error : Error
convert_to_dataflow_error = @Builtin_Method "Caught_Panic.convert_to_dataflow_error"
# Function types.
type Function

View File

@ -342,17 +342,17 @@ class RuntimeErrorsTest
TestMessages.error(
contextId,
xId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
mainResId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
context.executionComplete(contextId)
)
@ -504,7 +504,7 @@ class RuntimeErrorsTest
TestMessages.error(
contextId,
xId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.update(contextId, yId, Constants.INTEGER),
TestMessages.update(contextId, mainResId, Constants.NOTHING),
@ -566,12 +566,12 @@ class RuntimeErrorsTest
TestMessages.error(
contextId,
xId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.update(contextId, mainResId, Constants.NOTHING),
context.executionComplete(contextId)
@ -617,12 +617,12 @@ class RuntimeErrorsTest
TestMessages.error(
contextId,
xId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
context.executionComplete(contextId)
)
@ -706,12 +706,12 @@ class RuntimeErrorsTest
TestMessages.error(
contextId,
xId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.update(contextId, mainResId, Constants.NOTHING),
context.executionComplete(contextId)
@ -743,6 +743,7 @@ class RuntimeErrorsTest
val requestId = UUID.randomUUID()
val moduleName = "Enso_Test.Test.Main"
val metadata = new Metadata
val fooThrowId = metadata.addItem(74, 20)
val xId = metadata.addItem(111, 8)
val yId = metadata.addItem(128, 5)
val mainResId = metadata.addItem(138, 12)
@ -796,12 +797,12 @@ class RuntimeErrorsTest
contextId,
xId,
Api.MethodPointer(moduleName, moduleName, "foo"),
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(fooThrowId, xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(fooThrowId, xId))
),
TestMessages.update(contextId, mainResId, Constants.NOTHING),
context.executionComplete(contextId)
@ -1328,12 +1329,12 @@ class RuntimeErrorsTest
contextId,
xId,
Api.MethodPointer(moduleName, moduleName, "foo"),
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.error(
contextId,
yId,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(xId))
),
TestMessages.update(
contextId,

View File

@ -2869,7 +2869,7 @@ class RuntimeServerTest
"""from Standard.Builtins import all
|
|main =
| x = Panic.recover @
| x = Panic.catch_primitive @ .convert_to_dataflow_error
| IO.println (x.catch .to_text)
|""".stripMargin.linesIterator.mkString("\n")
val contents = metadata.appendToCode(code)
@ -2910,7 +2910,7 @@ class RuntimeServerTest
Api.ExecutionResult.Diagnostic.error(
"Unrecognized token.",
Some(mainFile),
Some(model.Range(model.Position(3, 22), model.Position(3, 23)))
Some(model.Range(model.Position(3, 30), model.Position(3, 31)))
)
)
)
@ -2932,7 +2932,7 @@ class RuntimeServerTest
"""from Standard.Builtins import all
|
|main =
| x = Panic.recover ()
| x = Panic.catch_primitive () .convert_to_dataflow_error
| IO.println (x.catch .to_text)
|
|""".stripMargin.linesIterator.mkString("\n")
@ -2974,7 +2974,7 @@ class RuntimeServerTest
Api.ExecutionResult.Diagnostic.error(
"Parentheses can't be empty.",
Some(mainFile),
Some(model.Range(model.Position(3, 22), model.Position(3, 24)))
Some(model.Range(model.Position(3, 30), model.Position(3, 32)))
)
)
)

View File

@ -1712,7 +1712,7 @@ class RuntimeVisualisationsTest
TestMessages.error(
contextId,
idMain,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain))
),
context.executionComplete(contextId)
)
@ -1813,7 +1813,7 @@ class RuntimeVisualisationsTest
Api.VisualisationConfiguration(
contextId,
moduleName,
"x -> Panic.recover x . catch_primitive _.to_text"
"x -> Panic.catch_primitive x caught_panic-> caught_panic.payload.to_text"
)
)
)
@ -1877,7 +1877,8 @@ class RuntimeVisualisationsTest
// NOTE: below values need to be kept in sync with what is used internally by Rust IDE code
val visualisationModule = "Standard.Base.Main"
val visualisationCode = Source.fromResource("error_preprocessor.enso").mkString
val visualisationCode =
Source.fromResource("error_preprocessor.enso").mkString
// create context
context.send(Api.Request(requestId, Api.CreateContextRequest(contextId)))
@ -1907,7 +1908,7 @@ class RuntimeVisualisationsTest
TestMessages.error(
contextId,
idMain,
Api.ExpressionUpdate.Payload.DataflowError(Seq())
Api.ExpressionUpdate.Payload.DataflowError(Seq(idMain))
),
context.executionComplete(contextId)
)

View File

@ -13,7 +13,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
"""from Standard.Builtins import all
|
|main =
| x = Panic.recover ()
| x = Panic.catch_primitive () .convert_to_dataflow_error
| x.catch_primitive err->
| case err of
| Syntax_Error msg -> "Oopsie, it's a syntax error: " + msg
@ -28,8 +28,8 @@ class CompileDiagnosticsTest extends InterpreterTest {
"""from Standard.Builtins import all
|
|main =
| x = Panic.recover @
| x.catch_primitive .to_text
| x = Panic.catch_primitive @ caught_panic-> caught_panic.payload
| x.to_text
|""".stripMargin
eval(code) shouldEqual "(Syntax_Error 'Unrecognized token.')"
}
@ -42,7 +42,7 @@ class CompileDiagnosticsTest extends InterpreterTest {
| x = 1
| x = 2
|
|main = Panic.recover here.foo . catch_primitive .to_text
|main = Panic.catch_primitive here.foo caught_panic-> caught_panic.payload.to_text
|""".stripMargin
eval(code) shouldEqual "(Compile_Error 'Variable x is being redefined.')"
}
@ -55,9 +55,11 @@ class CompileDiagnosticsTest extends InterpreterTest {
| my_var = 10
| my_vra
|
|main = Panic.recover here.foo . catch_primitive .to_text
|main = Panic.catch_primitive here.foo caught_panic-> caught_panic.payload.to_text
|""".stripMargin
eval(code) shouldEqual "(Compile_Error 'Variable `my_vra` is not defined.')"
eval(
code
) shouldEqual "(Compile_Error 'Variable `my_vra` is not defined.')"
}
}
}

View File

@ -41,7 +41,7 @@ class PanicsTest extends InterpreterTest {
|
|main =
| thrower = x -> Panic.throw x
| caught = Panic.recover (thrower MyError)
| caught = Panic.catch_primitive (thrower MyError) .convert_to_dataflow_error
| IO.println caught
|""".stripMargin
@ -55,7 +55,7 @@ class PanicsTest extends InterpreterTest {
|polyglot java import java.lang.Long
|
|main =
| caught = Panic.recover (Long.parseLong "oops")
| caught = Panic.catch_primitive (Long.parseLong "oops") .convert_to_dataflow_error
| IO.println caught
| cause = caught.catch_primitive e-> case e of
| Polyglot_Error err -> err

View File

@ -98,7 +98,7 @@ class StateTest extends InterpreterTest {
|
|stater =
| State.put Number 5
| Panic.recover here.panicker
| Panic.catch_primitive here.panicker x->x
| State.get Number
|
|main = State.run Number 0 here.stater

View File

@ -114,8 +114,8 @@ class TextTest extends InterpreterTest {
| IO.println (Compile_Error "error :(").to_display_text
| IO.println (Inexhaustive_Pattern_Match_Error 32).to_display_text
| IO.println (Arithmetic_Error "cannot frobnicate quaternions").to_display_text
| IO.println ((Panic.recover (1 + "foo")).catch_primitive .to_display_text)
| IO.println ((Panic.recover (7 1)).catch_primitive .to_display_text)
| IO.println ((Panic.catch_primitive (1 + "foo") .convert_to_dataflow_error).catch_primitive .to_display_text)
| IO.println ((Panic.catch_primitive (7 1) .convert_to_dataflow_error).catch_primitive .to_display_text)
| IO.println (Arity_Error 10 10 20).to_display_text
|""".stripMargin
eval(code)

View File

@ -3,7 +3,7 @@ from Standard.Database import all
import Standard.Test
spec prefix connection pending=Nothing =
make_table name column_names column_typenames = Panic.recover <|
make_table name column_names column_typenames = Panic.recover Any <|
quote x = '"' + x + '"'
# TODO this is a hack with no sanitization, just for testing; it should be removed when proper create table is supported by the library
cols = column_names.zip column_typenames name-> typ->
@ -11,7 +11,7 @@ spec prefix connection pending=Nothing =
sql = "CREATE TABLE " + quote name + " (" + (cols.join ", ") + ")"
Panic.rethrow <| connection.execute_update sql
Panic.rethrow <| connection.access_table name
clean_table name = Panic.recover <|
clean_table name = Panic.recover Any <|
sql = 'DROP TABLE "' + name + '"'
Panic.rethrow <| connection.execute_update sql
t1 = make_table "T1" ["a", "b", "c"] ["INT", "INT", "INT"]

View File

@ -8,9 +8,9 @@ polyglot java import java.util.Locale as JavaLocale
with_locale locale ~test =
default_locale = JavaLocale.getDefault
JavaLocale.setDefault locale.java_locale
result = Panic.recover test . catch_primitive e->
result = Panic.catch Any test caught_panic->
JavaLocale.setDefault default_locale
Panic.throw e
Panic.throw caught_panic
JavaLocale.setDefault default_locale
result

View File

@ -70,9 +70,9 @@ spec =
Text.from Methods.get_bar . should_equal "'bar'"
Test.specify "should fail graciously when there is no conversion" <|
Panic.recover (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`"
Panic.recover Any (Foo.from (Quux 10)) . catch .to_display_text . should_equal "Could not find a conversion from `Quux` to `Foo`"
Test.specify "should fail graciously when the conversion target is invalid" <|
Panic.recover (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type."
Panic.recover Any (123.from (Quux 10)) . catch .to_display_text . should_equal "123 is not a valid conversion target. Expected a type."
Test.specify "should be callable with by-name arguments" <|
.from that=4 this=Foo . should_equal (Foo [4, 0, 0, 0])

View File

@ -3,18 +3,23 @@ from Standard.Base import all
import Standard.Test
polyglot java import java.util.Random
polyglot java import java.lang.Long
polyglot java import java.lang.NumberFormatException
type My_Type foo
throw_a_bar = Error.throw "bar"
throw_a_bar_panicking = Panic.throw "bar"
throw_a_foo_panicking = Panic.throw "foo"
throw_raw_java msg = Panic.throw (NumberFormatException.new msg)
do_a_parse str = Long.parseLong str
spec =
Test.group "No Method Errors" <|
Test.specify "should be recoverable" <|
err_1 = Panic.recover (123 . foobar "baz") . catch e->e
err_2 = Panic.recover ("foo" . baz 123) . catch e->e
err_3 = Panic.recover (My_Type False . nope) . catch e->e
err_1 = Panic.recover Any (123 . foobar "baz") . catch e->e
err_2 = Panic.recover Any ("foo" . baz 123) . catch e->e
err_3 = Panic.recover Any (My_Type False . nope) . catch e->e
err_1.target.should_equal 123
err_1.method_name.should_equal "foobar"
@ -67,9 +72,149 @@ spec =
error = Error.throw 42
Random.new error . should_fail_with Integer
Test.specify "should allow to inspect the stack trace of a recovered panic" <|
error = Panic.recover <| here.throw_a_bar_panicking
Test.specify "should allow to inspect their stacktrace" <|
error = here.throw_a_bar
error.catch . should_equal "bar"
error.get_stack_trace.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_bar"
Test.specify "should allow to inspect the stack trace of a recovered panic" <|
error = Panic.recover Any <| here.throw_a_bar_panicking
error.catch . should_equal "bar"
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
Test.group "Panics" <|
Test.specify "should be able to be caught" <|
result = Panic.catch Any (Panic.throw 42) caught_panic->
caught_panic.payload+10
result . should_equal 52
result_2 = Panic.catch Any (1 + 2) caught_panic->
caught_panic.payload+10
result_2 . should_equal 3
Test.specify "should not mix with dataflow errors" <|
result = Panic.catch Any (Error.throw 42) caught_panic->
caught_panic.payload+10
result.catch . should_equal 42
Test.specify "should provide access to stack traces" <|
stack = Panic.catch Any here.throw_a_bar_panicking caught_panic->
caught_panic.stack_trace
stack.second.name . should_equal "Error_Spec.throw_a_bar_panicking"
Test.specify "should provide access to Java stack traces" <|
stack_1 = Panic.recover Any (here.do_a_parse "foo") . stack_trace
stack_1.at 2 . name . should_equal "Error_Spec.do_a_parse"
stack_2 = Panic.catch Any (here.do_a_parse "foo") caught_panic->
caught_panic.stack_trace
stack_2.at 2 . name . should_equal "Error_Spec.do_a_parse"
Test.specify "should be able to be rethrown without changing the stack trace" <|
caught_panic = Panic.catch Any here.throw_a_bar_panicking x->x
rethrow foo = Panic.throw foo
rethrown_panic = Panic.catch Any (rethrow caught_panic) x->x
(rethrown_panic.stack_trace.length > 0).should_be_true
(rethrown_panic.stack_trace.map .name) . should_equal (caught_panic.stack_trace.map .name)
Test.specify "should allow the pattern for handling selected exceptions" <|
perform_operation ~action =
Panic.catch Any action caught_panic->
if caught_panic.payload == "bar" then 42 else
Panic.throw caught_panic
Panic.recover Any (perform_operation here.throw_a_bar_panicking) . should_equal 42
error = Panic.recover Any (perform_operation here.throw_a_foo_panicking)
error.catch . should_equal "foo"
error.stack_trace.second.name . should_equal "Error_Spec.throw_a_foo_panicking"
Test.specify "should work as in the examples" <|
fun ~act =
Panic.catch Any act caught_panic-> case caught_panic.payload of
Illegal_Argument_Error message _ -> "Illegal arguments were provided: "+message
other_panic -> Panic.throw other_panic
Panic.recover Any (fun "bar") . should_equal "bar"
Panic.recover Any (fun (Panic.throw "foo")) . catch . should_equal "foo"
Panic.recover Any (fun (Panic.throw (Illegal_Argument_Error "msg" Nothing))) . should_equal "Illegal arguments were provided: msg"
Test.specify "should allow catching Java exceptions easily" <|
parse str =
Panic.catch NumberFormatException (Long.parseLong str) caught_panic->
Error.throw (Illegal_Argument_Error "The provided string is not a valid number: "+caught_panic.payload.cause.getMessage)
parse "42" . should_equal 42
dataflow_error = parse "foo"
dataflow_error.catch . should_equal (Illegal_Argument_Error 'The provided string is not a valid number: For input string: "foo"')
Test.expect_panic_with (parse 0.0) Unsupported_Argument_Types
Test.specify "should allow to throw raw Java exceptions" <|
exception = Panic.catch NumberFormatException (here.throw_raw_java "foo") (p -> p.payload.cause)
exception.getMessage . should_equal "foo"
Panic.get_attached_stack_trace exception . second . name . should_equal "Error_Spec.throw_raw_java"
caught_panic = Panic.catch Any (here.throw_raw_java "foo") x->x
caught_panic.stack_trace.second.name . should_equal "Error_Spec.throw_raw_java"
caught_panic.payload . should_be_a Polyglot_Error
Test.specify "should allow to re-throw raw Java exceptions" <|
message_1 = Ref.new ""
caught_1 = Panic.recover Any <|
Panic.catch NumberFormatException (here.do_a_parse "foo") caught_panic->
Ref.put message_1 caught_panic.payload.cause.getMessage
Panic.throw caught_panic.payload.cause
Ref.get message_1 . should_equal 'For input string: "foo"'
caught_1.catch . should_be_a Polyglot_Error
caught_1.stack_trace.at 2 . name . should_equal "Error_Spec.do_a_parse"
message_2 = Ref.new ""
caught_2 = Panic.recover Any <|
Panic.catch NumberFormatException (here.throw_raw_java "foo") caught_panic->
Ref.put message_2 caught_panic.payload.cause.getMessage
Panic.throw caught_panic.payload.cause
Ref.get message_2 . should_equal "foo"
caught_2.catch . should_be_a Polyglot_Error
caught_2.stack_trace.second.name . should_equal "Error_Spec.throw_raw_java"
Test.specify "should allow to catch a specific panic type easily" <|
message_1 = Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) caught_panic->
caught_panic.payload.message
message_1 . should_equal "msg"
error = Panic.recover Any <| Panic.catch Illegal_Argument_Error (Panic.throw (Illegal_State_Error "foo" Nothing)) caught_panic->
caught_panic.payload.message
error.catch . should_be_an Illegal_State_Error
message_2 = Panic.catch Any (Panic.throw (Illegal_Argument_Error "msg" Nothing)) _->
"caught"
message_2 . should_equal "caught"
message_3 = Panic.catch Polyglot_Error (Long.parseLong "foo") _->
"polyglot"
message_3 . should_equal "polyglot"
message_4 = Panic.catch Any (Long.parseLong "foo") _->
"polyglot2"
message_4 . should_equal "polyglot2"
message_5 = Panic.catch Unsupported_Argument_Types (Long.parseLong 0) _->
"uat"
message_5 . should_equal "uat"
Test.expect_panic_with (Panic.catch Illegal_Argument_Error (Long.parseLong "foo") (_->"polyglot3")) Polyglot_Error
Test.expect_panic_with (Panic.catch Nothing (Long.parseLong 0) (_->"polyglot4")) Unsupported_Argument_Types
Test.specify "should be able to be recovered selectively" <|
Panic.recover Illegal_Argument_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
Panic.recover Any (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
Panic.recover [Illegal_Argument_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
Panic.recover [Illegal_State_Error, Illegal_Argument_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch . should_be_an Illegal_Argument_Error
Test.expect_panic_with <| Panic.recover Illegal_State_Error (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
Test.expect_panic_with <| Panic.recover [Illegal_State_Error, Polyglot_Error] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
Test.expect_panic_with <| Panic.recover [] (Panic.throw (Illegal_Argument_Error "msg" Nothing)) . catch
Panic.recover [Polyglot_Error] (here.do_a_parse "foo") . catch . should_be_a Polyglot_Error
Panic.recover Any here.throw_a_bar_panicking . catch . should_equal "bar"
Panic.recover Text here.throw_a_bar_panicking . stack_trace . second . name . should_equal "Error_Spec.throw_a_bar_panicking"
main = Test.Suite.run_main here.spec

View File

@ -142,7 +142,7 @@ spec = Test.group "Polyglot JS" <|
Test.specify "should allow Enso to catch JS exceptions" <|
value = My_Type 1 2
result = Panic.recover <| value.my_throw
result = Panic.recover Any <| value.my_throw
err = result.catch
err.cause.message . should_equal "JS Exc"
err.cause.name . should_equal "Error"
@ -153,7 +153,7 @@ spec = Test.group "Polyglot JS" <|
result . should_equal 7
Test.specify "should properly handle parse errors" <|
err = Panic.recover here.does_not_parse . catch
err = Panic.recover Any here.does_not_parse . catch
err.cause.message.should .contain "Expected }"
Test.specify "allow access to properties of nested objects" <|

View File

@ -142,7 +142,7 @@ spec =
Test.specify "should allow Enso to catch Python exceptions" <|
value = My_Type 1 2
result = Panic.recover <| value.my_throw
result = Panic.recover Any <| value.my_throw
err = result.catch
err.cause.args.at 0 . should_equal 'Error!'
err.cause.to_text . should_equal "RuntimeError('Error!')"
@ -153,7 +153,7 @@ spec =
result . should_equal 7
Test.specify "should properly handle parse errors" <|
err = Panic.recover here.does_not_parse . catch
err = Panic.recover Any here.does_not_parse . catch
err.cause.args.at 0 . should .contain 'invalid syntax'
Test.specify "should perform maths with mixed numbers" <|

View File

@ -125,7 +125,7 @@ spec =
Test.specify "should allow Enso to catch R exceptions" <|
value = My_Type 1 2
result = Panic.recover <| value.my_throw
result = Panic.recover Any <| value.my_throw
err = result.catch
err.to_display_text.should_equal "Polyglot error: Error: error in R code!"
@ -136,7 +136,7 @@ spec =
result . should_equal 7
Test.specify "should properly report parse errors" <|
err = Panic.recover here.does_not_parse
err = Panic.recover Any here.does_not_parse
err.catch.to_display_text.should .contain 'parse exception'
Test.specify "should perform maths with mixed numbers" <|

View File

@ -141,10 +141,10 @@ spec = Test.group "Dataflow Warnings" <|
mapped_2.catch . should_equal 17
Warning.get_all mapped_2 . catch . should_equal 17
errored_3 = Panic.recover here.throw_a_bar
errored_3 = Panic.recover Any here.throw_a_bar
mapped_3 = here.map_odd_warnings_and_errors errored_3
mapped_3.catch . should_equal "bar"
mapped_3.get_stack_trace.second.name . should_equal "Warnings_Spec.throw_a_bar"
mapped_3.stack_trace.second.name . should_equal "Warnings_Spec.throw_a_bar"
Warning.get_all mapped_3 . catch . should_equal "bar"
main = Test.Suite.run_main here.spec

View File

@ -25,7 +25,7 @@ spec =
Test.specify "should handle exceptions when reading a non-existent file" <|
file = File.new "does_not_exist.txt"
successfully_failed = Panic.recover file.read . catch e-> case e of
successfully_failed = Panic.recover Any file.read . catch e-> case e of
File.No_Such_File_Error _ -> True
_ -> False
successfully_failed . should_be_true