Skip to content

Commit

Permalink
PoC: A new hybrid design for Try
Browse files Browse the repository at this point in the history
  • Loading branch information
scottmcm committed Feb 21, 2021
1 parent 3e826bb commit 0b12d0d
Show file tree
Hide file tree
Showing 46 changed files with 808 additions and 283 deletions.
54 changes: 24 additions & 30 deletions compiler/rustc_ast_lowering/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -462,8 +462,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
)
}

/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }`
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::continue_with(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::continue_with(()) }`
/// and save the block id to use it as a break target for desugaring of the `?` operator.
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
self.with_catch_scope(body.id, |this| {
Expand Down Expand Up @@ -492,9 +492,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ok_wrapped_span =
this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None);

// `::std::ops::Try::from_ok($tail_expr)`
// `::std::ops::Try::continue_with($tail_expr)`
block.expr = Some(this.wrap_in_try_constructor(
hir::LangItem::TryFromOk,
hir::LangItem::TryContinueWith,
try_span,
tail_expr,
ok_wrapped_span,
Expand Down Expand Up @@ -1793,14 +1793,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.allow_try_trait.clone(),
);

// `Try::into_result(<expr>)`
// `Try::branch(<expr>)`
let scrutinee = {
// expand <expr>
let sub_expr = self.lower_expr_mut(sub_expr);

self.expr_call_lang_item_fn(
unstable_span,
hir::LangItem::TryIntoResult,
hir::LangItem::TryBranch,
arena_vec![self; sub_expr],
)
};
Expand All @@ -1818,8 +1818,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
};
let attrs = vec![attr];

// `Ok(val) => #[allow(unreachable_code)] val,`
let ok_arm = {
// `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
let continue_arm = {
let val_ident = Ident::with_dummy_span(sym::val);
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
let val_expr = self.arena.alloc(self.expr_ident_with_attrs(
Expand All @@ -1828,27 +1828,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
val_pat_nid,
ThinVec::from(attrs.clone()),
));
let ok_pat = self.pat_ok(span, val_pat);
self.arm(ok_pat, val_expr)
let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
self.arm(continue_pat, val_expr)
};

// `Err(err) => #[allow(unreachable_code)]
// `ControlFlow::Break(err) => #[allow(unreachable_code)]
// return Try::from_error(From::from(err)),`
let err_arm = {
let err_ident = Ident::with_dummy_span(sym::err);
let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident);
let from_expr = {
let err_expr = self.expr_ident_mut(try_span, err_ident, err_local_nid);
self.expr_call_lang_item_fn(
try_span,
hir::LangItem::FromFrom,
arena_vec![self; err_expr],
)
};
let from_err_expr = self.wrap_in_try_constructor(
hir::LangItem::TryFromError,
let break_arm = {
let holder_ident = Ident::with_dummy_span(sym::holder);
let (holder_local, holder_local_nid) = self.pat_ident(try_span, holder_ident);
let holder_expr =
self.arena.alloc(self.expr_ident_mut(try_span, holder_ident, holder_local_nid));
let from_holder_expr = self.wrap_in_try_constructor(
hir::LangItem::FromHolder,
unstable_span,
from_expr,
holder_expr,
unstable_span,
);
let thin_attrs = ThinVec::from(attrs);
Expand All @@ -1859,25 +1853,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
try_span,
hir::ExprKind::Break(
hir::Destination { label: None, target_id },
Some(from_err_expr),
Some(from_holder_expr),
),
thin_attrs,
))
} else {
self.arena.alloc(self.expr(
try_span,
hir::ExprKind::Ret(Some(from_err_expr)),
hir::ExprKind::Ret(Some(from_holder_expr)),
thin_attrs,
))
};

let err_pat = self.pat_err(try_span, err_local);
self.arm(err_pat, ret_expr)
let break_pat = self.pat_cf_break(unstable_span, holder_local);
self.arm(break_pat, ret_expr)
};

hir::ExprKind::Match(
scrutinee,
arena_vec![self; err_arm, ok_arm],
arena_vec![self; break_arm, continue_arm],
hir::MatchSource::TryDesugar,
)
}
Expand Down
28 changes: 19 additions & 9 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,7 @@ pub fn lower_crate<'a, 'hir>(
lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false,
in_scope_lifetimes: Vec::new(),
allow_try_trait: Some([sym::try_trait][..].into()),
allow_try_trait: Some([sym::try_trait, sym::try_trait_v2, sym::control_flow_enum][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
}
.lower_crate(krate)
Expand Down Expand Up @@ -2546,15 +2546,15 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.pat(span, hir::PatKind::Lit(expr))
}

fn pat_ok(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ResultOk, field)
}
// fn pat_ok(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
// let field = self.single_pat_field(span, pat);
// self.pat_lang_item_variant(span, hir::LangItem::ResultOk, field)
// }

fn pat_err(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ResultErr, field)
}
// fn pat_err(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
// let field = self.single_pat_field(span, pat);
// self.pat_lang_item_variant(span, hir::LangItem::ResultErr, field)
// }

fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
let field = self.single_pat_field(span, pat);
Expand All @@ -2565,6 +2565,16 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.pat_lang_item_variant(span, hir::LangItem::OptionNone, &[])
}

fn pat_cf_continue(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field)
}

fn pat_cf_break(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
let field = self.single_pat_field(span, pat);
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowBreak, field)
}

fn single_pat_field(
&mut self,
span: Span,
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_hir/src/lang_items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,9 @@ language_item_table! {
TryFromError, sym::from_error, from_error_fn, Target::Method(MethodKind::Trait { body: false });
TryFromOk, sym::from_ok, from_ok_fn, Target::Method(MethodKind::Trait { body: false });
TryIntoResult, sym::into_result, into_result_fn, Target::Method(MethodKind::Trait { body: false });
TryContinueWith, sym::continue_with, continue_with_fn, Target::Method(MethodKind::Trait { body: false });
TryBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false });
FromHolder, sym::from_holder, from_holder_fn, Target::Method(MethodKind::Trait { body: false });

PollReady, sym::Ready, poll_ready_variant, Target::Variant;
PollPending, sym::Pending, poll_pending_variant, Target::Variant;
Expand All @@ -323,6 +326,9 @@ language_item_table! {
ResultOk, sym::Ok, result_ok_variant, Target::Variant;
ResultErr, sym::Err, result_err_variant, Target::Variant;

ControlFlowBreak, sym::Break, cf_break_variant, Target::Variant;
ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant;

IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false });
IteratorNext, sym::next, next_fn, Target::Method(MethodKind::Trait { body: false});

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/ty/query/on_disk_cache.rs
Original file line number Diff line number Diff line change
Expand Up @@ -405,7 +405,7 @@ impl<'sess> OnDiskCache<'sess> {

// Encode the position of the footer as the last 8 bytes of the
// file so we know where to look for it.
IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder)?;
IntEncodedWithFixedSize(footer_pos).encode(encoder.encoder).map_err(|x| x)?;

// DO NOT WRITE ANYTHING TO THE ENCODER AFTER THIS POINT! The address
// of the footer must be the last thing in the data stream.
Expand Down
8 changes: 8 additions & 0 deletions compiler/rustc_span/src/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,12 @@ symbols! {
Argument,
ArgumentV1,
Arguments,
Break,
C,
CString,
Center,
Clone,
Continue,
Copy,
Count,
Debug,
Expand Down Expand Up @@ -313,6 +315,7 @@ symbols! {
box_patterns,
box_syntax,
braced_empty_structs,
branch,
breakpoint,
bridge,
bswap,
Expand Down Expand Up @@ -392,6 +395,8 @@ symbols! {
constructor,
contents,
context,
continue_with,
control_flow_enum,
convert,
copy,
copy_closures,
Expand Down Expand Up @@ -561,6 +566,7 @@ symbols! {
from_desugaring,
from_error,
from_generator,
from_holder,
from_method,
from_ok,
from_size_align_unchecked,
Expand All @@ -587,6 +593,7 @@ symbols! {
hash,
hexagon_target_feature,
hidden,
holder,
homogeneous_aggregate,
html_favicon_url,
html_logo_url,
Expand Down Expand Up @@ -1177,6 +1184,7 @@ symbols! {
try_from_trait,
try_into_trait,
try_trait,
try_trait_v2,
tt,
tuple,
tuple_from_req,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_typeck/src/check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
) -> Ty<'tcx> {
debug!(">> type-checking: expr={:?} expected={:?}", expr, expected);

// True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block
// True if `expr` is a `Try::continue_with(())` that is a result of desugaring a try block
// without the final expr (e.g. `try { return; }`). We don't want to generate an
// unreachable_code lint for it since warnings for autogenerated code are confusing.
let is_try_block_generated_unit_expr = match expr.kind {
Expand Down
1 change: 1 addition & 0 deletions library/alloc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@
#![feature(alloc_layout_extra)]
#![feature(trusted_random_access)]
#![feature(try_trait)]
#![feature(try_trait_v2)]
#![feature(type_alias_impl_trait)]
#![feature(associated_type_bounds)]
#![feature(slice_group_by)]
Expand Down
24 changes: 24 additions & 0 deletions library/core/src/iter/adapters/peekable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ where
}
}

#[cfg(bootstrap)]
#[inline]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Expand All @@ -149,6 +150,29 @@ where
}
}

#[cfg(not(bootstrap))]
#[inline]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
F: FnMut(B, Self::Item) -> R,
R: Try<Ok = B>,
{
use crate::ops::ControlFlow;

match self.peeked.take() {
Some(None) => try { init },
Some(Some(v)) => match self.iter.try_rfold(init, &mut f).branch() {
ControlFlow::Continue(acc) => f(acc, v),
ControlFlow::Break(h) => {
self.peeked = Some(Some(v));
R::from_holder(h)
}
},
None => self.iter.try_rfold(init, f),
}
}

#[inline]
fn rfold<Acc, Fold>(self, init: Acc, mut fold: Fold) -> Acc
where
Expand Down
58 changes: 56 additions & 2 deletions library/core/src/iter/traits/iterator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
// can't split that into multiple files.

use crate::cmp::{self, Ordering};
use crate::ops::{Add, ControlFlow, Try};
use crate::ops::{self, Add, ControlFlow, Try};

use super::super::TrustedRandomAccess;
use super::super::{Chain, Cloned, Copied, Cycle, Enumerate, Filter, FilterMap, Fuse};
Expand Down Expand Up @@ -2388,13 +2388,14 @@ pub trait Iterator {
/// let result = a.iter().try_find(|&&s| is_my_num(s, 5));
/// assert!(result.is_err());
/// ```
#[cfg(bootstrap)]
#[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
fn try_find<F, R>(&mut self, f: F) -> Result<Option<Self::Item>, R::Error>
where
Self: Sized,
F: FnMut(&Self::Item) -> R,
R: Try<Ok = bool>,
R: ops::Try<Ok = bool>,
{
#[inline]
fn check<F, T, R>(mut f: F) -> impl FnMut((), T) -> ControlFlow<Result<T, R::Error>>
Expand All @@ -2412,6 +2413,59 @@ pub trait Iterator {
self.try_fold((), check(f)).break_value().transpose()
}

/// Applies function to the elements of iterator and returns
/// the first true result or the first error.
///
/// # Examples
///
/// ```
/// #![feature(try_find)]
///
/// let a = ["1", "2", "lol", "NaN", "5"];
///
/// let is_my_num = |s: &str, search: i32| -> Result<bool, std::num::ParseIntError> {
/// Ok(s.parse::<i32>()? == search)
/// };
///
/// let result = a.iter().try_find(|&&s| is_my_num(s, 2));
/// assert_eq!(result, Ok(Some(&"2")));
///
/// let result = a.iter().try_find(|&&s| is_my_num(s, 5));
/// assert!(result.is_err());
/// ```
#[cfg(not(bootstrap))]
#[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
fn try_find<F, R>(
&mut self,
f: F,
) -> <R::Holder as ops::BreakHolder<Option<Self::Item>>>::Output
where
Self: Sized,
F: FnMut(&Self::Item) -> R,
R: ops::Try<Ok = bool>,
R::Holder: ops::BreakHolder<Option<Self::Item>>,
{
#[inline]
fn check<F, T, R>(mut f: F) -> impl FnMut((), T) -> ControlFlow<Result<T, R::Holder>>
where
F: FnMut(&T) -> R,
R: Try<Ok = bool>,
{
move |(), x| match f(&x).branch() {
ControlFlow::Continue(false) => ControlFlow::CONTINUE,
ControlFlow::Continue(true) => ControlFlow::Break(Ok(x)),
ControlFlow::Break(h) => ControlFlow::Break(Err(h)),
}
}

match self.try_fold((), check(f)) {
ControlFlow::Continue(()) => ops::TryCore::continue_with(None),
ControlFlow::Break(Ok(x)) => ops::TryCore::continue_with(Some(x)),
ControlFlow::Break(Err(h)) => Try::from_holder(h),
}
}

/// Searches for an element in an iterator, returning its index.
///
/// `position()` takes a closure that returns `true` or `false`. It applies
Expand Down
Loading

0 comments on commit 0b12d0d

Please sign in to comment.