Actually implement the feature in the compiler

Including all the bootstrapping tweaks in the library.
This commit is contained in:
Scott McMurray 2021-04-15 00:33:55 -07:00
parent c10eec3a1c
commit ca92b5a23a
10 changed files with 144 additions and 51 deletions

View file

@ -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| {
@ -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,
@ -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],
)
};
@ -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(
@ -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);
@ -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,
)
}

View file

@ -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)
@ -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> {

View file

@ -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;
@ -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});

View file

@ -130,10 +130,12 @@ symbols! {
BTreeSet,
BinaryHeap,
Borrow,
Break,
C,
CString,
Center,
Clone,
Continue,
Copy,
Count,
Debug,
@ -326,6 +328,7 @@ symbols! {
box_patterns,
box_syntax,
braced_empty_structs,
branch,
breakpoint,
bridge,
bswap,
@ -410,6 +413,7 @@ symbols! {
constructor,
contents,
context,
control_flow_enum,
convert,
copy,
copy_closures,
@ -510,7 +514,6 @@ symbols! {
env,
eq,
ermsb_target_feature,
err,
exact_div,
except,
exchange_malloc,
@ -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,
@ -652,7 +655,6 @@ symbols! {
instruction_set,
intel,
into_iter,
into_result,
into_trait,
intra_doc_pointers,
intrinsics,
@ -962,6 +964,7 @@ symbols! {
repr_packed,
repr_simd,
repr_transparent,
residual,
result,
result_type,
rhs,
@ -1227,7 +1230,7 @@ symbols! {
try_blocks,
try_from_trait,
try_into_trait,
try_trait,
try_trait_v2,
tt,
tuple,
tuple_from_req,

View file

@ -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.
@ -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() {

View file

@ -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,

View file

@ -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>`
@ -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")]
@ -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.

View file

@ -183,6 +183,7 @@ pub use self::range::{Range, RangeFrom, RangeFull, RangeTo};
pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive};
#[unstable(feature = "try_trait", issue = "42327")]
#[cfg(bootstrap)]
pub use self::r#try::Try;
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
@ -191,6 +192,10 @@ pub use self::r#try::Try as TryV1;
#[unstable(feature = "try_trait_v2", issue = "84277")]
pub use self::try_trait::FromResidual;
#[unstable(feature = "try_trait_v2", issue = "84277")]
#[cfg(not(bootstrap))]
pub use self::try_trait::Try;
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
pub use self::try_trait::Try as TryV2;
@ -220,4 +225,9 @@ pub use self::control_flow::ControlFlow;
/// foo::<Option<i32>, i32>();
/// ```
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
#[cfg(not(bootstrap))]
pub trait TryWhereOutputEquals<T> = TryV2<Output = T>;
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
#[cfg(bootstrap)]
pub trait TryWhereOutputEquals<T> = TryV1<Ok = T>;

View file

@ -25,7 +25,7 @@
)
)]
#[doc(alias = "?")]
#[lang = "try"]
#[cfg_attr(bootstrap, lang = "try")]
pub trait Try {
/// The type of this value when viewed as successful.
#[unstable(feature = "try_trait", issue = "42327")]
@ -43,19 +43,19 @@ pub trait Try {
/// in the return type of the enclosing scope (which must itself implement
/// `Try`). Specifically, the value `X::from_error(From::from(e))`
/// is returned, where `X` is the return type of the enclosing function.
#[lang = "into_result"]
#[cfg_attr(bootstrap, lang = "into_result")]
#[unstable(feature = "try_trait", issue = "42327")]
fn into_result(self) -> Result<Self::Ok, Self::Error>;
/// Wrap an error value to construct the composite result. For example,
/// `Result::Err(x)` and `Result::from_error(x)` are equivalent.
#[lang = "from_error"]
#[cfg_attr(bootstrap, lang = "from_error")]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_error(v: Self::Error) -> Self;
/// Wrap an OK value to construct the composite result. For example,
/// `Result::Ok(x)` and `Result::from_ok(x)` are equivalent.
#[lang = "from_ok"]
#[cfg_attr(bootstrap, lang = "from_ok")]
#[unstable(feature = "try_trait", issue = "42327")]
fn from_ok(v: Self::Ok) -> Self;
}

View file

@ -119,6 +119,7 @@ use crate::ops::ControlFlow;
/// }
/// ```
#[unstable(feature = "try_trait_v2", issue = "84277")]
#[cfg_attr(not(bootstrap), lang = "Try")]
pub trait Try: FromResidual {
/// The type of the value produced by `?` when *not* short-circuiting.
#[unstable(feature = "try_trait_v2", issue = "84277")]
@ -178,6 +179,7 @@ pub trait Try: FromResidual {
/// let r = std::iter::empty().try_fold(4, |_, ()| -> Option<_> { unreachable!() });
/// assert_eq!(r, Some(4));
/// ```
#[cfg_attr(not(bootstrap), lang = "from_output")]
#[unstable(feature = "try_trait_v2", issue = "84277")]
fn from_output(output: Self::Output) -> Self;
@ -206,6 +208,7 @@ pub trait Try: FromResidual {
/// ControlFlow::Break(ControlFlow::Break(3)),
/// );
/// ```
#[cfg_attr(not(bootstrap), lang = "branch")]
#[unstable(feature = "try_trait_v2", issue = "84277")]
fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
}
@ -238,6 +241,7 @@ pub trait FromResidual<R = <Self as Try>::Residual> {
/// ControlFlow::Break(5),
/// );
/// ```
#[cfg_attr(not(bootstrap), lang = "from_residual")]
#[unstable(feature = "try_trait_v2", issue = "84277")]
fn from_residual(residual: R) -> Self;
}