1
Fork 0

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>) }`, /// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_output(<expr>) }`,
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }` /// `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. /// 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> { fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind<'hir> {
self.with_catch_scope(body.id, |this| { self.with_catch_scope(body.id, |this| {
@ -592,9 +592,9 @@ impl<'hir> LoweringContext<'_, 'hir> {
let ok_wrapped_span = let ok_wrapped_span =
this.mark_span_with_reason(DesugaringKind::TryBlock, tail_expr.span, None); 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( block.expr = Some(this.wrap_in_try_constructor(
hir::LangItem::TryFromOk, hir::LangItem::TryTraitFromOutput,
try_span, try_span,
tail_expr, tail_expr,
ok_wrapped_span, ok_wrapped_span,
@ -1896,14 +1896,14 @@ impl<'hir> LoweringContext<'_, 'hir> {
self.allow_try_trait.clone(), self.allow_try_trait.clone(),
); );
// `Try::into_result(<expr>)` // `Try::branch(<expr>)`
let scrutinee = { let scrutinee = {
// expand <expr> // expand <expr>
let sub_expr = self.lower_expr_mut(sub_expr); let sub_expr = self.lower_expr_mut(sub_expr);
self.expr_call_lang_item_fn( self.expr_call_lang_item_fn(
unstable_span, unstable_span,
hir::LangItem::TryIntoResult, hir::LangItem::TryTraitBranch,
arena_vec![self; sub_expr], arena_vec![self; sub_expr],
) )
}; };
@ -1921,8 +1921,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
}; };
let attrs = vec![attr]; let attrs = vec![attr];
// `Ok(val) => #[allow(unreachable_code)] val,` // `ControlFlow::Continue(val) => #[allow(unreachable_code)] val,`
let ok_arm = { let continue_arm = {
let val_ident = Ident::with_dummy_span(sym::val); let val_ident = Ident::with_dummy_span(sym::val);
let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident); let (val_pat, val_pat_nid) = self.pat_ident(span, val_ident);
let val_expr = self.arena.alloc(self.expr_ident_with_attrs( let val_expr = self.arena.alloc(self.expr_ident_with_attrs(
@ -1931,27 +1931,21 @@ impl<'hir> LoweringContext<'_, 'hir> {
val_pat_nid, val_pat_nid,
ThinVec::from(attrs.clone()), ThinVec::from(attrs.clone()),
)); ));
let ok_pat = self.pat_ok(span, val_pat); let continue_pat = self.pat_cf_continue(unstable_span, val_pat);
self.arm(ok_pat, val_expr) self.arm(continue_pat, val_expr)
}; };
// `Err(err) => #[allow(unreachable_code)] // `ControlFlow::Break(residual) =>
// return Try::from_error(From::from(err)),` // #[allow(unreachable_code)]
let err_arm = { // return Try::from_residual(residual),`
let err_ident = Ident::with_dummy_span(sym::err); let break_arm = {
let (err_local, err_local_nid) = self.pat_ident(try_span, err_ident); let residual_ident = Ident::with_dummy_span(sym::residual);
let from_expr = { let (residual_local, residual_local_nid) = self.pat_ident(try_span, residual_ident);
let err_expr = self.expr_ident_mut(try_span, err_ident, err_local_nid); let residual_expr = self.expr_ident_mut(try_span, residual_ident, residual_local_nid);
self.expr_call_lang_item_fn( let from_residual_expr = self.wrap_in_try_constructor(
hir::LangItem::TryTraitFromResidual,
try_span, try_span,
hir::LangItem::FromFrom, self.arena.alloc(residual_expr),
arena_vec![self; err_expr],
)
};
let from_err_expr = self.wrap_in_try_constructor(
hir::LangItem::TryFromError,
unstable_span,
from_expr,
unstable_span, unstable_span,
); );
let thin_attrs = ThinVec::from(attrs); let thin_attrs = ThinVec::from(attrs);
@ -1962,25 +1956,25 @@ impl<'hir> LoweringContext<'_, 'hir> {
try_span, try_span,
hir::ExprKind::Break( hir::ExprKind::Break(
hir::Destination { label: None, target_id }, hir::Destination { label: None, target_id },
Some(from_err_expr), Some(from_residual_expr),
), ),
thin_attrs, thin_attrs,
)) ))
} else { } else {
self.arena.alloc(self.expr( self.arena.alloc(self.expr(
try_span, try_span,
hir::ExprKind::Ret(Some(from_err_expr)), hir::ExprKind::Ret(Some(from_residual_expr)),
thin_attrs, thin_attrs,
)) ))
}; };
let err_pat = self.pat_err(try_span, err_local); let break_pat = self.pat_cf_break(try_span, residual_local);
self.arm(err_pat, ret_expr) self.arm(break_pat, ret_expr)
}; };
hir::ExprKind::Match( hir::ExprKind::Match(
scrutinee, scrutinee,
arena_vec![self; err_arm, ok_arm], arena_vec![self; break_arm, continue_arm],
hir::MatchSource::TryDesugar, hir::MatchSource::TryDesugar,
) )
} }

View file

@ -331,7 +331,7 @@ pub fn lower_crate<'a, 'hir>(
lifetimes_to_define: Vec::new(), lifetimes_to_define: Vec::new(),
is_collecting_in_band_lifetimes: false, is_collecting_in_band_lifetimes: false,
in_scope_lifetimes: Vec::new(), 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()), allow_gen_future: Some([sym::gen_future][..].into()),
} }
.lower_crate(krate) .lower_crate(krate)
@ -2479,14 +2479,14 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
self.pat(span, hir::PatKind::Lit(expr)) 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); 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); 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> { 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; 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 // Language items from AST lowering
TryFromError, sym::from_error, from_error_fn, Target::Method(MethodKind::Trait { body: false }); TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false });
TryFromOk, sym::from_ok, from_ok_fn, Target::Method(MethodKind::Trait { body: false }); TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false });
TryIntoResult, sym::into_result, into_result_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; PollReady, sym::Ready, poll_ready_variant, Target::Variant;
PollPending, sym::Pending, poll_pending_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; ResultOk, sym::Ok, result_ok_variant, Target::Variant;
ResultErr, sym::Err, result_err_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 }); IntoIterIntoIter, sym::into_iter, into_iter_fn, Target::Method(MethodKind::Trait { body: false });
IteratorNext, sym::next, next_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, BTreeSet,
BinaryHeap, BinaryHeap,
Borrow, Borrow,
Break,
C, C,
CString, CString,
Center, Center,
Clone, Clone,
Continue,
Copy, Copy,
Count, Count,
Debug, Debug,
@ -326,6 +328,7 @@ symbols! {
box_patterns, box_patterns,
box_syntax, box_syntax,
braced_empty_structs, braced_empty_structs,
branch,
breakpoint, breakpoint,
bridge, bridge,
bswap, bswap,
@ -410,6 +413,7 @@ symbols! {
constructor, constructor,
contents, contents,
context, context,
control_flow_enum,
convert, convert,
copy, copy,
copy_closures, copy_closures,
@ -510,7 +514,6 @@ symbols! {
env, env,
eq, eq,
ermsb_target_feature, ermsb_target_feature,
err,
exact_div, exact_div,
except, except,
exchange_malloc, exchange_malloc,
@ -580,10 +583,10 @@ symbols! {
frem_fast, frem_fast,
from, from,
from_desugaring, from_desugaring,
from_error,
from_generator, from_generator,
from_method, from_method,
from_ok, from_output,
from_residual,
from_size_align_unchecked, from_size_align_unchecked,
from_trait, from_trait,
from_usize, from_usize,
@ -652,7 +655,6 @@ symbols! {
instruction_set, instruction_set,
intel, intel,
into_iter, into_iter,
into_result,
into_trait, into_trait,
intra_doc_pointers, intra_doc_pointers,
intrinsics, intrinsics,
@ -962,6 +964,7 @@ symbols! {
repr_packed, repr_packed,
repr_simd, repr_simd,
repr_transparent, repr_transparent,
residual,
result, result,
result_type, result_type,
rhs, rhs,
@ -1227,7 +1230,7 @@ symbols! {
try_blocks, try_blocks,
try_from_trait, try_from_trait,
try_into_trait, try_into_trait,
try_trait, try_trait_v2,
tt, tt,
tuple, tuple,
tuple_from_req, tuple_from_req,

View file

@ -1,5 +1,5 @@
use crate::iter::{adapters::SourceIter, FusedIterator, InPlaceIterable, TrustedLen}; 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 /// An iterator with a `peek()` that returns an optional reference to the next
/// element. /// element.
@ -130,12 +130,35 @@ where
} }
#[inline] #[inline]
#[cfg(not(bootstrap))]
fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R fn try_rfold<B, F, R>(&mut self, init: B, mut f: F) -> R
where where
Self: Sized, Self: Sized,
F: FnMut(B, Self::Item) -> R, F: FnMut(B, Self::Item) -> R,
R: TryWhereOutputEquals<B>, 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() { match self.peeked.take() {
Some(None) => try { init }, Some(None) => try { init },
Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() { Some(Some(v)) => match self.iter.try_rfold(init, &mut f).into_result() {

View file

@ -2412,6 +2412,36 @@ pub trait Iterator {
/// ``` /// ```
#[inline] #[inline]
#[unstable(feature = "try_find", reason = "new API", issue = "63178")] #[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> fn try_find<F, R>(&mut self, f: F) -> Result<Option<Self::Item>, R::Error>
where where
Self: Sized, Self: Sized,

View file

@ -52,8 +52,10 @@ use crate::{convert, ops};
#[derive(Debug, Clone, Copy, PartialEq)] #[derive(Debug, Clone, Copy, PartialEq)]
pub enum ControlFlow<B, C = ()> { pub enum ControlFlow<B, C = ()> {
/// Move on to the next phase of the operation as normal. /// Move on to the next phase of the operation as normal.
#[cfg_attr(not(bootstrap), lang = "Continue")]
Continue(C), Continue(C),
/// Exit the operation without running subsequent phases. /// Exit the operation without running subsequent phases.
#[cfg_attr(not(bootstrap), lang = "Break")]
Break(B), Break(B),
// Yes, the order of the variants doesn't match the type parameters. // 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>` // 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> { impl<R: ops::TryV1> ControlFlow<R, R::Ok> {
/// Create a `ControlFlow` from any type implementing `Try`. /// Create a `ControlFlow` from any type implementing `Try`.
#[unstable(feature = "control_flow_enum", reason = "new API", issue = "75744")] #[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, ()> { impl<B> ControlFlow<B, ()> {
/// It's frequently the case that there's no value needed with `Continue`, /// 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. /// 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}; pub use self::range::{Bound, RangeBounds, RangeInclusive, RangeToInclusive};
#[unstable(feature = "try_trait", issue = "42327")] #[unstable(feature = "try_trait", issue = "42327")]
#[cfg(bootstrap)]
pub use self::r#try::Try; pub use self::r#try::Try;
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")] #[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")] #[unstable(feature = "try_trait_v2", issue = "84277")]
pub use self::try_trait::FromResidual; 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")] #[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")]
pub use self::try_trait::Try as TryV2; pub use self::try_trait::Try as TryV2;
@ -220,4 +225,9 @@ pub use self::control_flow::ControlFlow;
/// foo::<Option<i32>, i32>(); /// foo::<Option<i32>, i32>();
/// ``` /// ```
#[unstable(feature = "try_trait_transition", reason = "for bootstrap", issue = "none")] #[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>; pub trait TryWhereOutputEquals<T> = TryV1<Ok = T>;

View file

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

View file

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