summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_typeck/src/outlives/outlives_bounds.rs
blob: 229a64650848cff7eae1401fe3a5cff720d1354a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
use rustc_hir as hir;
use rustc_middle::ty::{self, Ty};
use rustc_trait_selection::infer::InferCtxt;
use rustc_trait_selection::traits::query::type_op::{self, TypeOp, TypeOpOutput};
use rustc_trait_selection::traits::query::NoSolution;
use rustc_trait_selection::traits::{ObligationCause, TraitEngine, TraitEngineExt};

pub use rustc_middle::traits::query::OutlivesBound;

pub trait InferCtxtExt<'tcx> {
    fn implied_outlives_bounds(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        body_id: hir::HirId,
        ty: Ty<'tcx>,
    ) -> Vec<OutlivesBound<'tcx>>;
}

impl<'cx, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'cx, 'tcx> {
    /// Implied bounds are region relationships that we deduce
    /// automatically. The idea is that (e.g.) a caller must check that a
    /// function's argument types are well-formed immediately before
    /// calling that fn, and hence the *callee* can assume that its
    /// argument types are well-formed. This may imply certain relationships
    /// between generic parameters. For example:
    /// ```
    /// fn foo<'a,T>(x: &'a T) {}
    /// ```
    /// can only be called with a `'a` and `T` such that `&'a T` is WF.
    /// For `&'a T` to be WF, `T: 'a` must hold. So we can assume `T: 'a`.
    ///
    /// # Parameters
    ///
    /// - `param_env`, the where-clauses in scope
    /// - `body_id`, the body-id to use when normalizing assoc types.
    ///   Note that this may cause outlives obligations to be injected
    ///   into the inference context with this body-id.
    /// - `ty`, the type that we are supposed to assume is WF.
    #[instrument(level = "debug", skip(self, param_env, body_id))]
    fn implied_outlives_bounds(
        &self,
        param_env: ty::ParamEnv<'tcx>,
        body_id: hir::HirId,
        ty: Ty<'tcx>,
    ) -> Vec<OutlivesBound<'tcx>> {
        let span = self.tcx.hir().span(body_id);
        let result = param_env
            .and(type_op::implied_outlives_bounds::ImpliedOutlivesBounds { ty })
            .fully_perform(self);
        let result = match result {
            Ok(r) => r,
            Err(NoSolution) => {
                self.tcx.sess.delay_span_bug(
                    span,
                    "implied_outlives_bounds failed to solve all obligations",
                );
                return vec![];
            }
        };

        let TypeOpOutput { output, constraints, .. } = result;

        if let Some(constraints) = constraints {
            // Instantiation may have produced new inference variables and constraints on those
            // variables. Process these constraints.
            let mut fulfill_cx = <dyn TraitEngine<'tcx>>::new(self.tcx);
            let cause = ObligationCause::misc(span, body_id);
            for &constraint in &constraints.outlives {
                let obligation = self.query_outlives_constraint_to_obligation(
                    constraint,
                    cause.clone(),
                    param_env,
                );
                fulfill_cx.register_predicate_obligation(self, obligation);
            }
            if !constraints.member_constraints.is_empty() {
                span_bug!(span, "{:#?}", constraints.member_constraints);
            }
            let errors = fulfill_cx.select_all_or_error(self);
            if !errors.is_empty() {
                self.tcx.sess.delay_span_bug(
                    span,
                    "implied_outlives_bounds failed to solve obligations from instantiation",
                );
            }
        };

        output
    }
}