rustc_trait_selection/traits/query/type_op/
custom.rs

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