Skip to content

Commit

Permalink
Actually implement the feature in the compiler
Browse files Browse the repository at this point in the history
Including all the bootstrapping tweaks in the library.
  • Loading branch information
scottmcm committed May 6, 2021
1 parent c10eec3 commit ca92b5a
Show file tree
Hide file tree
Showing 10 changed files with 144 additions and 51 deletions.
58 changes: 26 additions & 32 deletions compiler/rustc_ast_lowering/src/expr.rs
Expand Up @@ -562,8 +562,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::from_output(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_output(()) }`
/// 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 @@ -592,9 +592,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::from_output($tail_expr)`
block.expr = Some(this.wrap_in_try_constructor(
hir::LangItem::TryFromOk,
hir::LangItem::TryTraitFromOutput,
try_span,
tail_expr,
ok_wrapped_span,
Expand Down Expand Up @@ -1896,14 +1896,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::TryTraitBranch,
arena_vec![self; sub_expr],
)
};
Expand All @@ -1921,8 +1921,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 @@ -1931,27 +1931,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)]
// 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,
unstable_span,
from_expr,
// `ControlFlow::Break(residual) =>
// #[allow(unreachable_code)]
// return Try::from_residual(residual),`
let break_arm = {
let residual_ident = Ident::with_dummy_span(sym::residual);
let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
let from_residual_expr = self.wrap_in_try_constructor(
hir::LangItem::TryTraitFromResidual,
try_span,
self.arena.alloc(residual_expr),
unstable_span,
);
let thin_attrs = ThinVec::from(attrs);
Expand All @@ -1962,25 +1956,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
try_span,
hir::ExprKind::Break(
hir::Destination { label: None, target_id },
Some(from_err_expr),
Some(from_residual_expr),
),
thin_attrs,
))
} else {
self.arena.alloc(self.expr(
try_span,
hir::ExprKind::Ret(Some(from_err_expr)),
hir::ExprKind::Ret(Some(from_residual_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(try_span, residual_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
10 changes: 5 additions & 5 deletions compiler/rustc_ast_lowering/src/lib.rs
Expand Up @@ -331,7 +331,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::control_flow_enum, sym::try_trait_v2][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
}
.lower_crate(krate)
Expand Down Expand Up @@ -2479,14 +2479,14 @@ 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> {
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::ResultOk, field)
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowContinue, field)
}

fn pat_err(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
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::ResultErr, field)
self.pat_lang_item_variant(span, hir::LangItem::ControlFlowBreak, field)
}

fn pat_some(&mut self, span: Span, pat: &'hir hir::Pat<'hir>) -> &'hir hir::Pat<'hir> {
Expand Down
11 changes: 7 additions & 4 deletions compiler/rustc_hir/src/lang_items.rs
Expand Up @@ -308,12 +308,12 @@ language_item_table! {

Termination, sym::termination, termination, Target::Trait;

Try, kw::Try, try_trait, Target::Trait;
Try, sym::Try, try_trait, Target::Trait;

// Language items from AST lowering
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 });
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false });
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false });
TryTraitBranch, sym::branch, branch_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 @@ -331,6 +331,9 @@ language_item_table! {
ResultOk, sym::Ok, result_ok_variant, Target::Variant;
ResultErr, sym::Err, result_err_variant, Target::Variant;

ControlFlowContinue, sym::Continue, cf_continue_variant, Target::Variant;
ControlFlowBreak, sym::Break, cf_break_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
13 changes: 8 additions & 5 deletions compiler/rustc_span/src/symbol.rs
Expand Up @@ -130,10 +130,12 @@ symbols! {
BTreeSet,
BinaryHeap,
Borrow,
Break,
C,
CString,
Center,
Clone,
Continue,
Copy,
Count,
Debug,
Expand Down Expand Up @@ -326,6 +328,7 @@ symbols! {
box_patterns,
box_syntax,
braced_empty_structs,
branch,
breakpoint,
bridge,
bswap,
Expand Down Expand Up @@ -410,6 +413,7 @@ symbols! {
constructor,
contents,
context,
control_flow_enum,
convert,
copy,
copy_closures,
Expand Down Expand Up @@ -510,7 +514,6 @@ symbols! {
env,
eq,
ermsb_target_feature,
err,
exact_div,
except,
exchange_malloc,
Expand Down Expand Up @@ -580,10 +583,10 @@ symbols! {
frem_fast,
from,
from_desugaring,
from_error,
from_generator,
from_method,
from_ok,
from_output,
from_residual,
from_size_align_unchecked,
from_trait,
from_usize,
Expand Down Expand Up @@ -652,7 +655,6 @@ symbols! {
instruction_set,
intel,
into_iter,
into_result,
into_trait,
intra_doc_pointers,
intrinsics,
Expand Down Expand Up @@ -962,6 +964,7 @@ symbols! {
repr_packed,
repr_simd,
repr_transparent,
residual,
result,
result_type,
rhs,
Expand Down Expand Up @@ -1227,7 +1230,7 @@ symbols! {
try_blocks,
try_from_trait,
try_into_trait,
try_trait,
try_trait_v2,
tt,
tuple,
tuple_from_req,
Expand Down
25 changes: 24 additions & 1 deletion library/core/src/iter/adapters/peekable.rs
@@ -1,5 +1,5 @@
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen};
use crate::ops::TryWhereOutputEquals;
use crate::ops::{ControlFlow, TryWhereOutputEquals};

/// An iterator with a `peek()` that returns an optional reference to the next
/// element.
Expand Down Expand Up @@ -130,12 +130,35 @@ where
}

#[inline]
#[cfg(not(bootstrap))]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
F: FnMut(B, Self::Item) -> R,
R: TryWhereOutputEquals<B>,
{
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(r) => {
self.peeked = Some(Some(v));
R::from_residual(r)
}
},
None => self.iter.try_rfold(init, f),
}
}

#[inline]
#[cfg(bootstrap)]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where
Self: Sized,
F: FnMut(B, Self::Item) -> R,
R: TryWhereOutputEquals<B>,
{
let _use_the_import: ControlFlow<()>;
match self.peeked.take() {
Some(None) => try { init },
Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() {
Expand Down
30 changes: 30 additions & 0 deletions library/core/src/iter/traits/iterator.rs
Expand Up @@ -2412,6 +2412,36 @@ pub trait Iterator {
/// ```
#[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
#[cfg(not(bootstrap))]
fn try_find<F, R, E>(&mut self, f: F) -> Result<Option<Self::Item>, E>
where
Self: Sized,
F: FnMut(&Self::Item) -> R,
R: TryWhereOutputEquals<bool>,
// FIXME: This is a weird bound; the API should change
R: crate::ops::TryV2<Residual = Result<crate::convert::Infallible, E>>,
{
#[inline]
fn check<F, T, R, E>(mut f: F) -> impl FnMut((), T) -> ControlFlow<Result<T, E>>
where
F: FnMut(&T) -> R,
R: TryWhereOutputEquals<bool>,
R: crate::ops::TryV2<Residual = Result<crate::convert::Infallible, E>>,
{
move |(), x| match f(&x).branch() {
ControlFlow::Continue(false) => ControlFlow::CONTINUE,
ControlFlow::Continue(true) => ControlFlow::Break(Ok(x)),
ControlFlow::Break(Err(x)) => ControlFlow::Break(Err(x)),
}
}

self.try_fold((), check(f)).break_value().transpose()
}

/// We're bootstrapping.
#[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")]
#[cfg(bootstrap)]
fn try_find<F, R>(&mut self, f: F) -> Result<Option<Self::Item>, R::Error>
where
Self: Sized,
Expand Down
26 changes: 26 additions & 0 deletions library/core/src/ops/control_flow.rs
Expand Up @@ -52,8 +52,10 @@ use crate::{convert, ops};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> {
/// Move on to the next phase of the operation as normal.
#[cfg_attr(not(bootstrap), lang = "Continue")]
Continue(C),
/// Exit the operation without running subsequent phases.
#[cfg_attr(not(bootstrap), lang = "Break")]
Break(B),
// Yes, the order of the variants doesn't match the type parameters.
// They're in this order so that `ControlFlow<A, B>` <-> `Result<B, A>`
Expand Down Expand Up @@ -181,6 +183,7 @@ impl<B, C> ControlFlow<B, C> {
}
}

#[cfg(bootstrap)]
impl<R: ops::TryV1> ControlFlow<R, R::Ok> {
/// Create a `ControlFlow` from any type implementing `Try`.
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
Expand All @@ -203,6 +206,29 @@ impl<R: ops::TryV1> ControlFlow<R, R::Ok> {
}
}

#[cfg(not(bootstrap))]
impl<R: ops::TryV2> ControlFlow<R, R::Output> {
/// Create a `ControlFlow` from any type implementing `Try`.
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
#[inline]
pub fn from_try(r: R) -> Self {
match R::branch(r) {
ControlFlow::Continue(v) => ControlFlow::Continue(v),
ControlFlow::Break(v) => ControlFlow::Break(R::from_residual(v)),
}
}

/// Convert a `ControlFlow` into any type implementing `Try`;
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")]
#[inline]
pub fn into_try(self) -> R {
match self {
ControlFlow::Continue(v) => R::from_output(v),
ControlFlow::Break(v) => v,
}
}
}

impl<B> ControlFlow<B, ()> {
/// It's frequently the case that there's no value needed with `Continue`,
/// so this provides a way to avoid typing `(())`, if you prefer it.
Expand Down

0 comments on commit ca92b5a

Please sign in to comment.