Skip to main content

rustc_borrowck/polonius/legacy/
mod.rs

1//! Functions dedicated to fact generation for the `-Zpolonius=legacy` datalog implementation.
2//!
3//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
4//! parity.
5
6use std::iter;
7
8use either::Either;
9use rustc_middle::mir::{Body, Local, LocalKind, Location};
10use rustc_middle::ty::{GenericArg, TyCtxt};
11use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
12use tracing::debug;
13
14use crate::borrow_set::BorrowSet;
15use crate::constraints::OutlivesConstraint;
16use crate::handle_placeholders::LoweredConstraints;
17use crate::type_check::free_region_relations::UniversalRegionRelations;
18use crate::universal_regions::UniversalRegions;
19
20mod accesses;
21mod loan_invalidations;
22mod loan_kills;
23mod location;
24pub use self::location::*;
25mod facts;
26pub use self::facts::*;
27
28/// When requested, emit most of the facts needed by polonius:
29/// - moves and assignments
30/// - universal regions and their relations
31/// - CFG points and edges
32/// - loan kills
33/// - loan invalidations
34/// - access facts such as variable definitions, uses, drops, and path accesses
35/// - outlives constraints
36///
37/// The rest of the facts are emitted during typeck and liveness.
38pub(crate) fn emit_facts<'tcx>(
39    facts: &mut Option<PoloniusFacts>,
40    tcx: TyCtxt<'tcx>,
41    location_table: &PoloniusLocationTable,
42    body: &Body<'tcx>,
43    borrow_set: &BorrowSet<'tcx>,
44    move_data: &MoveData<'tcx>,
45    universal_region_relations: &UniversalRegionRelations<'tcx>,
46    constraints: &LoweredConstraints<'tcx>,
47) {
48    let Some(facts) = facts else {
49        // We don't do anything if there are no facts to fill.
50        return;
51    };
52    let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
53    emit_move_facts(facts, body, location_table, move_data);
54    emit_universal_region_facts(facts, borrow_set, universal_region_relations);
55    loan_kills::emit_loan_kills(tcx, facts, body, location_table, borrow_set);
56    loan_invalidations::emit_loan_invalidations(tcx, facts, body, location_table, borrow_set);
57    accesses::emit_access_facts(
58        tcx,
59        facts,
60        body,
61        location_table,
62        move_data,
63        &universal_region_relations.universal_regions,
64    );
65    emit_outlives_facts(facts, location_table, constraints);
66}
67
68/// Emit facts needed for move/init analysis: moves and assignments.
69fn emit_move_facts(
70    facts: &mut PoloniusFacts,
71    body: &Body<'_>,
72    location_table: &PoloniusLocationTable,
73    move_data: &MoveData<'_>,
74) {
75    facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
76
77    for (child, move_path) in move_data.move_paths.iter_enumerated() {
78        if let Some(parent) = move_path.parent {
79            facts.child_path.push((child, parent));
80        }
81    }
82
83    let fn_entry_start = location_table.start_index(Location::START);
84
85    // initialized_at
86    for init in move_data.inits.iter() {
87        match init.location {
88            InitLocation::Statement(location) => {
89                let block_data = &body[location.block];
90                let is_terminator = location.statement_index == block_data.statements.len();
91
92                if is_terminator && init.kind == InitKind::NonPanicPathOnly {
93                    // We are at the terminator of an init that has a panic path,
94                    // and where the init should not happen on panic
95
96                    for successor in block_data.terminator().successors() {
97                        if body[successor].is_cleanup {
98                            continue;
99                        }
100
101                        // The initialization happened in (or rather, when arriving at)
102                        // the successors, but not in the unwind block.
103                        let first_statement = successor.start_location();
104                        facts
105                            .path_assigned_at_base
106                            .push((init.path, location_table.start_index(first_statement)));
107                    }
108                } else {
109                    // In all other cases, the initialization just happens at the
110                    // midpoint, like any other effect.
111                    facts
112                        .path_assigned_at_base
113                        .push((init.path, location_table.mid_index(location)));
114                }
115            }
116            // Arguments are initialized on function entry
117            InitLocation::Argument(local) => {
118                if !(body.local_kind(local) == LocalKind::Arg) {
    ::core::panicking::panic("assertion failed: body.local_kind(local) == LocalKind::Arg")
};assert!(body.local_kind(local) == LocalKind::Arg);
119                facts.path_assigned_at_base.push((init.path, fn_entry_start));
120            }
121        }
122    }
123
124    for (local, path) in move_data.rev_lookup.iter_locals_enumerated() {
125        if body.local_kind(local) != LocalKind::Arg {
126            // Non-arguments start out deinitialised; we simulate this with an
127            // initial move:
128            facts.path_moved_at_base.push((path, fn_entry_start));
129        }
130    }
131
132    // moved_out_at
133    // deinitialisation is assumed to always happen!
134    facts.path_moved_at_base.extend(
135        move_data.move_outs.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))),
136    );
137}
138
139/// Emit universal regions facts, and their relations.
140fn emit_universal_region_facts(
141    facts: &mut PoloniusFacts,
142    borrow_set: &BorrowSet<'_>,
143    universal_region_relations: &UniversalRegionRelations<'_>,
144) {
145    // 1: universal regions are modeled in Polonius as a pair:
146    // - the universal region vid itself.
147    // - a "placeholder loan" associated to this universal region. Since they don't exist in
148    //   the `borrow_set`, their `BorrowIndex` are synthesized as the universal region index
149    //   added to the existing number of loans, as if they succeeded them in the set.
150    //
151    let universal_regions = &universal_region_relations.universal_regions;
152    facts
153        .universal_region
154        .extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
155    let borrow_count = borrow_set.len();
156    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs:156",
                        "rustc_borrowck::polonius::legacy", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(156u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::polonius::legacy"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("emit_universal_region_facts: polonius placeholders, num_universals={0}, borrow_count={1}",
                                                    universal_regions.len(), borrow_count) as
                                            &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
157        "emit_universal_region_facts: polonius placeholders, num_universals={}, borrow_count={}",
158        universal_regions.len(),
159        borrow_count
160    );
161
162    for universal_region in universal_regions.universal_regions_iter() {
163        let universal_region_idx = universal_region.index();
164        let placeholder_loan_idx = borrow_count + universal_region_idx;
165        facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
166    }
167
168    // 2: the universal region relations `outlives` constraints are emitted as
169    //  `known_placeholder_subset` facts.
170    for (fr1, fr2) in universal_region_relations.known_outlives() {
171        if fr1 != fr2 {
172            {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs:172",
                        "rustc_borrowck::polonius::legacy", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(172u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::polonius::legacy"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("emit_universal_region_facts: emitting polonius `known_placeholder_subset` fr1={0:?}, fr2={1:?}",
                                                    fr1, fr2) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!(
173                "emit_universal_region_facts: emitting polonius `known_placeholder_subset` \
174                     fr1={:?}, fr2={:?}",
175                fr1, fr2
176            );
177            facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
178        }
179    }
180}
181
182/// For every potentially drop()-touched region `region` in `local`'s type
183/// (`kind`), emit a `drop_of_var_derefs_origin(local, origin)` fact.
184pub(crate) fn emit_drop_facts<'tcx>(
185    tcx: TyCtxt<'tcx>,
186    local: Local,
187    kind: &GenericArg<'tcx>,
188    universal_regions: &UniversalRegions<'tcx>,
189    facts: &mut Option<PoloniusFacts>,
190) {
191    {
    use ::tracing::__macro_support::Callsite as _;
    static __CALLSITE: ::tracing::callsite::DefaultCallsite =
        {
            static META: ::tracing::Metadata<'static> =
                {
                    ::tracing_core::metadata::Metadata::new("event /rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs:191",
                        "rustc_borrowck::polonius::legacy", ::tracing::Level::DEBUG,
                        ::tracing_core::__macro_support::Option::Some("/rustc-dev/080cc27146389dbb5c8772c476b1937be9c6110f/compiler/rustc_borrowck/src/polonius/legacy/mod.rs"),
                        ::tracing_core::__macro_support::Option::Some(191u32),
                        ::tracing_core::__macro_support::Option::Some("rustc_borrowck::polonius::legacy"),
                        ::tracing_core::field::FieldSet::new(&["message"],
                            ::tracing_core::callsite::Identifier(&__CALLSITE)),
                        ::tracing::metadata::Kind::EVENT)
                };
            ::tracing::callsite::DefaultCallsite::new(&META)
        };
    let enabled =
        ::tracing::Level::DEBUG <= ::tracing::level_filters::STATIC_MAX_LEVEL
                &&
                ::tracing::Level::DEBUG <=
                    ::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};
                __CALLSITE.metadata().fields().value_set_all(&[(::tracing::__macro_support::Option::Some(&format_args!("emit_drop_facts(local={0:?}, kind={1:?}",
                                                    local, kind) as &dyn ::tracing::field::Value))])
            });
    } else { ; }
};debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind);
192    let Some(facts) = facts.as_mut() else { return };
193    let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
194    tcx.for_each_free_region(&kind, |drop_live_region| {
195        let region_vid = universal_regions.to_region_vid(drop_live_region);
196        facts.drop_of_var_derefs_origin.push((local, region_vid.into()));
197    });
198}
199
200/// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive
201/// closure.
202fn emit_outlives_facts<'tcx>(
203    facts: &mut PoloniusFacts,
204    location_table: &PoloniusLocationTable,
205    constraints: &LoweredConstraints<'tcx>,
206) {
207    facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
208        |constraint: &OutlivesConstraint<'_>| {
209            if let Some(from_location) = constraint.locations.from_location() {
210                Either::Left(iter::once((
211                    constraint.sup.into(),
212                    constraint.sub.into(),
213                    location_table.mid_index(from_location),
214                )))
215            } else {
216                Either::Right(
217                    location_table.all_points().map(move |location| {
218                        (constraint.sup.into(), constraint.sub.into(), location)
219                    }),
220                )
221            }
222        },
223    ));
224}