Actually implement the feature in the compiler
Including all the bootstrapping tweaks in the library.
This commit is contained in:
parent
c10eec3a1c
commit
ca92b5a23a
10 changed files with 144 additions and 51 deletions
|
@ -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(
|
||||
// `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,
|
||||
hir::LangItem::FromFrom,
|
||||
arena_vec![self; err_expr],
|
||||
)
|
||||
};
|
||||
let from_err_expr = self.wrap_in_try_constructor(
|
||||
hir::LangItem::TryFromError,
|
||||
unstable_span,
|
||||
from_expr,
|
||||
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,
|
||||
)
|
||||
}
|
||||
|
|
|
@ -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> {
|
||||
|
|
|
@ -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});
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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>;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue