rustc_trait_selection/traits/query/type_op/
custom.rs

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