]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs
Separate trait selection from ambiguity reporting.
[rust.git] / compiler / rustc_trait_selection / src / traits / query / type_op / custom.rs
1 use crate::infer::canonical::query_response;
2 use crate::infer::{InferCtxt, InferOk};
3 use crate::traits;
4 use crate::traits::query::type_op::TypeOpOutput;
5 use crate::traits::query::Fallible;
6 use rustc_infer::infer::region_constraints::RegionConstraintData;
7 use rustc_span::source_map::DUMMY_SP;
8
9 use std::fmt;
10
11 pub struct CustomTypeOp<F, G> {
12     closure: F,
13     description: G,
14 }
15
16 impl<F, G> CustomTypeOp<F, G> {
17     pub fn new<'tcx, R>(closure: F, description: G) -> Self
18     where
19         F: FnOnce(&InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>,
20         G: Fn() -> String,
21     {
22         CustomTypeOp { closure, description }
23     }
24 }
25
26 impl<'tcx, F, R: fmt::Debug, G> super::TypeOp<'tcx> for CustomTypeOp<F, G>
27 where
28     F: for<'a, 'cx> FnOnce(&'a InferCtxt<'tcx>) -> Fallible<InferOk<'tcx, R>>,
29     G: Fn() -> String,
30 {
31     type Output = R;
32     /// We can't do any custom error reporting for `CustomTypeOp`, so
33     /// we can use `!` to enforce that the implementation never provides it.
34     type ErrorInfo = !;
35
36     /// Processes the operation and all resulting obligations,
37     /// returning the final result along with any region constraints
38     /// (they will be given over to the NLL region solver).
39     fn fully_perform(self, infcx: &InferCtxt<'tcx>) -> Fallible<TypeOpOutput<'tcx, Self>> {
40         if cfg!(debug_assertions) {
41             info!("fully_perform({:?})", self);
42         }
43
44         Ok(scrape_region_constraints(infcx, || (self.closure)(infcx))?.0)
45     }
46 }
47
48 impl<F, G> fmt::Debug for CustomTypeOp<F, G>
49 where
50     G: Fn() -> String,
51 {
52     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53         write!(f, "{}", (self.description)())
54     }
55 }
56
57 /// Executes `op` and then scrapes out all the "old style" region
58 /// constraints that result, creating query-region-constraints.
59 pub fn scrape_region_constraints<'tcx, Op: super::TypeOp<'tcx, Output = R>, R>(
60     infcx: &InferCtxt<'tcx>,
61     op: impl FnOnce() -> Fallible<InferOk<'tcx, R>>,
62 ) -> Fallible<(TypeOpOutput<'tcx, Op>, RegionConstraintData<'tcx>)> {
63     // During NLL, we expect that nobody will register region
64     // obligations **except** as part of a custom type op (and, at the
65     // end of each custom type op, we scrape out the region
66     // obligations that resulted). So this vector should be empty on
67     // entry.
68     let pre_obligations = infcx.take_registered_region_obligations();
69     assert!(
70         pre_obligations.is_empty(),
71         "scrape_region_constraints: incoming region obligations = {:#?}",
72         pre_obligations,
73     );
74
75     let InferOk { value, obligations } = infcx.commit_if_ok(|_| op())?;
76     let errors = traits::fully_solve_obligations(infcx, obligations);
77     if !errors.is_empty() {
78         infcx.tcx.sess.diagnostic().delay_span_bug(
79             DUMMY_SP,
80             &format!("errors selecting obligation during MIR typeck: {:?}", errors),
81         );
82     }
83
84     let region_obligations = infcx.take_registered_region_obligations();
85
86     let region_constraint_data = infcx.take_and_reset_region_constraints();
87
88     let region_constraints = query_response::make_query_region_constraints(
89         infcx.tcx,
90         region_obligations
91             .iter()
92             .map(|r_o| (r_o.sup_type, r_o.sub_region, r_o.origin.to_constraint_category()))
93             .map(|(ty, r, cc)| (infcx.resolve_vars_if_possible(ty), r, cc)),
94         &region_constraint_data,
95     );
96
97     if region_constraints.is_empty() {
98         Ok((
99             TypeOpOutput { output: value, constraints: None, error_info: None },
100             region_constraint_data,
101         ))
102     } else {
103         Ok((
104             TypeOpOutput {
105                 output: value,
106                 constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
107                 error_info: None,
108             },
109             region_constraint_data,
110         ))
111     }
112 }