summaryrefslogtreecommitdiff
path: root/compiler/rustc_hir_analysis/src/outlives/mod.rs
blob: da72d2584e335ac450e5db3e4a0e8f4a87f71898 (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use hir::Node;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::{self, CratePredicatesMap, TyCtxt};
use rustc_span::symbol::sym;
use rustc_span::Span;

mod explicit;
mod implicit_infer;
/// Code to write unit test for outlives.
pub mod test;
mod utils;

pub fn provide(providers: &mut Providers) {
    *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers };
}

fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: LocalDefId) -> &[(ty::Clause<'_>, Span)] {
    let id = tcx.hir().local_def_id_to_hir_id(item_def_id);

    if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
    {
        if tcx.hir().opt_const_param_default_param_def_id(id).is_some() {
            // In `generics_of` we set the generics' parent to be our parent's parent which means that
            // we lose out on the predicates of our actual parent if we dont return those predicates here.
            // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
            //
            // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ());
            //        ^^^                          ^^^^^^^ the def id we are calling
            //        ^^^                                  inferred_outlives_of on
            //        parent item we dont have set as the
            //        parent of generics returned by `generics_of`
            //
            // In the above code we want the anon const to have predicates in its param env for `'b: 'a`
            let item_def_id = tcx.hir().get_parent_item(id);
            // In the above code example we would be calling `inferred_outlives_of(Foo)` here
            return tcx.inferred_outlives_of(item_def_id);
        }
    }

    match tcx.hir().get(id) {
        Node::Item(item) => match item.kind {
            hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
                let crate_map = tcx.inferred_outlives_crate(());

                let predicates =
                    crate_map.predicates.get(&item_def_id.to_def_id()).copied().unwrap_or(&[]);

                if tcx.has_attr(item_def_id, sym::rustc_outlives) {
                    let mut pred: Vec<String> = predicates
                        .iter()
                        .map(|(out_pred, _)| match out_pred {
                            ty::Clause::RegionOutlives(p) => p.to_string(),
                            ty::Clause::TypeOutlives(p) => p.to_string(),
                            err => bug!("unexpected clause {:?}", err),
                        })
                        .collect();
                    pred.sort();

                    let span = tcx.def_span(item_def_id);
                    let mut err = tcx.sess.struct_span_err(span, "rustc_outlives");
                    for p in &pred {
                        err.note(p);
                    }
                    err.emit();
                }

                debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates);

                predicates
            }

            _ => &[],
        },

        _ => &[],
    }
}

fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
    // Compute a map from each struct/enum/union S to the **explicit**
    // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
    // Typically there won't be many of these, except in older code where
    // they were mandatory. Nonetheless, we have to ensure that every such
    // predicate is satisfied, so they form a kind of base set of requirements
    // for the type.

    // Compute the inferred predicates
    let global_inferred_outlives = implicit_infer::infer_predicates(tcx);

    // Convert the inferred predicates into the "collected" form the
    // global data structure expects.
    //
    // FIXME -- consider correcting impedance mismatch in some way,
    // probably by updating the global data structure.
    let predicates = global_inferred_outlives
        .iter()
        .map(|(&def_id, set)| {
            let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
                |(ty::OutlivesPredicate(kind1, region2), &span)| {
                    match kind1.unpack() {
                        GenericArgKind::Type(ty1) => Some((
                            ty::Clause::TypeOutlives(ty::OutlivesPredicate(ty1, *region2)),
                            span,
                        )),
                        GenericArgKind::Lifetime(region1) => Some((
                            ty::Clause::RegionOutlives(ty::OutlivesPredicate(region1, *region2)),
                            span,
                        )),
                        GenericArgKind::Const(_) => {
                            // Generic consts don't impose any constraints.
                            None
                        }
                    }
                },
            ));
            (def_id, predicates)
        })
        .collect();

    ty::CratePredicatesMap { predicates }
}