1//! The bulk of the logic for implementing `-Zassumptions-on-binders`
23use derive_where::derive_where;
4use indexmap::IndexSet;
5#[cfg(feature = "nightly")]
6use rustc_data_structures::stable_hash::{StableHash, StableHashCtxt, StableHasher};
7#[cfg(feature = "nightly")]
8use rustc_data_structures::transitive_relation::{TransitiveRelation, TransitiveRelationBuilder};
9use tracing::{debug, instrument};
1011// Workaround for TransitiveRelation being in rustc_data_structures which isn't accessible on stable
12#[cfg(not(feature = "nightly"))]
13#[derive(Default, Clone, Debug)]
14pub struct TransitiveRelation<T>(T);
15#[cfg(not(feature = "nightly"))]
16impl<T> TransitiveRelation<T> {
17pub fn reachable_from(&self, _data: T) -> Vec<T> {
18unreachable!("-Zassumptions-on-binders is not supported for r-a")
19 }
2021pub fn base_edges(&self) -> impl Iterator<Item = (T, T)> {
22unreachable!("-Zassumptions-on-binders is not supported for r-a");
2324#[allow(unreachable_code)]
25[].into_iter()
26 }
27}
28#[derive(Clone, Debug)]
29#[cfg(not(feature = "nightly"))]
30pub struct TransitiveRelationBuilder<T>(T);
31#[cfg(not(feature = "nightly"))]
32impl<T> TransitiveRelationBuilder<T> {
33pub fn freeze(self) -> TransitiveRelation<T> {
34unreachable!("-Zassumptions-on-binders is not supported for r-a")
35 }
3637pub fn add(&mut self, _: T, _: T) {
38unreachable!("-Zassumptions-on-binders is not supported for r-a")
39 }
40}
41#[cfg(not(feature = "nightly"))]
42impl<T> Default for TransitiveRelationBuilder<T> {
43fn default() -> Self {
44unreachable!("-Zassumptions-on-binders is not supported for r-a")
45 }
46}
4748use crate::data_structures::IndexMap;
49use crate::fold::TypeSuperFoldable;
50use crate::inherent::*;
51use crate::relate::{Relate, RelateResult, TypeRelation, VarianceDiagInfo};
52use crate::{
53AliasTy, Binder, BoundRegion, BoundVar, BoundVariableKind, DebruijnIndex, FallibleTypeFolder,
54GenericTypeVisitable, InferCtxtLike, Interner, IsRigid, OutlivesPredicate, Region, RegionKind,
55TyKind, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor, TypingMode, UniverseIndex,
56Variance, VisitorResult, max_universe, set_aliases_to_non_rigid, try_visit,
57walk_visitable_list,
58};
5960#[automatically_derived]
impl<I: Interner> ::core::fmt::Debug for Assumptions<I> where I: Interner {
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
Assumptions {
type_outlives: ref __field_type_outlives,
region_outlives: ref __field_region_outlives,
inverse_region_outlives: ref __field_inverse_region_outlives }
=> {
let mut __builder =
::core::fmt::Formatter::debug_struct(__f, "Assumptions");
::core::fmt::DebugStruct::field(&mut __builder,
"type_outlives", __field_type_outlives);
::core::fmt::DebugStruct::field(&mut __builder,
"region_outlives", __field_region_outlives);
::core::fmt::DebugStruct::field(&mut __builder,
"inverse_region_outlives", __field_inverse_region_outlives);
::core::fmt::DebugStruct::finish(&mut __builder)
}
}
}
}#[derive_where(Clone, Debug; I: Interner)]61pub struct Assumptions<I: Interner> {
62pub type_outlives: Vec<Binder<I, OutlivesPredicate<I, I::Ty>>>,
63pub region_outlives: TransitiveRelation<Region<I>>,
64pub inverse_region_outlives: TransitiveRelation<Region<I>>,
65}
6667impl<I: Interner> Assumptions<I> {
68pub fn empty() -> Self {
69Self {
70 type_outlives: Vec::new(),
71 region_outlives: TransitiveRelationBuilder::default().freeze(),
72 inverse_region_outlives: TransitiveRelationBuilder::default().freeze(),
73 }
74 }
7576pub fn new(
77 type_outlives: Vec<Binder<I, OutlivesPredicate<I, I::Ty>>>,
78 region_outlives: TransitiveRelation<Region<I>>,
79 ) -> Self {
80Self {
81 inverse_region_outlives: {
82let mut builder = TransitiveRelationBuilder::default();
83for (r1, r2) in region_outlives.base_edges() {
84 builder.add(r2, r1);
85 }
86builder.freeze()
87 },
88type_outlives,
89region_outlives,
90 }
91 }
92}
9394#[automatically_derived]
impl<I: Interner> ::core::fmt::Debug for RegionConstraint<I> where I: Interner
{
fn fmt(&self, __f: &mut ::core::fmt::Formatter<'_>)
-> ::core::fmt::Result {
match self {
RegionConstraint::Ambiguity =>
::core::fmt::Formatter::write_str(__f, "Ambiguity"),
RegionConstraint::RegionOutlives(ref __field_0, ref __field_1) =>
{
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "RegionOutlives");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::field(&mut __builder, __field_1);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::AliasTyOutlivesViaEnv(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f,
"AliasTyOutlivesViaEnv");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::PlaceholderTyOutlives(ref __field_0,
ref __field_1) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f,
"PlaceholderTyOutlives");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::field(&mut __builder, __field_1);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::And(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "And");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
RegionConstraint::Or(ref __field_0) => {
let mut __builder =
::core::fmt::Formatter::debug_tuple(__f, "Or");
::core::fmt::DebugTuple::field(&mut __builder, __field_0);
::core::fmt::DebugTuple::finish(&mut __builder)
}
}
}
}#[derive_where(Clone, Hash, PartialEq, Debug; I: Interner)]95#[derive(GenericTypeVisitable)]
96pub enum RegionConstraint<I: Interner> {
97 Ambiguity,
98 RegionOutlives(Region<I>, Region<I>),
99/// Requirement that a (potentially higher ranked) alias outlives some (potentially higher ranked)
100 /// region due to an assumption in the environment. This cannot be satisfied via component outlives
101 /// or item bounds.
102 ///
103 /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
104 /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
105 ///
106 /// We eagerly destructure alias outlives requirements into region outlives requirements corresponding to
107 /// component outlives & item bound outlives rules, leaving only param env candidates.
108AliasTyOutlivesViaEnv(Binder<I, (AliasTy<I>, Region<I>)>),
109/// This is an `I::Ty` for two reasons:
110 /// 1. We need the type visitable impl to be able to `visit_ty` on this so canonicalization
111 /// knows about the placeholder
112 /// 2. When exiting the trait solver there may be placeholder outlives corresponding to params
113 /// from the root universe. These need to be changed from a `Placeholder` to the original
114 /// `Param`.
115 ///
116 /// We cannot eagerly look at assumptions as we are usually working with an incomplete set of assumptions
117 /// and there may wind up being assumptions we can use to prove this when we're in a smaller universe.
118PlaceholderTyOutlives(I::Ty, Region<I>),
119120 And(Box<[RegionConstraint<I>]>),
121 Or(Box<[RegionConstraint<I>]>),
122}
123124// This is not a derived impl because a perfect derive leads to inductive
125// cycle causing the trait to never actually be implemented.
126#[cfg(feature = "nightly")]
127impl<I: Interner> StableHashfor RegionConstraint<I>
128where
129Region<I>: StableHash,
130 I::Ty: StableHash,
131 I::GenericArgs: StableHash,
132 I::TraitAssocTyId: StableHash,
133 I::InherentAssocTyId: StableHash,
134 I::OpaqueTyId: StableHash,
135 I::FreeTyAliasId: StableHash,
136 I::BoundVarKinds: StableHash,
137{
138#[inline]
139fn stable_hash<CTX: StableHashCtxt>(&self, hcx: &mut CTX, hasher: &mut StableHasher) {
140use RegionConstraint::*;
141142 std::mem::discriminant(self).stable_hash(hcx, hasher);
143match self {
144Ambiguity => (),
145RegionOutlives(a, b) => {
146a.stable_hash(hcx, hasher);
147b.stable_hash(hcx, hasher);
148 }
149AliasTyOutlivesViaEnv(outlives) => {
150outlives.stable_hash(hcx, hasher);
151 }
152PlaceholderTyOutlives(a, b) => {
153a.stable_hash(hcx, hasher);
154b.stable_hash(hcx, hasher);
155 }
156And(and) => {
157for a in and.iter() {
158 a.stable_hash(hcx, hasher);
159 }
160 }
161Or(or) => {
162for a in or.iter() {
163 a.stable_hash(hcx, hasher);
164 }
165 }
166 }
167 }
168}
169170impl<I: Interner> TypeFoldable<I> for RegionConstraint<I> {
171fn try_fold_with<F: FallibleTypeFolder<I>>(self, f: &mut F) -> Result<Self, F::Error> {
172use RegionConstraint::*;
173Ok(match self {
174Ambiguity => self,
175RegionOutlives(a, b) => RegionOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?),
176AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.try_fold_with(f)?),
177PlaceholderTyOutlives(a, b) => {
178PlaceholderTyOutlives(a.try_fold_with(f)?, b.try_fold_with(f)?)
179 }
180And(and) => {
181let mut new_and = Vec::new();
182for a in and {
183 new_and.push(a.try_fold_with(f)?);
184 }
185And(new_and.into_boxed_slice())
186 }
187Or(or) => {
188let mut new_or = Vec::new();
189for a in or {
190 new_or.push(a.try_fold_with(f)?);
191 }
192Or(new_or.into_boxed_slice())
193 }
194 })
195 }
196197fn fold_with<F: TypeFolder<I>>(self, f: &mut F) -> Self {
198use RegionConstraint::*;
199match self {
200Ambiguity => self,
201RegionOutlives(a, b) => RegionOutlives(a.fold_with(f), b.fold_with(f)),
202AliasTyOutlivesViaEnv(outlives) => AliasTyOutlivesViaEnv(outlives.fold_with(f)),
203PlaceholderTyOutlives(a, b) => PlaceholderTyOutlives(a.fold_with(f), b.fold_with(f)),
204And(and) => {
205let mut new_and = Vec::new();
206for a in and {
207 new_and.push(a.fold_with(f));
208 }
209And(new_and.into_boxed_slice())
210 }
211Or(or) => {
212let mut new_or = Vec::new();
213for a in or {
214 new_or.push(a.fold_with(f));
215 }
216Or(new_or.into_boxed_slice())
217 }
218 }
219 }
220}
221222impl<I: Interner> TypeVisitable<I> for RegionConstraint<I> {
223fn visit_with<F: TypeVisitor<I>>(&self, f: &mut F) -> F::Result {
224use RegionConstraint::*;
225226match self {
227Ambiguity => (),
228RegionOutlives(a, b) => {
229match ::rustc_ast_ir::visit::VisitorResult::branch(a.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};try_visit!(a.visit_with(f));
230match ::rustc_ast_ir::visit::VisitorResult::branch(b.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};try_visit!(b.visit_with(f));
231 }
232AliasTyOutlivesViaEnv(outlives) => {
233match ::rustc_ast_ir::visit::VisitorResult::branch(outlives.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};try_visit!(outlives.visit_with(f));
234 }
235PlaceholderTyOutlives(a, b) => {
236match ::rustc_ast_ir::visit::VisitorResult::branch(a.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};try_visit!(a.visit_with(f));
237match ::rustc_ast_ir::visit::VisitorResult::branch(b.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};try_visit!(b.visit_with(f));
238 }
239And(and) => {
240for elem in and {
match ::rustc_ast_ir::visit::VisitorResult::branch(elem.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};
};walk_visitable_list!(f, and);
241 }
242Or(or) => {
243for elem in or {
match ::rustc_ast_ir::visit::VisitorResult::branch(elem.visit_with(f)) {
core::ops::ControlFlow::Continue(()) =>
(),
#[allow(unreachable_code)]
core::ops::ControlFlow::Break(r) => {
return ::rustc_ast_ir::visit::VisitorResult::from_residual(r);
}
};
};walk_visitable_list!(f, or);
244 }
245 };
246247 F::Result::output()
248 }
249}
250251impl<I: Interner> Defaultfor RegionConstraint<I> {
252fn default() -> Self {
253Self::new_true()
254 }
255}
256257impl<I: Interner> RegionConstraint<I> {
258pub fn new_true() -> Self {
259 RegionConstraint::And(Box::new([]))
260 }
261262pub fn is_true(&self) -> bool {
263match self {
264Self::And(and) => and.is_empty(),
265_ => false,
266 }
267 }
268269pub fn new_false() -> Self {
270 RegionConstraint::Or(Box::new([]))
271 }
272273pub fn is_false(&self) -> bool {
274match self {
275Self::Or(or) => or.is_empty(),
276_ => false,
277 }
278 }
279280pub fn is_or(&self) -> bool {
281#[allow(non_exhaustive_omitted_patterns)] match self {
Self::Or(_) => true,
_ => false,
}matches!(self, Self::Or(_))282 }
283284pub fn unwrap_or(self) -> Box<[RegionConstraint<I>]> {
285match self {
286Self::Or(ors) => ors,
287_ => {
::core::panicking::panic_fmt(format_args!("`unwrap_or` on non-Or: {0:?}",
self));
}panic!("`unwrap_or` on non-Or: {self:?}"),
288 }
289 }
290291pub fn unwrap_and(self) -> Box<[RegionConstraint<I>]> {
292match self {
293Self::And(ands) => ands,
294_ => {
::core::panicking::panic_fmt(format_args!("`unwrap_and` on non-And: {0:?}",
self));
}panic!("`unwrap_and` on non-And: {self:?}"),
295 }
296 }
297298pub fn is_and(&self) -> bool {
299#[allow(non_exhaustive_omitted_patterns)] match self {
Self::And(_) => true,
_ => false,
}matches!(self, Self::And(_))300 }
301302pub fn is_ambig(&self) -> bool {
303#[allow(non_exhaustive_omitted_patterns)] match self {
Self::Ambiguity => true,
_ => false,
}matches!(self, Self::Ambiguity)304 }
305306pub fn and(self, other: RegionConstraint<I>) -> RegionConstraint<I> {
307use RegionConstraint::*;
308309match (self, other) {
310 (And(a_ands), And(b_ands)) => And(a_ands311 .into_iter()
312 .chain(b_ands.into_iter())
313 .collect::<Vec<_>>()
314 .into_boxed_slice()),
315 (And(ands), other) | (other, And(ands)) => {
316And(ands.into_iter().chain([other]).collect::<Vec<_>>().into_boxed_slice())
317 }
318 (this, other) => And(Box::new([this, other])),
319 }
320 }
321322/// Converts the region constraint into an ORs of ANDs of "leaf" constraints. Where
323 /// a leaf constraint is a non-or/and constraint.
324x;#[instrument(level = "debug", ret)]325pub fn canonical_form(self) -> Self {
326use RegionConstraint::*;
327328fn permutations<I: Interner>(
329 ors: &[Vec<RegionConstraint<I>>],
330 ) -> Vec<Vec<RegionConstraint<I>>> {
331match ors {
332 [] => vec![vec![]],
333 [or1] => {
334let mut choices = vec![];
335for choice in or1 {
336 choices.push(vec![choice.clone()]);
337 }
338 choices
339 }
340 [or1, rest_ors @ ..] => {
341let mut choices = vec![];
342for choice in or1 {
343 choices.extend(permutations(rest_ors).into_iter().map(|mut and| {
344 and.push(choice.clone());
345 and
346 }));
347 }
348 choices
349 }
350 }
351 }
352353let canonical = match self {
354 And(ands) => {
355// AND of OR of AND of LEAFs
356 //
357 // We can turn `AND of OR of X` into `OR of AND of X` by enumerating every set of choices
358 // for the list of ORs. For example if we have `AND ( OR(A, B), OR(C, D) )` we can convert this into
359 // `OR ( AND (A, C), AND (A, D), AND (B, C), AND (B, D ))`
360 //
361 // if A/B/C/D are all in canonical forms then we wind up with an `OR of AND of AND of LEAFs` which
362 // is trivially canonicalizeable by flattening the multiple layers of AND into one.
363let ors = ands
364 .into_iter()
365 .map(|c| c.canonical_form().unwrap_or().to_vec())
366 .collect::<Vec<_>>();
367debug!(?ors);
368let or_permutations = permutations(&ors);
369debug!(?or_permutations);
370371 Or(or_permutations
372 .into_iter()
373 .map(|c| {
374 And(c
375 .into_iter()
376 .flat_map(|c2| c2.unwrap_and().into_iter())
377 .collect::<Vec<_>>()
378 .into_boxed_slice())
379 })
380 .collect::<Vec<_>>()
381 .into_boxed_slice())
382 }
383 Or(ors) => {
384// OR of OR of AND of LEAFs
385 //
386 // trivially canonicalizeable by concatenating all of the ORs into one big OR
387Or(ors
388 .into_iter()
389 .flat_map(|c| c.canonical_form().unwrap_or().into_iter())
390 .collect::<Vec<_>>()
391 .into_boxed_slice())
392 }
393_ => Or(Box::new([And(Box::new([self]))])),
394 };
395396assert!(
397 canonical.is_canonical_form(),
398"non canonical form region constraint: {:?}",
399 canonical
400 );
401 canonical
402 }
403404fn is_leaf_constraint(&self) -> bool {
405use RegionConstraint::*;
406match self {
407Ambiguity408 | RegionOutlives(..)
409 | AliasTyOutlivesViaEnv(..)
410 | PlaceholderTyOutlives(..) => true,
411And(..) | Or(..) => false,
412 }
413 }
414415fn is_canonical_and(&self) -> bool {
416if let Self::And(ands) = self { ands.iter().all(|c| c.is_leaf_constraint()) } else { false }
417 }
418419pub fn is_canonical_form(&self) -> bool {
420if let Self::Or(ors) = self { ors.iter().all(|c| c.is_canonical_and()) } else { false }
421 }
422}
423424/// Takes any constraints involving placeholders from the current universe and eagerly checks them.
425/// This can be done a few ways:
426/// - There's an assumption on the binder introducing the placeholder which means the constraint is satisfied (true)
427/// - There's assumptions on the binder introducing the placeholder which allow us to rewrite the constraint in
428/// terms of lower universe variables. For example given `for<'a> where('b: 'a) { prove(T: '!a_u1) }` we can
429/// convert this constraint to `T: 'b` which no longer references anything from `u1`.
430/// - There are no relevant assumptions so we can neither rewrite the constraint nor consider it satisfied (false)
431/// - We failed to compute the full set of assumptions when entering the binder corresponding to `u`. (ambiguity)
432///
433/// After handling all of the region constraints in `u` we then evaluate the entire constraint as much as possible,
434/// propagating true/false/ambiguity as close to the root of the constraint as we can. The returned constraint should
435/// be checked for whether it is true/false/ambiguous as that should affect the result of whatever operation required
436/// entering the binder corresponding to `u`.
437x;#[instrument(level = "debug", skip(infcx), ret)]438pub fn eagerly_handle_placeholders_in_universe<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
439 infcx: &Infcx,
440 constraint: RegionConstraint<I>,
441 u: UniverseIndex,
442) -> RegionConstraint<I> {
443use RegionConstraint::*;
444445let assumptions = infcx.get_placeholder_assumptions(u);
446447// 1. rewrite type outlives constraints involving things from `u` into either region constraints
448 // involving things from `u` or type outlives constraints not involving things from `u`
449 //
450 // IOW, we only want to encounter things from `u` as part of region out lives constraints.
451let constraint = rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
452 infcx,
453 constraint,
454 u,
455&assumptions,
456 );
457458// 2. rewrite the constraint into a canonical ORs of ANDs form
459let constraint = constraint.canonical_form();
460461// 3. compute transitive region outlives and get a new set of region outlives constraints by
462 // looking for every region which either a placeholder_u flows into it, or it flows into
463 // the placeholder.
464 //
465 // do this for each element in the top level OR
466let constraint = Or(constraint
467 .unwrap_or()
468 .into_iter()
469 .map(|c| {
470let and =
471 And(compute_new_region_constraints(infcx, &c.unwrap_and(), u).into_boxed_slice());
472473// 4. rewrite region outlives constraints (potentially to false/true)
474pull_region_outlives_constraints_out_of_universe(infcx, and, u, &assumptions)
475 })
476 .collect::<Vec<_>>()
477 .into_boxed_slice());
478479// 5. actually evaluate the constraint to eagerly error on false
480evaluate_solver_constraint(&constraint)
481}
482483/// Filter our region constraints to not include constraints between region variables from `u` and
484/// other regions as those are always satisfied. This requires some care to handle correctly for example:
485/// `'!a_u1: '?x_u1: '!b_u1` should result in us requiring `'!a_u1: '!b_u1` rather than dropping the two
486/// constraints entirely.
487///
488/// The only constraints involving things from `u` should be region outlives constraints at this point. Type
489/// outlives constraints should have been handled already either by destructuring into region outlives or by
490/// being rewritten in terms of smaller universe variables.
491x;#[instrument(level = "debug", skip(infcx), ret)]492fn compute_new_region_constraints<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
493 infcx: &Infcx,
494 constraints: &[RegionConstraint<I>],
495 u: UniverseIndex,
496) -> Vec<RegionConstraint<I>> {
497use RegionConstraint::*;
498499let mut new_constraints = vec![];
500501let mut region_flows_builder = TransitiveRelationBuilder::default();
502let mut regions = IndexSet::new();
503for c in constraints {
504match c {
505 And(..) | Or(..) => unreachable!(),
506 Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
507 new_constraints.push(c.clone())
508 }
509 RegionOutlives(r1, r2) => {
510 regions.insert(r1);
511 regions.insert(r2);
512 region_flows_builder.add(r2, r1);
513 }
514 }
515 }
516517let region_flow = region_flows_builder.freeze();
518for r in regions.into_iter() {
519for ub in region_flow.reachable_from(r) {
520// we want to retain any region constraints between two "placeholder-likes" where for our
521 // purposes a placeholder-like is either a placeholder or variable in a lower universe
522let is_placeholder_like = |r: Region<I>| match r.kind() {
523 RegionKind::ReLateParam(..)
524 | RegionKind::ReEarlyParam(..)
525 | RegionKind::RePlaceholder(..)
526 | RegionKind::ReStatic => true,
527 RegionKind::ReVar(..) => max_universe(infcx, r) < u,
528 RegionKind::ReError(..) => false,
529 RegionKind::ReErased | RegionKind::ReBound(..) => unreachable!(),
530 };
531532if is_placeholder_like(*r) && is_placeholder_like(*ub) {
533 new_constraints.push(RegionOutlives(*ub, *r));
534 }
535 }
536 }
537538 new_constraints
539}
540541/// Evaluate ANDs and ORs to true/false/ambiguous based on whether their arguments are true/false/ambiguous
542x;#[instrument(level = "debug", ret)]543pub fn evaluate_solver_constraint<I: Interner>(
544 constraint: &RegionConstraint<I>,
545) -> RegionConstraint<I> {
546use RegionConstraint::*;
547match constraint {
548 Ambiguity | RegionOutlives(..) | AliasTyOutlivesViaEnv(..) | PlaceholderTyOutlives(..) => {
549 constraint.clone()
550 }
551 And(and) => {
552let mut and_constraints = Vec::new();
553let mut is_ambiguous_constraint = false;
554for c in and.iter() {
555let evaluated_constraint = evaluate_solver_constraint(c);
556if evaluated_constraint.is_true() {
557// - do nothing
558} else if evaluated_constraint.is_false() {
559return RegionConstraint::new_false();
560 } else if evaluated_constraint.is_ambig() {
561 is_ambiguous_constraint = true;
562 } else {
563 and_constraints.push(evaluated_constraint);
564 }
565 }
566567if is_ambiguous_constraint {
568 RegionConstraint::Ambiguity
569 } else {
570 RegionConstraint::And(and_constraints.into_boxed_slice())
571 }
572 }
573 Or(or) => {
574let mut or_constraints = Vec::new();
575let mut is_ambiguous_constraint = false;
576for c in or.iter() {
577let evaluated_constraint = evaluate_solver_constraint(c);
578if evaluated_constraint.is_false() {
579// do nothing
580} else if evaluated_constraint.is_true() {
581return RegionConstraint::new_true();
582 } else if evaluated_constraint.is_ambig() {
583 is_ambiguous_constraint = true;
584 } else {
585 or_constraints.push(evaluated_constraint);
586 }
587 }
588589if is_ambiguous_constraint {
590 RegionConstraint::Ambiguity
591 } else {
592 RegionConstraint::Or(or_constraints.into_boxed_slice())
593 }
594 }
595 }
596}
597598/// Handles converting region outlives constraints involving placeholders from `u` into OR constraints
599/// involving regions from smaller universes with known relationships to the placeholder. For example:
600/// ```ignore (not rust)
601/// for<'a, 'b> where(
602/// 'c: 'b, 'd: 'b,
603/// 'a: 'e, 'a: 'f,
604/// ) {
605/// 'a_u1: 'b_u1
606/// }
607/// ```
608/// will get converted to:
609/// ```ignore (not rust)
610/// OR(
611/// 'e: 'c,
612/// 'e: 'd,
613/// 'f: 'c,
614/// 'f: 'd,
615/// )
616/// ```
617/// if we are handling constraints in `u1`.
618x;#[instrument(level = "debug", skip(infcx), ret)]619fn pull_region_outlives_constraints_out_of_universe<
620 Infcx: InferCtxtLike<Interner = I>,
621 I: Interner,
622>(
623 infcx: &Infcx,
624 constraint: RegionConstraint<I>,
625 u: UniverseIndex,
626 assumptions: &Option<Assumptions<I>>,
627) -> RegionConstraint<I> {
628assert!(max_universe(infcx, constraint.clone()) <= u);
629630// FIXME(-Zassumptions-on-binders): we don't lower universes of region variables when exiting `u`
631 // this seems dubious/potentially wrong? we can't just blindly do this though as if we had something
632 // like `!T_u -> ?x_u -> !U_u` then lowering `?x` to `u-1` when exiting `u` would be wrong.
633 //
634 // I'm not even sure this would be necessary given we filter out region constraints involving regions#
635 // from the current universe and only retain those between placeholders.
636637use RegionConstraint::*;
638match constraint {
639 Ambiguity | PlaceholderTyOutlives(..) | AliasTyOutlivesViaEnv(..) => {
640assert!(max_universe(infcx, constraint.clone()) < u);
641 constraint
642 }
643 RegionOutlives(region_1, region_2) => {
644let region_1_u = max_universe(infcx, region_1);
645let region_2_u = max_universe(infcx, region_2);
646647if region_1_u != u && region_2_u != u {
648return constraint;
649 }
650651let assumptions = match assumptions {
652Some(assumptions) => assumptions,
653None => return RegionConstraint::Ambiguity,
654 };
655656let mut candidates = vec![];
657for ub in
658regions_outlived_by(region_1, assumptions).filter(|r| max_universe(infcx, *r) < u)
659 {
660// FIXME(-Zassumptions-on-binders): if `region_2` is in a smaller universe there'll be both
661 // `'region_2` and `'static` as lower bounds which seems... unfortunate and may cause us to
662 // add a bunch of duplicate `'ub: 'static` candidates the more binders we leave.
663for lb in regions_outliving(region_2, assumptions, infcx.cx())
664 .filter(|r| max_universe(infcx, *r) < u)
665 {
666// As long as any region outlived by `region_1` outlives any region region which
667 // `region_2` outlives, we know that `region_1: region_2` holds. In other words,
668 // there exists some set of 4 regions for which `'r1: 'i1` `'i1: 'i2` `'i2: 'r2`
669candidates.push(RegionOutlives(ub, lb));
670 }
671 }
672673 RegionConstraint::Or(candidates.into_boxed_slice())
674 }
675 And(constraints) => And(constraints
676 .into_iter()
677 .map(|constraint| {
678 pull_region_outlives_constraints_out_of_universe(infcx, constraint, u, assumptions)
679 })
680 .collect()),
681 Or(_) => unreachable!(),
682 }
683}
684685/// Converts type outlives constraints into region outlives constraints. This assumes the *complete* set of
686/// assumptions are known. This should not be called until the end of type checking.
687///
688/// The returned region constraint will not have *any* PlaceholderTyOutlives or AliasTyOutlivesViaEnv constraints.
689pub fn destructure_type_outlives_constraints_in_root<
690 Infcx: InferCtxtLike<Interner = I>,
691 I: Interner,
692>(
693 infcx: &Infcx,
694 constraint: RegionConstraint<I>,
695 assumptions: &Assumptions<I>,
696) -> RegionConstraint<I> {
697use RegionConstraint::*;
698699match constraint {
700Ambiguity | RegionOutlives(..) => constraint,
701PlaceholderTyOutlives(ty, r) => {
702Or(regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
703 .map(move |assumption_r| RegionOutlives(assumption_r, r))
704 .collect::<Vec<_>>()
705 .into_boxed_slice())
706 }
707AliasTyOutlivesViaEnv(bound_outlives) => {
708alias_outlives_candidates_from_assumptions(infcx, bound_outlives, assumptions)
709 }
710And(constraints) => And(constraints711 .into_iter()
712 .map(|constraint| {
713destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
714 })
715 .collect()),
716Or(constraints) => Or(constraints717 .into_iter()
718 .map(|constraint| {
719destructure_type_outlives_constraints_in_root(infcx, constraint, assumptions)
720 })
721 .collect()),
722 }
723}
724725/// Converts type outlives constraints into either region outlives constraints, or type outlives
726/// constraints which do not contain anything from `u`.
727///
728/// This only works off assumptions associated with the binder corresponding to `u` both for
729/// perf reasons and because the full set of region assumptions is not known during type checking
730/// due to closure signature inference.
731///
732/// This only really causes problems for higher-ranked outlives assumptions, for example if we have
733/// `where for<'a> <T as Trait<'a>>::Assoc: 'b` then we can't use that to prove `<T as Trait<'!c>>::Assoc: 'b`
734/// until we are in the root context. See comments inside this function for more detail.
735x;#[instrument(level = "debug", skip(infcx), ret)]736fn rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling<
737 Infcx: InferCtxtLike<Interner = I>,
738 I: Interner,
739>(
740 infcx: &Infcx,
741 constraint: RegionConstraint<I>,
742 u: UniverseIndex,
743 assumptions: &Option<Assumptions<I>>,
744) -> RegionConstraint<I> {
745assert!(
746 max_universe(infcx, constraint.clone()) <= u,
747"constraint {:?} contains terms from a larger universe than {:?}",
748 constraint.clone(),
749 u
750 );
751752use RegionConstraint::*;
753match constraint {
754 Ambiguity | RegionOutlives(..) => constraint,
755 PlaceholderTyOutlives(ty, region) => {
756let ty_u = max_universe(infcx, ty);
757let region_u = max_universe(infcx, region);
758759if region_u != u && ty_u != u {
760return constraint;
761 }
762763let assumptions = match assumptions {
764Some(assumptions) => assumptions,
765None => return Ambiguity,
766 };
767768let mut candidates = vec![];
769770// There could be `!T: 'region` assumptions in the env even if `!T` is in a
771 // smaller universe
772candidates.extend(
773 regions_outlived_by_placeholder(ty, assumptions, infcx.cx())
774 .map(move |assumption_r| RegionOutlives(assumption_r, region)),
775 );
776777// We can express `!T: 'region` as `!T: 'r` where `'r: 'region`. This is only necessary
778 // if the placeholder type is in a smaller universe as otherwise we know all regions which
779 // the placeholder outlives and can just destructure into an OR of RegionOutlives.
780if region_u == u && ty_u < u {
781 candidates.extend(
782 regions_outliving::<I>(region, assumptions, infcx.cx())
783 .filter(|r| max_universe(infcx, *r) < u)
784 .map(|r| PlaceholderTyOutlives(ty, r)),
785 );
786 }
787788 Or(candidates.into_boxed_slice())
789 }
790 AliasTyOutlivesViaEnv(bound_outlives) => {
791let mut candidates = Vec::new();
792793// given there can be higher ranked assumptions, e.g. `for<'a> <T as Trait<'a>>::Assoc: 'c`, that
794 // means that it's actually *always* possible for an alias outlive to be satisfied in the root universe
795 // which means there should *always* be atleast two candidates when destructuring alias outlives. The
796 // two candidates being component outlives and then a higher ranked alias outlives.
797 //
798 // we dont care about this for region outlives as `for<'a> 'a: 'b` can't exist as we don't elaborate
799 // higher ranked type outlives assumptions into higher ranked region outlives assumptions. similarly,
800 // we don't care about `for<'a> Foo<'a>: 'b` as we always destructure adts into their components and if
801 // we dont equivalently elaborate the assumption into assumptions on the adt's components we just drop the
802 // assumptions
803 //
804 // so actually only `for<'a, 'b> Alias<'a>: 'b` and `for<'a> T: 'a` are assumptions we actually need to
805 // handle.
806 //
807 // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
808if max_universe(infcx, bound_outlives) == u {
809let mut replacer = PlaceholderReplacer {
810 cx: infcx.cx(),
811 existing_var_count: bound_outlives.bound_vars().len(),
812 bound_vars: IndexMap::default(),
813 universe: u,
814 current_index: DebruijnIndex::ZERO,
815 };
816let escaping_outlives = bound_outlives.skip_binder().fold_with(&mut replacer);
817let bound_vars = bound_outlives.bound_vars().iter().chain(
818 core::mem::take(&mut replacer.bound_vars)
819 .into_iter()
820 .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
821 );
822let bound_outlives = Binder::bind_with_vars(
823 escaping_outlives,
824 I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
825 );
826let candidate = RegionConstraint::AliasTyOutlivesViaEnv(bound_outlives);
827if max_universe(infcx, candidate.clone()) < u {
828 candidates.push(candidate);
829 } else {
830// `PlaceholderReplacer` only folds regions. A non-lifetime binder can leave
831 // a placeholder type in `u`, so this type-outlives constraint cannot be
832 // handled by the region-outlives-only eager placeholder machinery.
833candidates.push(Ambiguity);
834 }
835 }
836837let assumptions = match assumptions {
838Some(assumptions) => assumptions,
839None => {
840 candidates.push(Ambiguity);
841return Or(candidates.into_boxed_slice());
842 }
843 };
844845// Actually look at the assumptions and matching our higher ranked alias outlives goal
846 // against potentially higher ranked type outlives assumptions.
847candidates.push(alias_outlives_candidates_from_assumptions(
848 infcx,
849 bound_outlives,
850 assumptions,
851 ));
852853// we can rewrite `Alias_u1: 'u2` into `Or(Alias_u1: 'u1)`
854 // given a list of regions which outlive `'u2`
855 //
856 // we don't care about this when rewriting in the root universe as we know the complete set of assumptions
857let (escaping_alias, escaping_r) = bound_outlives.skip_binder();
858if max_universe(infcx, escaping_r) == u {
859let mut replacer = PlaceholderReplacer {
860 cx: infcx.cx(),
861 existing_var_count: bound_outlives.bound_vars().len(),
862 bound_vars: IndexMap::default(),
863 universe: u,
864 current_index: DebruijnIndex::ZERO,
865 };
866let escaping_alias = escaping_alias.fold_with(&mut replacer);
867let bound_vars = bound_outlives.bound_vars().iter().chain(
868 core::mem::take(&mut replacer.bound_vars)
869 .into_iter()
870 .map(|(_, bound_region)| BoundVariableKind::Region(bound_region.kind)),
871 );
872let bound_alias = Binder::bind_with_vars(
873 escaping_alias,
874 I::BoundVarKinds::from_vars(infcx.cx(), bound_vars),
875 );
876877// while we did skip the binder, bound vars aren't in any universe so
878 // this can't be an escaping bound var
879for r2 in regions_outliving(escaping_r, assumptions, infcx.cx())
880 .filter(|r2| max_universe(infcx, *r2) < u)
881 {
882let candidate =
883 AliasTyOutlivesViaEnv(bound_alias.map_bound(|alias| (alias, r2)));
884if max_universe(infcx, candidate.clone()) < u {
885 candidates.push(candidate);
886 } else {
887 candidates.push(Ambiguity);
888 }
889 }
890 }
891892// I'm not convinced our handling here is *complete* so for now
893 // let's be conservative and not let alias outlives' cause NoSolution
894 // in coherence
895match infcx.typing_mode_raw() {
896 TypingMode::Coherence => candidates.push(RegionConstraint::Ambiguity),
897 TypingMode::Typeck { .. }
898 | TypingMode::ErasedNotCoherence { .. }
899 | TypingMode::PostTypeckUntilBorrowck { .. }
900 | TypingMode::PostBorrowck { .. }
901 | TypingMode::Reflection
902 | TypingMode::PostAnalysis
903 | TypingMode::Codegen => (),
904 };
905906 RegionConstraint::Or(candidates.into_boxed_slice())
907 }
908 And(constraints) => And(constraints
909 .into_iter()
910 .map(|constraint| {
911 rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
912 infcx,
913 constraint,
914 u,
915 assumptions,
916 )
917 })
918 .collect()),
919 Or(constraints) => Or(constraints
920 .into_iter()
921 .map(|constraint| {
922 rewrite_type_outlives_constraints_in_universe_for_eager_placeholder_handling(
923 infcx,
924 constraint,
925 u,
926 assumptions,
927 )
928 })
929 .collect()),
930 }
931}
932933/// Returns all regions `r2` for which `r: r2` is known to hold in
934/// the universe associated with `assumptions`
935pub fn regions_outlived_by<I: Interner>(
936 r: Region<I>,
937 assumptions: &Assumptions<I>,
938) -> impl Iterator<Item = Region<I>> {
939// FIXME(-Zassumptions-on-binders): do we need to be adding the reflexive edge here?
940assumptions.region_outlives.reachable_from(r).into_iter().chain([r])
941}
942943/// Returns all regions `r2` for which `r2: r` is known to hold in
944/// the universe associated with `assumptions`
945pub fn regions_outliving<I: Interner>(
946 r: Region<I>,
947 assumptions: &Assumptions<I>,
948 cx: I,
949) -> impl Iterator<Item = Region<I>> {
950assumptions951 .inverse_region_outlives
952 .reachable_from(r)
953 .into_iter()
954// FIXME(-Zassumptions-on-binders): 'static may have been an input region canonicalized to something else is that important?
955 // FIXME(-Zassumptions-on-binders): do we need to adding the reflexive edge here?
956.chain([r, Region::new_static(cx)])
957}
958959/// Returns all regions `r` for which `!t: r` is known to hold in
960/// the universe associated with `assumptions`
961pub fn regions_outlived_by_placeholder<I: Interner>(
962 t: I::Ty,
963 assumptions: &Assumptions<I>,
964 cx: I,
965) -> impl Iterator<Item = Region<I>> {
966match t.kind() {
967 TyKind::Placeholder(..) | TyKind::Param(..) => (),
968_ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("non-placeholder in `regions_outlived_by_placeholder`: {0:?}",
t)));
}unreachable!("non-placeholder in `regions_outlived_by_placeholder`: {t:?}"),
969 }
970971assumptions.type_outlives.iter().flat_map(move |binder| match binder.no_bound_vars() {
972Some(OutlivesPredicate(ty, r)) => (ty == t).then_some(r),
973None => Some(Region::new_static(cx)),
974 })
975}
976977pub struct PlaceholderReplacer<I: Interner> {
978 cx: I,
979 existing_var_count: usize,
980 bound_vars: IndexMap<BoundVar, BoundRegion<I>>,
981 universe: UniverseIndex,
982 current_index: DebruijnIndex,
983}
984985impl<I: Interner> TypeFolder<I> for PlaceholderReplacer<I> {
986fn cx(&self) -> I {
987self.cx
988 }
989990fn fold_region(&mut self, r: Region<I>) -> Region<I> {
991match r.kind() {
992 RegionKind::RePlaceholder(p) if p.universe == self.universe => {
993let bound_vars_len = self.bound_vars.len();
994let mapped_var = self.bound_vars.entry(p.bound.var).or_insert(BoundRegion {
995 var: BoundVar::from_usize(self.existing_var_count + bound_vars_len),
996 kind: p.bound.kind,
997 });
998Region::new_bound(self.cx, self.current_index, *mapped_var)
999 }
1000// FIXME(-Zassumptions-on-binders): We should be handling region variables here somehow
1001_ => r,
1002 }
1003 }
10041005fn fold_binder<T: TypeFoldable<I>>(&mut self, b: Binder<I, T>) -> Binder<I, T> {
1006self.current_index.shift_in(1);
1007let b = b.super_fold_with(self);
1008self.current_index.shift_out(1);
1009b1010 }
1011}
10121013/// Converts an `AliasTyOutlivesViaEnv` constraint into an OR of region outlives constraints by
1014/// matching the alias against any `Alias: 'a` assumptions. This is somewhat tricky as we have a
1015/// potentially higher ranked alias being equated with a potentially higher ranked assumption and
1016/// we don't handle it correctly right now (though it is a somewhat reasonable halfway step).
1017x;#[instrument(level = "debug", skip(infcx), ret)]1018fn alias_outlives_candidates_from_assumptions<Infcx: InferCtxtLike<Interner = I>, I: Interner>(
1019 infcx: &Infcx,
1020 bound_outlives: Binder<I, (AliasTy<I>, Region<I>)>,
1021 assumptions: &Assumptions<I>,
1022) -> RegionConstraint<I> {
1023let mut candidates = Vec::new();
10241025let prev_universe = infcx.universe();
10261027 infcx.enter_forall_with_empty_assumptions(bound_outlives, |(alias, r)| {
1028for bound_type_outlives in assumptions.type_outlives.iter() {
1029let OutlivesPredicate(alias2, r2) =
1030 infcx.instantiate_binder_with_infer(*bound_type_outlives);
10311032let mut relation = HigherRankedAliasMatcher {
1033 infcx,
1034 region_constraints: vec![RegionConstraint::RegionOutlives(r2, r)],
1035 };
10361037// FIXME(#155345): Both sides should be rigid in the future.
1038 // Currently we can't guarantee that.
1039if let Ok(_) = relation.relate(
1040 alias.to_ty(infcx.cx(), IsRigid::No),
1041 set_aliases_to_non_rigid(infcx.cx(), alias2).skip_norm_wip(),
1042 ) {
1043 candidates
1044 .push(RegionConstraint::And(relation.region_constraints.into_boxed_slice()));
1045 }
1046 }
1047 });
10481049let constraint = RegionConstraint::Or(candidates.into_boxed_slice());
10501051let largest_universe = infcx.universe();
1052debug!(?prev_universe, ?largest_universe);
10531054 ((prev_universe.index() + 1)..=largest_universe.index())
1055 .map(|u| UniverseIndex::from_usize(u))
1056 .rev()
1057 .fold(constraint, |constraint, u| {
1058 eagerly_handle_placeholders_in_universe(infcx, constraint, u)
1059 })
1060}
10611062struct HigherRankedAliasMatcher<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> {
1063 infcx: &'a Infcx,
1064 region_constraints: Vec<RegionConstraint<I>>,
1065}
10661067impl<'a, Infcx: InferCtxtLike<Interner = I>, I: Interner> TypeRelation<I>
1068for HigherRankedAliasMatcher<'a, Infcx, I>
1069{
1070fn cx(&self) -> I {
1071self.infcx.cx()
1072 }
10731074fn relate_ty_args(
1075&mut self,
1076 a_ty: I::Ty,
1077 _b_ty: I::Ty,
1078 _ty_def_id: I::DefId,
1079 a_args: I::GenericArgs,
1080 b_args: I::GenericArgs,
1081 _mk: impl FnOnce(I::GenericArgs) -> I::Ty,
1082 ) -> RelateResult<I, I::Ty> {
1083 rustc_type_ir::relate::relate_args_invariantly(self, a_args, b_args)?;
1084Ok(a_ty)
1085 }
10861087fn relate_with_variance<T: Relate<I>>(
1088&mut self,
1089 _variance: Variance,
1090 _info: VarianceDiagInfo<I>,
1091 a: T,
1092 b: T,
1093 ) -> RelateResult<I, T> {
1094// FIXME(-Zassumptions-on-binders): bivariance is important for opaque type args so
1095 // we should actually handle variance in some way here.
1096self.relate(a, b)
1097 }
10981099fn tys(&mut self, a: I::Ty, b: I::Ty) -> RelateResult<I, I::Ty> {
1100 rustc_type_ir::relate::structurally_relate_tys(self, a, b)
1101 }
11021103fn regions(&mut self, a: Region<I>, b: Region<I>) -> RelateResult<I, Region<I>> {
1104if a != b {
1105self.region_constraints.push(RegionConstraint::RegionOutlives(a, b));
1106self.region_constraints.push(RegionConstraint::RegionOutlives(b, a));
1107 }
1108Ok(a)
1109 }
11101111fn consts(&mut self, a: I::Const, b: I::Const) -> RelateResult<I, I::Const> {
1112 rustc_type_ir::relate::structurally_relate_consts(self, a, b)
1113 }
11141115fn binders<T>(&mut self, a: Binder<I, T>, b: Binder<I, T>) -> RelateResult<I, Binder<I, T>>
1116where
1117T: Relate<I>,
1118 {
1119self.infcx.enter_forall_with_empty_assumptions(a, |a| {
1120let u = self.infcx.universe();
1121self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1122let b = self.infcx.instantiate_binder_with_infer(b);
1123self.relate(a, b)
1124 })?;
11251126self.infcx.enter_forall_with_empty_assumptions(b, |b| {
1127let u = self.infcx.universe();
1128self.infcx.insert_placeholder_assumptions(u, Some(Assumptions::empty()));
1129let a = self.infcx.instantiate_binder_with_infer(a);
1130self.relate(a, b)
1131 })?;
11321133Ok(a)
1134 }
1135}