rustc_trait_selection/traits/query/type_op/
custom.rs1use 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 type ErrorInfo = !;
38
39 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
61pub 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 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 let pre_assumptions = infcx.take_registered_region_assumptions();
84 assert!(
85 pre_assumptions.is_empty(),
86 "scrape_region_constraints: incoming region assumptions = {pre_assumptions:#?}",
87 );
88
89 let value = infcx.commit_if_ok(|_| {
90 let ocx = ObligationCtxt::new(infcx);
91 let value = op(&ocx).map_err(|_| {
92 infcx.dcx().span_delayed_bug(span, format!("error performing operation: {name}"))
93 })?;
94 let errors = ocx.select_all_or_error();
95 if errors.is_empty() {
96 Ok(value)
97 } else {
98 Err(infcx
99 .dcx()
100 .delayed_bug(format!("errors selecting obligation during MIR typeck: {errors:?}")))
101 }
102 })?;
103
104 let value = infcx.resolve_vars_if_possible(value);
106
107 let region_obligations = infcx.take_registered_region_obligations();
108 let region_assumptions = infcx.take_registered_region_assumptions();
109 let region_constraint_data = infcx.take_and_reset_region_constraints();
110 let region_constraints = query_response::make_query_region_constraints(
111 region_obligations,
112 ®ion_constraint_data,
113 region_assumptions,
114 );
115
116 if region_constraints.is_empty() {
117 Ok((
118 TypeOpOutput { output: value, constraints: None, error_info: None },
119 region_constraint_data,
120 ))
121 } else {
122 Ok((
123 TypeOpOutput {
124 output: value,
125 constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
126 error_info: None,
127 },
128 region_constraint_data,
129 ))
130 }
131}