Skip to content

Commit c867991

Browse files
committed
Add ResultExt::or_signal; remove ResultExt::unwrap_or_propagate
1 parent 712922b commit c867991

File tree

4 files changed

+44
-61
lines changed

4 files changed

+44
-61
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
88
+ Added `use_symbols!`, which enables module code to use Lisp symbols without repeatedly interning them.
99
+ Added `define_errors!` and `Env::signal` to simplify the process of defining and signaling custom Lisp errors.
1010
- Raised the minimum supported Rust version to 1.45.
11+
- Added `ResultExt::or_signal` to make it more convenient to convert a Rust error into a Lisp error.
12+
- Remove `ResultExt::unwrap_or_propagate`.
1113

1214
## [0.16.2] - 2021-03-04
1315
- Fixed compilation on `aarch64-apple-darwin` (Apple Silicon).

src/error.rs

Lines changed: 27 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
#[doc(no_inline)]
2+
use std::{any::Any, fmt::Display, mem::MaybeUninit, result, thread};
3+
24
pub use anyhow::{self, Error};
3-
use std::mem::MaybeUninit;
4-
use std::result;
5-
use std::thread;
6-
use std::any::Any;
75
use thiserror::Error;
86

9-
use super::IntoLisp;
10-
use super::{Env, Value};
117
use emacs_module::*;
12-
use crate::{symbol::{self, IntoLispSymbol}, GlobalRef};
13-
use crate::call::IntoLispArgs;
8+
9+
use crate::{
10+
Env, Value, IntoLisp,
11+
GlobalRef,
12+
symbol::{self, IntoLispSymbol},
13+
call::IntoLispArgs,
14+
};
1415

1516
// We use const instead of enum, in case Emacs add more exit statuses in the future.
1617
// See https://github.com/rust-lang/rust/issues/36927
@@ -132,6 +133,7 @@ impl TempValue {
132133
// XXX: Technically these are unsound, but they are necessary to use the `Fail` trait. We ensure
133134
// safety by marking TempValue methods as unsafe.
134135
unsafe impl Send for TempValue {}
136+
135137
unsafe impl Sync for TempValue {}
136138

137139
impl Env {
@@ -149,16 +151,14 @@ impl Env {
149151
Err(ErrorKind::Signal {
150152
symbol: unsafe { TempValue::new(symbol.assume_init()) },
151153
data: unsafe { TempValue::new(data.assume_init()) },
152-
}
153-
.into())
154+
}.into())
154155
}
155156
(THROW, tag, value) => {
156157
self.non_local_exit_clear();
157158
Err(ErrorKind::Throw {
158159
tag: unsafe { TempValue::new(tag.assume_init()) },
159160
value: unsafe { TempValue::new(value.assume_init()) },
160-
}
161-
.into())
161+
}.into())
162162
}
163163
_ => panic!("Unexpected non local exit status {}", status),
164164
}
@@ -195,7 +195,7 @@ impl Env {
195195
if let Err(error) = m {
196196
m = match error.downcast::<ErrorKind>() {
197197
// TODO: Explain safety.
198-
Ok(err) => unsafe { return self.handle_known(&*err) },
198+
Ok(err) => unsafe { return self.handle_known(&*err); },
199199
Err(error) => Err(error),
200200
}
201201
}
@@ -210,12 +210,12 @@ impl Env {
210210
pub(crate) fn define_core_errors(&self) -> Result<()> {
211211
// FIX: Make panics louder than errors, by somehow make sure that 'rust-panic is
212212
// not a sub-type of 'error.
213-
self.define_error(symbol::rust_panic, "Rust panic", (symbol::error,))?;
214-
self.define_error(symbol::rust_error, "Rust error", (symbol::error,))?;
213+
self.define_error(symbol::rust_panic, "Rust panic", (symbol::error, ))?;
214+
self.define_error(symbol::rust_error, "Rust error", (symbol::error, ))?;
215215
self.define_error(
216216
symbol::rust_wrong_type_user_ptr,
217217
"Wrong type user-ptr",
218-
(symbol::rust_error, self.intern("wrong-type-argument")?)
218+
(symbol::rust_error, self.intern("wrong-type-argument")?),
219219
)?;
220220
Ok(())
221221
}
@@ -287,46 +287,21 @@ impl Env {
287287
}
288288
}
289289

290-
/// Emacs-specific extension methods for [`Result`].
290+
/// Emacs-specific extension methods for the standard library's [`Result`].
291291
///
292-
/// [`Result`]: type.Result.html
292+
/// [`Result`]: result::Result
293293
pub trait ResultExt<T, E> {
294-
/// Unwraps a result, yielding the content of an [`Ok`].
295-
///
296-
/// # Panics
297-
///
298-
/// Panics if the value is an [`Err`], using a sensible panic value.
294+
/// Converts the error into a Lisp signal if this result is an [`Err`]. The first element of the
295+
/// associated signal data will be a string formatted with [`Display::fmt`].
299296
///
300-
/// If the underlying error is an [`ErrorKind`], it will be used as the value of the panic,
301-
/// which makes the `#[defun]` behave as if the corresponding non-local exit was propagated.
302-
/// Otherwise, tries to use [`Display`] to get a descriptive error message.
303-
///
304-
/// This is useful when errors cannot be propagated using [`Result`], e.g. callbacks whose types
305-
/// are dictated by 3rd-party libraries.
306-
///
307-
/// # Safety
308-
///
309-
/// The panic must not propagate across an FFI boundary, e.g. this must not be used in callbacks
310-
/// that will be called by C code. See Rust's [`issue #52652`].
311-
///
312-
/// [`Ok`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Ok
313-
/// [`Err`]: https://doc.rust-lang.org/std/result/enum.Result.html#variant.Err
314-
/// [`ErrorKind`]: enum.ErrorKind.html
315-
/// [`Display`]: https://doc.rust-lang.org/std/fmt/trait.Display.html
316-
/// [`Result`]: type.Result.html
317-
/// [`issue #52652`]: https://github.com/rust-lang/rust/issues/52652
318-
#[deprecated(since = "0.12.0", note = "Use Result or a variable to track error instead")]
319-
unsafe fn unwrap_or_propagate(self) -> T;
297+
/// If the result is an [`Ok`], it is returned unchanged.
298+
fn or_signal<'e, S>(self, env: &'e Env, symbol: S) -> Result<T> where S: IntoLispSymbol<'e>;
320299
}
321300

322-
impl<T> ResultExt<T, Error> for Result<T> {
323-
#[inline]
324-
unsafe fn unwrap_or_propagate(self) -> T {
325-
self.unwrap_or_else(|error| {
326-
match error.downcast::<ErrorKind>() {
327-
Ok(err) => panic!(err),
328-
Err(error) => panic!("{}", error),
329-
};
330-
})
301+
impl<T, E: Display> ResultExt<T, E> for result::Result<T, E> {
302+
fn or_signal<'e, S>(self, env: &'e Env, symbol: S) -> Result<T> where S: IntoLispSymbol<'e> {
303+
self.or_else(|err| env.signal(symbol, (
304+
format!("{}", err),
305+
)))
331306
}
332307
}

test-module/src/test_error.rs

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//! Testing error reporting and handling.
22
3+
use std::fs;
4+
35
use emacs::{defun, CallEnv, Env, Result, Value};
46
use emacs::ErrorKind::{self, Signal, Throw};
57
use emacs::ResultExt;
@@ -58,17 +60,16 @@ fn catch<'e>(expected_tag: Value<'e>, lambda: Value<'e>) -> Result<Value<'e>> {
5860
}
5961
}
6062

61-
#[allow(deprecated)]
62-
unsafe fn apply_inner(lambda: Value<'_>, args: Value<'_>) {
63+
/// Call `apply` on LAMBDA and ARGS, propagating any signaled error.
64+
#[defun(mod_in_name = false, name = "error:apply")]
65+
fn apply<'e>(lambda: Value<'e>, args: Value<'e>) -> Result<Value<'e>> {
6366
let env = lambda.env;
64-
env.call("apply", (lambda, args)).unwrap_or_propagate();
67+
env.call("apply", (lambda, args))
6568
}
6669

67-
/// Call `apply` on LAMBDA and ARGS, using panics instead of Result to propagate errors.
68-
#[defun(mod_in_name = false, name = "error:apply")]
69-
fn apply(lambda: Value<'_>, args: Value<'_>) -> Result<()> {
70-
unsafe { apply_inner(lambda, args); }
71-
Ok(())
70+
#[defun(mod_in_name = false)]
71+
fn read_file<'e>(env: &Env, path: String) -> Result<String> {
72+
fs::read_to_string(path).or_signal(env, emrs_file_error)
7273
}
7374

7475
#[defun(mod_in_name = false, name = "error:panic")]
@@ -88,8 +89,9 @@ fn parse_arg(env: &CallEnv) -> Result<String> {
8889
}
8990

9091
emacs::define_errors! {
92+
emrs_file_error "File error"
9193
emacs_module_rs_test_error "Hello" (rust_error)
92-
error_defined_without_parent "No error message"
94+
error_defined_without_parent "Error"
9395
}
9496

9597
pub fn init(env: &Env) -> Result<()> {

test-module/tests/main.el

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,10 @@
144144
nil))
145145
msg))))
146146

147+
(ert-deftest error::wrap-signal ()
148+
(should (> (length (t/read-file "Cargo.toml")) 0))
149+
(should-error (t/read-file "!@#%%&") :type 'emrs-file-error))
150+
147151
(ert-deftest error::handling-signal ()
148152
(should (eq (t/error:get-type (lambda () (error "?"))) 'error))
149153
(should (eq (t/error:get-type (lambda () (user-error "?"))) 'user-error)))

0 commit comments

Comments
 (0)