• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::infer::canonical::query_response;
2 use crate::infer::InferCtxt;
3 use crate::traits::query::type_op::TypeOpOutput;
4 use crate::traits::ObligationCtxt;
5 use rustc_errors::ErrorGuaranteed;
6 use rustc_infer::infer::region_constraints::RegionConstraintData;
7 use rustc_middle::traits::query::NoSolution;
8 use rustc_middle::ty::{TyCtxt, TypeFoldable};
9 use rustc_span::source_map::DUMMY_SP;
10 use rustc_span::Span;
11 
12 use std::fmt;
13 
14 pub struct CustomTypeOp<F> {
15     closure: F,
16     description: &'static str,
17 }
18 
19 impl<F> CustomTypeOp<F> {
new<'tcx, R>(closure: F, description: &'static str) -> Self where F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,20     pub fn new<'tcx, R>(closure: F, description: &'static str) -> Self
21     where
22         F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
23     {
24         CustomTypeOp { closure, description }
25     }
26 }
27 
28 impl<'tcx, F, R> super::TypeOp<'tcx> for CustomTypeOp<F>
29 where
30     F: FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
31     R: fmt::Debug + TypeFoldable<TyCtxt<'tcx>>,
32 {
33     type Output = R;
34     /// We can't do any custom error reporting for `CustomTypeOp`, so
35     /// we can use `!` to enforce that the implementation never provides it.
36     type ErrorInfo = !;
37 
38     /// Processes the operation and all resulting obligations,
39     /// returning the final result along with any region constraints
40     /// (they will be given over to the NLL region solver).
fully_perform( self, infcx: &InferCtxt<'tcx>, span: Span, ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed>41     fn fully_perform(
42         self,
43         infcx: &InferCtxt<'tcx>,
44         span: Span,
45     ) -> Result<TypeOpOutput<'tcx, Self>, ErrorGuaranteed> {
46         if cfg!(debug_assertions) {
47             info!("fully_perform({:?})", self);
48         }
49 
50         Ok(scrape_region_constraints(infcx, self.closure, self.description, span)?.0)
51     }
52 }
53 
54 impl<F> fmt::Debug for CustomTypeOp<F> {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result55     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56         self.description.fmt(f)
57     }
58 }
59 
60 /// Executes `op` and then scrapes out all the "old style" region
61 /// constraints that result, creating query-region-constraints.
scrape_region_constraints<'tcx, Op, R>( infcx: &InferCtxt<'tcx>, op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>, name: &'static str, span: Span, ) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed> where R: TypeFoldable<TyCtxt<'tcx>>, Op: super::TypeOp<'tcx, Output = R>,62 pub fn scrape_region_constraints<'tcx, Op, R>(
63     infcx: &InferCtxt<'tcx>,
64     op: impl FnOnce(&ObligationCtxt<'_, 'tcx>) -> Result<R, NoSolution>,
65     name: &'static str,
66     span: Span,
67 ) -> Result<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>), ErrorGuaranteed>
68 where
69     R: TypeFoldable<TyCtxt<'tcx>>,
70     Op: super::TypeOp<'tcx, Output = R>,
71 {
72     // During NLL, we expect that nobody will register region
73     // obligations **except** as part of a custom type op (and, at the
74     // end of each custom type op, we scrape out the region
75     // obligations that resulted). So this vector should be empty on
76     // entry.
77     let pre_obligations = infcx.take_registered_region_obligations();
78     assert!(
79         pre_obligations.is_empty(),
80         "scrape_region_constraints: incoming region obligations = {:#?}",
81         pre_obligations,
82     );
83 
84     let value = infcx.commit_if_ok(|_| {
85         let ocx = ObligationCtxt::new(infcx);
86         let value = op(&ocx).map_err(|_| {
87             infcx.tcx.sess.delay_span_bug(span, format!("error performing operation: {name}"))
88         })?;
89         let errors = ocx.select_all_or_error();
90         if errors.is_empty() {
91             Ok(value)
92         } else {
93             Err(infcx.tcx.sess.delay_span_bug(
94                 DUMMY_SP,
95                 format!("errors selecting obligation during MIR typeck: {:?}", errors),
96             ))
97         }
98     })?;
99 
100     // Next trait solver performs operations locally, and normalize goals should resolve vars.
101     let value = infcx.resolve_vars_if_possible(value);
102 
103     let region_obligations = infcx.take_registered_region_obligations();
104     let region_constraint_data = infcx.take_and_reset_region_constraints();
105     let region_constraints = query_response::make_query_region_constraints(
106         infcx.tcx,
107         region_obligations
108             .iter()
109             .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category()))
110             .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)),
111         &region_constraint_data,
112     );
113 
114     if region_constraints.is_empty() {
115         Ok((
116             TypeOpOutput { output: value, constraints: None, error_info: None },
117             region_constraint_data,
118         ))
119     } else {
120         Ok((
121             TypeOpOutput {
122                 output: value,
123                 constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
124                 error_info: None,
125             },
126             region_constraint_data,
127         ))
128     }
129 }
130