Skip to main content

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 truecfg!(debug_assertions) {
50            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs:50",
                        "rustc_trait_selection::traits::query::type_op::custom",
                        ::tracing::Level::INFO,
                        ::tracing_core::__macro_support::Option::Some("compiler/rustc_trait_selection/src/traits/query/type_op/custom.rs"),
                        ::tracing_core::__macro_support::Option::Some(50u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_trait_selection::traits::query::type_op::custom"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::INFO <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::INFO <=
                    ::tracing::level_filters::LevelFilter::current() &&
            {
                let interest = __CALLSITE.interest();
                !interest.is_never() &&
                    ::tracing::__macro_support::__is_enabled(__CALLSITE.metadata(),
                        interest)
            };
    if enabled {
        (|value_set: ::tracing::field::ValueSet|
                    {
                        let meta = __CALLSITE.metadata();
                        ::tracing::Event::dispatch(meta, &value_set);
                        ;
                    })({
                #[allow(unused_imports)]
                use ::tracing::field::{debug, display, Value};
                let mut iter = __CALLSITE.metadata().fields().iter();
                __CALLSITE.metadata().fields().value_set(&[(&::tracing::__macro_support::Iterator::next(&mut iter).expect("FieldSet corrupted (this is a bug)"),
                                    ::tracing::__macro_support::Option::Some(&format_args!("fully_perform({0:?})",
                                                    self) as &dyn Value))])
            });
    } else { ; }
};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    if !pre_obligations.is_empty() {
    {
        ::core::panicking::panic_fmt(format_args!("scrape_region_constraints: incoming region obligations = {0:#?}",
                pre_obligations));
    }
};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    if !pre_assumptions.is_empty() {
    {
        ::core::panicking::panic_fmt(format_args!("scrape_region_constraints: incoming region assumptions = {0:#?}",
                pre_assumptions));
    }
};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.tcx.check_potentially_region_dependent_goals(root_def_id).err().unwrap_or_else(
96                // FIXME: In this region-dependent context, `type_op` should only fail due to
97                // region-dependent goals. Any other kind of failure indicates a bug and we
98                // should ICE.
99                //
100                // In principle, all non-region errors are expected to be emitted before
101                // borrowck. Such errors should taint the body and prevent borrowck from
102                // running at all. However, this invariant does not currently hold.
103                //
104                // Consider:
105                //
106                // ```ignore (illustrative)
107                // struct Foo<T>(T)
108                // where
109                //     T: Iterator,
110                //     <T as Iterator>::Item: Default;
111                //
112                // fn foo<T>() -> Foo<T> {
113                //     loop {}
114                // }
115                // ```
116                //
117                // Here, `fn foo` ought to error and taint the body before MIR build, since
118                // `Foo<T>` is not well-formed. However, we currently avoid checking this
119                // during HIR typeck to prevent duplicate diagnostics.
120                //
121                // If we ICE here on any non-region-dependent failure, we would trigger ICEs
122                // too often for such cases. For now, we emit a delayed bug instead.
123                || {
124                    infcx
125                        .dcx()
126                        .span_delayed_bug(span, ::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("error performing operation: {0}",
                name))
    })format!("error performing operation: {name}"))
127                },
128            )
129        })?;
130        let errors = ocx.evaluate_obligations_error_on_ambiguity();
131        if errors.is_empty() {
132            Ok(value)
133        } else if let Err(guar) = infcx.tcx.check_potentially_region_dependent_goals(root_def_id) {
134            Err(guar)
135        } else {
136            Err(infcx.dcx().delayed_bug(::alloc::__export::must_use({
        ::alloc::fmt::format(format_args!("errors selecting obligation during MIR typeck: {0} {1:?} {2:?}",
                name, root_def_id, errors))
    })format!(
137                "errors selecting obligation during MIR typeck: {name} {root_def_id:?} {errors:?}"
138            )))
139        }
140    })?;
141
142    // Next trait solver performs operations locally, and normalize goals should resolve vars.
143    let value = infcx.resolve_vars_if_possible(value);
144
145    let region_obligations = infcx.take_registered_region_obligations();
146    let region_assumptions = infcx.take_registered_region_assumptions();
147    let region_constraint_data = infcx.take_and_reset_region_constraints();
148    let region_constraints = query_response::make_query_region_constraints(
149        region_obligations,
150        &region_constraint_data,
151        region_assumptions,
152    );
153
154    if region_constraints.is_empty() {
155        Ok((
156            TypeOpOutput { output: value, constraints: None, error_info: None },
157            region_constraint_data,
158        ))
159    } else {
160        Ok((
161            TypeOpOutput {
162                output: value,
163                constraints: Some(infcx.tcx.arena.alloc(region_constraints)),
164                error_info: None,
165            },
166            region_constraint_data,
167        ))
168    }
169}