mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-25 16:24:46 +00:00
Move MatchPair
tree creation to its own module
This makes it easier to see that `MatchPair::new` has only one non-recursive caller, because the recursive callers are all in this module.
This commit is contained in:
parent
00167abb41
commit
ce86b2ae96
245
compiler/rustc_mir_build/src/build/matches/match_pair.rs
Normal file
245
compiler/rustc_mir_build/src/build/matches/match_pair.rs
Normal file
@ -0,0 +1,245 @@
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty::{self, Ty, TypeVisitableExt};
|
||||
|
||||
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||
use crate::build::matches::{FlatPat, MatchPair, TestCase};
|
||||
use crate::build::Builder;
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
fn field_match_pairs<'pat>(
|
||||
&mut self,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
subpatterns: &'pat [FieldPat<'tcx>],
|
||||
) -> Vec<MatchPair<'pat, 'tcx>> {
|
||||
subpatterns
|
||||
.iter()
|
||||
.map(|fieldpat| {
|
||||
let place =
|
||||
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
|
||||
MatchPair::new(place, &fieldpat.pattern, self)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn prefix_slice_suffix<'pat>(
|
||||
&mut self,
|
||||
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
|
||||
place: &PlaceBuilder<'tcx>,
|
||||
prefix: &'pat [Box<Pat<'tcx>>],
|
||||
opt_slice: &'pat Option<Box<Pat<'tcx>>>,
|
||||
suffix: &'pat [Box<Pat<'tcx>>],
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
|
||||
match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
|
||||
ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true),
|
||||
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
|
||||
}
|
||||
} else {
|
||||
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
||||
};
|
||||
|
||||
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
||||
let elem =
|
||||
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
|
||||
MatchPair::new(place.clone_project(elem), subpattern, self)
|
||||
}));
|
||||
|
||||
if let Some(subslice_pat) = opt_slice {
|
||||
let suffix_len = suffix.len() as u64;
|
||||
let subslice = place.clone_project(PlaceElem::Subslice {
|
||||
from: prefix.len() as u64,
|
||||
to: if exact_size { min_length - suffix_len } else { suffix_len },
|
||||
from_end: !exact_size,
|
||||
});
|
||||
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
|
||||
}
|
||||
|
||||
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
|
||||
let end_offset = (idx + 1) as u64;
|
||||
let elem = ProjectionElem::ConstantIndex {
|
||||
offset: if exact_size { min_length - end_offset } else { end_offset },
|
||||
min_length,
|
||||
from_end: !exact_size,
|
||||
};
|
||||
let place = place.clone_project(elem);
|
||||
MatchPair::new(place, subpattern, self)
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
/// Recursively builds a `MatchPair` tree for the given pattern and its
|
||||
/// subpatterns.
|
||||
pub(in crate::build) fn new(
|
||||
mut place_builder: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
) -> MatchPair<'pat, 'tcx> {
|
||||
// Force the place type to the pattern's type.
|
||||
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
|
||||
if let Some(resolved) = place_builder.resolve_upvar(cx) {
|
||||
place_builder = resolved;
|
||||
}
|
||||
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place_builder.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty =
|
||||
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if may_need_cast {
|
||||
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
|
||||
let place = place_builder.try_to_place(cx);
|
||||
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
|
||||
let mut subpairs = Vec::new();
|
||||
let test_case = match pattern.kind {
|
||||
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
|
||||
|
||||
PatKind::Or { ref pats } => TestCase::Or {
|
||||
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
|
||||
},
|
||||
|
||||
PatKind::Range(ref range) => {
|
||||
if range.is_full_range(cx.tcx) == Some(true) {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Range(range)
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Constant { value } => TestCase::Constant { value },
|
||||
|
||||
PatKind::AscribeUserType {
|
||||
ascription: thir::Ascription { ref annotation, variance },
|
||||
ref subpattern,
|
||||
..
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`
|
||||
let ascription = place.map(|source| super::Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source,
|
||||
variance,
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Binding { mode, var, ref subpattern, .. } => {
|
||||
let binding = place.map(|source| super::Binding {
|
||||
span: pattern.span,
|
||||
source,
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
|
||||
if let Some(subpattern) = subpattern.as_ref() {
|
||||
// this is the `x @ P` case; have to keep matching against `P` now
|
||||
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
|
||||
}
|
||||
TestCase::Irrefutable { ascription: None, binding }
|
||||
}
|
||||
|
||||
PatKind::InlineConstant { subpattern: ref pattern, def, .. } => {
|
||||
// Apply a type ascription for the inline constant to the value at `match_pair.place`
|
||||
let ascription = place.map(|source| {
|
||||
let span = pattern.span;
|
||||
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
|
||||
let args = ty::InlineConstArgs::new(
|
||||
cx.tcx,
|
||||
ty::InlineConstArgsParts {
|
||||
parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
|
||||
ty: cx.infcx.next_ty_var(span),
|
||||
},
|
||||
)
|
||||
.args;
|
||||
let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
|
||||
def.to_def_id(),
|
||||
ty::UserArgs { args, user_self_ty: None },
|
||||
));
|
||||
let annotation = ty::CanonicalUserTypeAnnotation {
|
||||
inferred_ty: pattern.ty,
|
||||
span,
|
||||
user_ty: Box::new(user_ty),
|
||||
};
|
||||
super::Ascription { annotation, source, variance: ty::Contravariant }
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place_builder, pattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
|
||||
default_irrefutable()
|
||||
}
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
|
||||
|
||||
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Slice {
|
||||
len: prefix.len() + suffix.len(),
|
||||
variable_length: slice.is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
|
||||
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
|
||||
|
||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||
i == variant_index || {
|
||||
(cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& !v
|
||||
.inhabited_predicate(cx.tcx, adt_def)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply_ignore_module(cx.tcx, cx.param_env)
|
||||
}
|
||||
}) && (adt_def.did().is_local()
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if irrefutable {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Variant { adt_def, variant_index }
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Leaf { ref subpatterns } => {
|
||||
subpairs = cx.field_match_pairs(place_builder, subpatterns);
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx));
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern, mutability } => {
|
||||
// Create a new temporary for each deref pattern.
|
||||
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
|
||||
let temp = cx.temp(
|
||||
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
|
||||
pattern.span,
|
||||
);
|
||||
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
|
||||
TestCase::Deref { temp, mutability }
|
||||
}
|
||||
|
||||
PatKind::Never => TestCase::Never,
|
||||
};
|
||||
|
||||
MatchPair { place, test_case, subpairs, pattern }
|
||||
}
|
||||
}
|
@ -24,6 +24,7 @@ use tracing::{debug, instrument};
|
||||
use util::visit_bindings;
|
||||
|
||||
// helper functions, broken out by category:
|
||||
mod match_pair;
|
||||
mod simplify;
|
||||
mod test;
|
||||
mod util;
|
||||
|
@ -1,78 +1,15 @@
|
||||
use std::marker::PhantomData;
|
||||
|
||||
use crate::build::expr::as_place::{PlaceBase, PlaceBuilder};
|
||||
use crate::build::expr::as_place::PlaceBase;
|
||||
use crate::build::matches::{Binding, Candidate, FlatPat, MatchPair, TestCase};
|
||||
use crate::build::Builder;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_middle::mir::*;
|
||||
use rustc_middle::thir::{self, *};
|
||||
use rustc_middle::ty::TypeVisitableExt;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Span;
|
||||
use tracing::debug;
|
||||
|
||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
pub(crate) fn field_match_pairs<'pat>(
|
||||
&mut self,
|
||||
place: PlaceBuilder<'tcx>,
|
||||
subpatterns: &'pat [FieldPat<'tcx>],
|
||||
) -> Vec<MatchPair<'pat, 'tcx>> {
|
||||
subpatterns
|
||||
.iter()
|
||||
.map(|fieldpat| {
|
||||
let place =
|
||||
place.clone_project(PlaceElem::Field(fieldpat.field, fieldpat.pattern.ty));
|
||||
MatchPair::new(place, &fieldpat.pattern, self)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub(crate) fn prefix_slice_suffix<'pat>(
|
||||
&mut self,
|
||||
match_pairs: &mut Vec<MatchPair<'pat, 'tcx>>,
|
||||
place: &PlaceBuilder<'tcx>,
|
||||
prefix: &'pat [Box<Pat<'tcx>>],
|
||||
opt_slice: &'pat Option<Box<Pat<'tcx>>>,
|
||||
suffix: &'pat [Box<Pat<'tcx>>],
|
||||
) {
|
||||
let tcx = self.tcx;
|
||||
let (min_length, exact_size) = if let Some(place_resolved) = place.try_to_place(self) {
|
||||
match place_resolved.ty(&self.local_decls, tcx).ty.kind() {
|
||||
ty::Array(_, length) => (length.eval_target_usize(tcx, self.param_env), true),
|
||||
_ => ((prefix.len() + suffix.len()).try_into().unwrap(), false),
|
||||
}
|
||||
} else {
|
||||
((prefix.len() + suffix.len()).try_into().unwrap(), false)
|
||||
};
|
||||
|
||||
match_pairs.extend(prefix.iter().enumerate().map(|(idx, subpattern)| {
|
||||
let elem =
|
||||
ProjectionElem::ConstantIndex { offset: idx as u64, min_length, from_end: false };
|
||||
MatchPair::new(place.clone_project(elem), subpattern, self)
|
||||
}));
|
||||
|
||||
if let Some(subslice_pat) = opt_slice {
|
||||
let suffix_len = suffix.len() as u64;
|
||||
let subslice = place.clone_project(PlaceElem::Subslice {
|
||||
from: prefix.len() as u64,
|
||||
to: if exact_size { min_length - suffix_len } else { suffix_len },
|
||||
from_end: !exact_size,
|
||||
});
|
||||
match_pairs.push(MatchPair::new(subslice, subslice_pat, self));
|
||||
}
|
||||
|
||||
match_pairs.extend(suffix.iter().rev().enumerate().map(|(idx, subpattern)| {
|
||||
let end_offset = (idx + 1) as u64;
|
||||
let elem = ProjectionElem::ConstantIndex {
|
||||
offset: if exact_size { min_length - end_offset } else { end_offset },
|
||||
min_length,
|
||||
from_end: !exact_size,
|
||||
};
|
||||
let place = place.clone_project(elem);
|
||||
MatchPair::new(place, subpattern, self)
|
||||
}));
|
||||
}
|
||||
|
||||
/// Creates a false edge to `imaginary_target` and a real edge to
|
||||
/// real_target. If `imaginary_target` is none, or is the same as the real
|
||||
/// target, a Goto is generated instead to simplify the generated MIR.
|
||||
@ -96,181 +33,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'pat, 'tcx> MatchPair<'pat, 'tcx> {
|
||||
/// Recursively builds a `MatchPair` tree for the given pattern and its
|
||||
/// subpatterns.
|
||||
pub(in crate::build) fn new(
|
||||
mut place_builder: PlaceBuilder<'tcx>,
|
||||
pattern: &'pat Pat<'tcx>,
|
||||
cx: &mut Builder<'_, 'tcx>,
|
||||
) -> MatchPair<'pat, 'tcx> {
|
||||
// Force the place type to the pattern's type.
|
||||
// FIXME(oli-obk): can we use this to simplify slice/array pattern hacks?
|
||||
if let Some(resolved) = place_builder.resolve_upvar(cx) {
|
||||
place_builder = resolved;
|
||||
}
|
||||
|
||||
// Only add the OpaqueCast projection if the given place is an opaque type and the
|
||||
// expected type from the pattern is not.
|
||||
let may_need_cast = match place_builder.base() {
|
||||
PlaceBase::Local(local) => {
|
||||
let ty =
|
||||
Place::ty_from(local, place_builder.projection(), &cx.local_decls, cx.tcx).ty;
|
||||
ty != pattern.ty && ty.has_opaque_types()
|
||||
}
|
||||
_ => true,
|
||||
};
|
||||
if may_need_cast {
|
||||
place_builder = place_builder.project(ProjectionElem::OpaqueCast(pattern.ty));
|
||||
}
|
||||
|
||||
let place = place_builder.try_to_place(cx);
|
||||
let default_irrefutable = || TestCase::Irrefutable { binding: None, ascription: None };
|
||||
let mut subpairs = Vec::new();
|
||||
let test_case = match pattern.kind {
|
||||
PatKind::Wild | PatKind::Error(_) => default_irrefutable(),
|
||||
|
||||
PatKind::Or { ref pats } => TestCase::Or {
|
||||
pats: pats.iter().map(|pat| FlatPat::new(place_builder.clone(), pat, cx)).collect(),
|
||||
},
|
||||
|
||||
PatKind::Range(ref range) => {
|
||||
if range.is_full_range(cx.tcx) == Some(true) {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Range(range)
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Constant { value } => TestCase::Constant { value },
|
||||
|
||||
PatKind::AscribeUserType {
|
||||
ascription: thir::Ascription { ref annotation, variance },
|
||||
ref subpattern,
|
||||
..
|
||||
} => {
|
||||
// Apply the type ascription to the value at `match_pair.place`
|
||||
let ascription = place.map(|source| super::Ascription {
|
||||
annotation: annotation.clone(),
|
||||
source,
|
||||
variance,
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Binding { mode, var, ref subpattern, .. } => {
|
||||
let binding = place.map(|source| super::Binding {
|
||||
span: pattern.span,
|
||||
source,
|
||||
var_id: var,
|
||||
binding_mode: mode,
|
||||
});
|
||||
|
||||
if let Some(subpattern) = subpattern.as_ref() {
|
||||
// this is the `x @ P` case; have to keep matching against `P` now
|
||||
subpairs.push(MatchPair::new(place_builder, subpattern, cx));
|
||||
}
|
||||
TestCase::Irrefutable { ascription: None, binding }
|
||||
}
|
||||
|
||||
PatKind::InlineConstant { subpattern: ref pattern, def, .. } => {
|
||||
// Apply a type ascription for the inline constant to the value at `match_pair.place`
|
||||
let ascription = place.map(|source| {
|
||||
let span = pattern.span;
|
||||
let parent_id = cx.tcx.typeck_root_def_id(cx.def_id.to_def_id());
|
||||
let args = ty::InlineConstArgs::new(
|
||||
cx.tcx,
|
||||
ty::InlineConstArgsParts {
|
||||
parent_args: ty::GenericArgs::identity_for_item(cx.tcx, parent_id),
|
||||
ty: cx.infcx.next_ty_var(span),
|
||||
},
|
||||
)
|
||||
.args;
|
||||
let user_ty = cx.infcx.canonicalize_user_type_annotation(ty::UserType::TypeOf(
|
||||
def.to_def_id(),
|
||||
ty::UserArgs { args, user_self_ty: None },
|
||||
));
|
||||
let annotation = ty::CanonicalUserTypeAnnotation {
|
||||
inferred_ty: pattern.ty,
|
||||
span,
|
||||
user_ty: Box::new(user_ty),
|
||||
};
|
||||
super::Ascription { annotation, source, variance: ty::Contravariant }
|
||||
});
|
||||
|
||||
subpairs.push(MatchPair::new(place_builder, pattern, cx));
|
||||
TestCase::Irrefutable { ascription, binding: None }
|
||||
}
|
||||
|
||||
PatKind::Array { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
|
||||
default_irrefutable()
|
||||
}
|
||||
PatKind::Slice { ref prefix, ref slice, ref suffix } => {
|
||||
cx.prefix_slice_suffix(&mut subpairs, &place_builder, prefix, slice, suffix);
|
||||
|
||||
if prefix.is_empty() && slice.is_some() && suffix.is_empty() {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Slice {
|
||||
len: prefix.len() + suffix.len(),
|
||||
variable_length: slice.is_some(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Variant { adt_def, variant_index, args, ref subpatterns } => {
|
||||
let downcast_place = place_builder.downcast(adt_def, variant_index); // `(x as Variant)`
|
||||
subpairs = cx.field_match_pairs(downcast_place, subpatterns);
|
||||
|
||||
let irrefutable = adt_def.variants().iter_enumerated().all(|(i, v)| {
|
||||
i == variant_index || {
|
||||
(cx.tcx.features().exhaustive_patterns
|
||||
|| cx.tcx.features().min_exhaustive_patterns)
|
||||
&& !v
|
||||
.inhabited_predicate(cx.tcx, adt_def)
|
||||
.instantiate(cx.tcx, args)
|
||||
.apply_ignore_module(cx.tcx, cx.param_env)
|
||||
}
|
||||
}) && (adt_def.did().is_local()
|
||||
|| !adt_def.is_variant_list_non_exhaustive());
|
||||
if irrefutable {
|
||||
default_irrefutable()
|
||||
} else {
|
||||
TestCase::Variant { adt_def, variant_index }
|
||||
}
|
||||
}
|
||||
|
||||
PatKind::Leaf { ref subpatterns } => {
|
||||
subpairs = cx.field_match_pairs(place_builder, subpatterns);
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::Deref { ref subpattern } => {
|
||||
subpairs.push(MatchPair::new(place_builder.deref(), subpattern, cx));
|
||||
default_irrefutable()
|
||||
}
|
||||
|
||||
PatKind::DerefPattern { ref subpattern, mutability } => {
|
||||
// Create a new temporary for each deref pattern.
|
||||
// FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls?
|
||||
let temp = cx.temp(
|
||||
Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, subpattern.ty, mutability),
|
||||
pattern.span,
|
||||
);
|
||||
subpairs.push(MatchPair::new(PlaceBuilder::from(temp).deref(), subpattern, cx));
|
||||
TestCase::Deref { temp, mutability }
|
||||
}
|
||||
|
||||
PatKind::Never => TestCase::Never,
|
||||
};
|
||||
|
||||
MatchPair { place, test_case, subpairs, pattern }
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine the set of places that have to be stable across match guards.
|
||||
///
|
||||
/// Returns a list of places that need a fake borrow along with a local to store it.
|
||||
|
Loading…
Reference in New Issue
Block a user