Skip to content

Commit

Permalink
Use a Carrier trait with the ? operator
Browse files Browse the repository at this point in the history
Allows use with `Option` and custom `Result`-like types.
  • Loading branch information
nrc committed Aug 18, 2016
1 parent aef6971 commit 683bcc0
Show file tree
Hide file tree
Showing 4 changed files with 296 additions and 49 deletions.
125 changes: 125 additions & 0 deletions src/libcore/ops.rs
Expand Up @@ -75,6 +75,8 @@
use cmp::PartialOrd;
use fmt;
use marker::{Sized, Unsize};
use result::Result::{self, Ok, Err};
use option::Option::{self, Some, None};

/// The `Drop` trait is used to run some code when a value goes out of scope.
/// This is sometimes called a 'destructor'.
Expand Down Expand Up @@ -2150,3 +2152,126 @@ pub trait BoxPlace<Data: ?Sized> : Place<Data> {
/// Creates a globally fresh place.
fn make_place() -> Self;
}

/// A trait for types which have success and error states and are meant to work
/// with the question mark operator.
/// When the `?` operator is used with a value, whether the value is in the
/// success or error state is determined by calling `translate`.
///
/// This trait is **very** experimental, it will probably be iterated on heavily
/// before it is stabilised. Implementors should expect change. Users of `?`
/// should not rely on any implementations of `Carrier` other than `Result`,
/// i.e., you should not expect `?` to continue to work with `Option`, etc.
#[unstable(feature = "question_mark_carrier", issue = "31436")]
pub trait Carrier {
/// The type of the value when computation succeeds.
type Success;
/// The type of the value when computation errors out.
type Error;

/// Create a `Carrier` from a success value.
fn from_success(Self::Success) -> Self;

/// Create a `Carrier` from an error value.
fn from_error(Self::Error) -> Self;

/// Translate this `Carrier` to another implementation of `Carrier` with the
/// same associated types.
fn translate<T>(self) -> T where T: Carrier<Success=Self::Success, Error=Self::Error>;
}

#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U, V> Carrier for Result<U, V> {
type Success = U;
type Error = V;

fn from_success(u: U) -> Result<U, V> {
Ok(u)
}

fn from_error(e: V) -> Result<U, V> {
Err(e)
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=V>
{
match self {
Ok(u) => T::from_success(u),
Err(e) => T::from_error(e),
}
}
}

#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl<U> Carrier for Option<U> {
type Success = U;
type Error = ();

fn from_success(u: U) -> Option<U> {
Some(u)
}

fn from_error(_: ()) -> Option<U> {
None
}

fn translate<T>(self) -> T
where T: Carrier<Success=U, Error=()>
{
match self {
Some(u) => T::from_success(u),
None => T::from_error(()),
}
}
}

// Implementing Carrier for bools means it's easy to write short-circuiting
// functions. E.g.,
// ```
// fn foo() -> bool {
// if !(f() || g()) {
// return false;
// }
//
// some_computation();
// if h() {
// return false;
// }
//
// more_computation();
// i()
// }
// ```
// becomes
// ```
// fn foo() -> bool {
// (f() || g())?;
// some_computation();
// (!h())?;
// more_computation();
// i()
// }
// ```
#[unstable(feature = "question_mark_carrier", issue = "31436")]
impl Carrier for bool {
type Success = ();
type Error = ();

fn from_success(_: ()) -> bool {
true
}

fn from_error(_: ()) -> bool {
false
}

fn translate<T>(self) -> T
where T: Carrier<Success=(), Error=()>
{
match self {
true => T::from_success(()),
false => T::from_error(()),
}
}
}
130 changes: 81 additions & 49 deletions src/librustc/hir/lowering.rs
Expand Up @@ -966,7 +966,7 @@ impl<'a> LoweringContext<'a> {
let inplace_finalize = ["ops", "InPlace", "finalize"];

let make_call = |this: &mut LoweringContext, p, args| {
let path = this.core_path(e.span, p);
let path = this.std_path(e.span, p);
let path = this.expr_path(path, ThinVec::new());
this.expr_call(e.span, path, args)
};
Expand Down Expand Up @@ -1159,15 +1159,13 @@ impl<'a> LoweringContext<'a> {
ast_expr: &Expr,
path: &[&str],
fields: &[(&str, &P<Expr>)]) -> P<hir::Expr> {
let strs = this.std_path(&iter::once(&"ops")
.chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let structpath = this.path_global(ast_expr.span, strs);
let struct_path = this.std_path(ast_expr.span,
&iter::once(&"ops").chain(path)
.map(|s| *s)
.collect::<Vec<_>>());

let hir_expr = if fields.len() == 0 {
this.expr_path(structpath, ast_expr.attrs.clone())
this.expr_path(struct_path, ast_expr.attrs.clone())
} else {
let fields = fields.into_iter().map(|&(s, e)| {
let expr = this.lower_expr(&e);
Expand All @@ -1180,7 +1178,7 @@ impl<'a> LoweringContext<'a> {
}).collect();
let attrs = ast_expr.attrs.clone();

this.expr_struct(ast_expr.span, structpath, fields, None, attrs)
this.expr_struct(ast_expr.span, struct_path, fields, None, attrs)
};

this.signal_block_expr(hir_vec![],
Expand Down Expand Up @@ -1463,11 +1461,7 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::Iterator::next(&mut iter) { ... }`
let match_expr = {
let next_path = {
let strs = self.std_path(&["iter", "Iterator", "next"]);

self.path_global(e.span, strs)
};
let next_path = self.std_path(e.span, &["iter", "Iterator", "next"]);
let iter = self.expr_ident(e.span, iter, iter_pat.id);
let ref_mut_iter = self.expr_mut_addr_of(e.span, iter);
let next_path = self.expr_path(next_path, ThinVec::new());
Expand All @@ -1494,11 +1488,8 @@ impl<'a> LoweringContext<'a> {

// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
let into_iter_expr = {
let into_iter_path = {
let strs = self.std_path(&["iter", "IntoIterator", "into_iter"]);

self.path_global(e.span, strs)
};
let into_iter_path = self.std_path(e.span,
&["iter", "IntoIterator", "into_iter"]);

let into_iter = self.expr_path(into_iter_path, ThinVec::new());
self.expr_call(e.span, into_iter, hir_vec![head])
Expand Down Expand Up @@ -1527,16 +1518,32 @@ impl<'a> LoweringContext<'a> {
// to:
//
// {
// match <expr> {
// match { Carrier::translate( { <expr> } ) } {
// Ok(val) => val,
// Err(err) => {
// return Err(From::from(err))
// }
// Err(err) => { return Carrier::from_error(From::from(err)); }
// }
// }

// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
// { Carrier::translate( { <expr> } ) }
let discr = {
// expand <expr>
let sub_expr = self.lower_expr(sub_expr);
let sub_expr = self.signal_block_expr(hir_vec![],
sub_expr,
e.span,
hir::PopUnstableBlock,
ThinVec::new());

let path = self.std_path(e.span, &["ops", "Carrier", "translate"]);
let path = self.expr_path(path, ThinVec::new());
let call = self.expr_call(e.span, path, hir_vec![sub_expr]);

self.signal_block_expr(hir_vec![],
call,
e.span,
hir::PushUnstableBlock,
ThinVec::new())
};

// Ok(val) => val
let ok_arm = {
Expand All @@ -1548,32 +1555,35 @@ impl<'a> LoweringContext<'a> {
self.arm(hir_vec![ok_pat], val_expr)
};

// Err(err) => return Err(From::from(err))
// Err(err) => { return Carrier::from_error(From::from(err)); }
let err_arm = {
let err_ident = self.str_to_ident("err");
let err_local = self.pat_ident(e.span, err_ident);
let from_expr = {
let path = self.std_path(&["convert", "From", "from"]);
let path = self.path_global(e.span, path);
let path = self.std_path(e.span, &["convert", "From", "from"]);
let from = self.expr_path(path, ThinVec::new());
let err_expr = self.expr_ident(e.span, err_ident, err_local.id);

self.expr_call(e.span, from, hir_vec![err_expr])
};
let err_expr = {
let path = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(e.span, path);
let err_ctor = self.expr_path(path, ThinVec::new());
self.expr_call(e.span, err_ctor, hir_vec![from_expr])
let from_err_expr = {
let path = self.std_path(e.span, &["ops", "Carrier", "from_error"]);
let from_err = self.expr_path(path, ThinVec::new());
self.expr_call(e.span, from_err, hir_vec![from_expr])
};
let err_pat = self.pat_err(e.span, err_local);

let ret_expr = self.expr(e.span,
hir::Expr_::ExprRet(Some(err_expr)),
ThinVec::new());
self.arm(hir_vec![err_pat], ret_expr)
hir::Expr_::ExprRet(Some(from_err_expr)),
ThinVec::new());
let ret_stmt = self.stmt_expr(ret_expr);
let block = self.signal_block_stmt(ret_stmt, e.span,
hir::PushUnstableBlock, ThinVec::new());

let err_pat = self.pat_err(e.span, err_local);
self.arm(hir_vec![err_pat], block)
};

return self.expr_match(e.span, sub_expr, hir_vec![err_arm, ok_arm],
return self.expr_match(e.span, discr, hir_vec![err_arm, ok_arm],
hir::MatchSource::TryDesugar);
}

Expand Down Expand Up @@ -1787,6 +1797,15 @@ impl<'a> LoweringContext<'a> {
(respan(sp, hir::StmtDecl(P(decl), self.next_id())), pat_id)
}

// Turns `<expr>` into `<expr>;`, note that this produces a StmtSemi, not a
// StmtExpr.
fn stmt_expr(&self, expr: P<hir::Expr>) -> hir::Stmt {
hir::Stmt {
span: expr.span,
node: hir::StmtSemi(expr, self.next_id()),
}
}

fn block_expr(&mut self, expr: P<hir::Expr>) -> P<hir::Block> {
self.block_all(expr.span, hir::HirVec::new(), Some(expr))
}
Expand All @@ -1803,26 +1822,22 @@ impl<'a> LoweringContext<'a> {
}

fn pat_ok(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let ok = self.std_path(&["result", "Result", "Ok"]);
let path = self.path_global(span, ok);
let path = self.std_path(span, &["result", "Result", "Ok"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_err(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let err = self.std_path(&["result", "Result", "Err"]);
let path = self.path_global(span, err);
let path = self.std_path(span, &["result", "Result", "Err"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_some(&mut self, span: Span, pat: P<hir::Pat>) -> P<hir::Pat> {
let some = self.std_path(&["option", "Option", "Some"]);
let path = self.path_global(span, some);
let path = self.std_path(span, &["option", "Option", "Some"]);
self.pat_enum(span, path, hir_vec![pat])
}

fn pat_none(&mut self, span: Span) -> P<hir::Pat> {
let none = self.std_path(&["option", "Option", "None"]);
let path = self.path_global(span, none);
let path = self.std_path(span, &["option", "Option", "None"]);
self.pat_enum(span, path, hir_vec![])
}

Expand Down Expand Up @@ -1920,7 +1935,7 @@ impl<'a> LoweringContext<'a> {
}
}

fn std_path(&mut self, components: &[&str]) -> Vec<Name> {
fn std_path_components(&mut self, components: &[&str]) -> Vec<Name> {
let mut v = Vec::new();
if let Some(s) = self.crate_root {
v.push(token::intern(s));
Expand All @@ -1931,8 +1946,8 @@ impl<'a> LoweringContext<'a> {

// Given suffix ["b","c","d"], returns path `::std::b::c::d` when
// `fld.cx.use_std`, and `::core::b::c::d` otherwise.
fn core_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path(components);
fn std_path(&mut self, span: Span, components: &[&str]) -> hir::Path {
let idents = self.std_path_components(components);
self.path_global(span, idents)
}

Expand All @@ -1953,4 +1968,21 @@ impl<'a> LoweringContext<'a> {
});
self.expr_block(block, attrs)
}

fn signal_block_stmt(&mut self,
stmt: hir::Stmt,
span: Span,
rule: hir::BlockCheckMode,
attrs: ThinVec<Attribute>)
-> P<hir::Expr> {
let id = self.next_id();
let block = P(hir::Block {
rules: rule,
span: span,
id: id,
stmts: hir_vec![stmt],
expr: None,
});
self.expr_block(block, attrs)
}
}

0 comments on commit 683bcc0

Please sign in to comment.