Rollup merge of #134378 - lqd:polonius-next-episode-2, r=jackh726
An octuple of polonius fact generation cleanups This PR is extracted from https://github.com/rust-lang/rust/pull/134268 for easier review and contains its first 8 commits. They have already been reviewed by ````@jackh726```` over there. r? ````@jackh726````
This commit is contained in:
commit
264566fc61
9 changed files with 240 additions and 260 deletions
|
|
@ -124,6 +124,7 @@ pub(crate) fn compute_regions<'a, 'tcx>(
|
|||
borrow_set,
|
||||
move_data,
|
||||
&universal_region_relations,
|
||||
&constraints,
|
||||
);
|
||||
|
||||
let mut regioncx = RegionInferenceContext::new(
|
||||
|
|
|
|||
85
compiler/rustc_borrowck/src/polonius/legacy/accesses.rs
Normal file
85
compiler/rustc_borrowck/src/polonius/legacy/accesses.rs
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{Body, Local, Location, Place};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::def_use::{self, DefUse};
|
||||
use crate::facts::AllFacts;
|
||||
use crate::location::{LocationIndex, LocationTable};
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
/// Emit polonius facts for variable defs, uses, drops, and path accesses.
|
||||
pub(crate) fn emit_access_facts<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
facts: &mut AllFacts,
|
||||
body: &Body<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
move_data: &MoveData<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
) {
|
||||
let mut extractor = AccessFactsExtractor { facts, move_data, location_table };
|
||||
extractor.visit_body(body);
|
||||
|
||||
for (local, local_decl) in body.local_decls.iter_enumerated() {
|
||||
debug!("add use_of_var_derefs_origin facts - local={:?}, type={:?}", local, local_decl.ty);
|
||||
tcx.for_each_free_region(&local_decl.ty, |region| {
|
||||
let region_vid = universal_regions.to_region_vid(region);
|
||||
facts.use_of_var_derefs_origin.push((local, region_vid.into()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR visitor extracting point-wise facts about accesses.
|
||||
struct AccessFactsExtractor<'a, 'tcx> {
|
||||
facts: &'a mut AllFacts,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
location_table: &'a LocationTable,
|
||||
}
|
||||
|
||||
impl<'tcx> AccessFactsExtractor<'_, 'tcx> {
|
||||
fn location_to_index(&self, location: Location) -> LocationIndex {
|
||||
self.location_table.mid_index(location)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
|
||||
match def_use::categorize(context) {
|
||||
Some(DefUse::Def) => {
|
||||
debug!("AccessFactsExtractor - emit def");
|
||||
self.facts.var_defined_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Use) => {
|
||||
debug!("AccessFactsExtractor - emit use");
|
||||
self.facts.var_used_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Drop) => {
|
||||
debug!("AccessFactsExtractor - emit drop");
|
||||
self.facts.var_dropped_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.super_place(place, context, location);
|
||||
|
||||
match context {
|
||||
PlaceContext::NonMutatingUse(_)
|
||||
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
|
||||
let path = match self.move_data.rev_lookup.find(place.as_ref()) {
|
||||
LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path,
|
||||
_ => {
|
||||
// There's no path access to emit.
|
||||
return;
|
||||
}
|
||||
};
|
||||
debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})");
|
||||
self.facts.path_accessed_at_base.push((path, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -21,22 +21,22 @@ use crate::{
|
|||
/// Emit `loan_invalidated_at` facts.
|
||||
pub(super) fn emit_loan_invalidations<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &mut AllFacts,
|
||||
location_table: &LocationTable,
|
||||
facts: &mut AllFacts,
|
||||
body: &Body<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let dominators = body.basic_blocks.dominators();
|
||||
let mut visitor =
|
||||
LoanInvalidationsGenerator { all_facts, borrow_set, tcx, location_table, body, dominators };
|
||||
LoanInvalidationsGenerator { facts, borrow_set, tcx, location_table, body, dominators };
|
||||
visitor.visit_body(body);
|
||||
}
|
||||
|
||||
struct LoanInvalidationsGenerator<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &'a mut AllFacts,
|
||||
location_table: &'a LocationTable,
|
||||
facts: &'a mut AllFacts,
|
||||
body: &'a Body<'tcx>,
|
||||
location_table: &'a LocationTable,
|
||||
dominators: &'a Dominators<BasicBlock>,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
}
|
||||
|
|
@ -151,7 +151,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
let resume = self.location_table.start_index(resume.start_location());
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((resume, i));
|
||||
self.facts.loan_invalidated_at.push((resume, i));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -165,7 +165,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
let start = self.location_table.start_index(location);
|
||||
for (i, data) in borrow_set.iter_enumerated() {
|
||||
if borrow_of_local_data(data.borrowed_place) {
|
||||
self.all_facts.loan_invalidated_at.push((start, i));
|
||||
self.facts.loan_invalidated_at.push((start, i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -409,7 +409,7 @@ impl<'a, 'tcx> LoanInvalidationsGenerator<'a, 'tcx> {
|
|||
/// Generates a new `loan_invalidated_at(L, B)` fact.
|
||||
fn emit_loan_invalidated_at(&mut self, b: BorrowIndex, l: Location) {
|
||||
let lidx = self.location_table.start_index(l);
|
||||
self.all_facts.loan_invalidated_at.push((lidx, b));
|
||||
self.facts.loan_invalidated_at.push((lidx, b));
|
||||
}
|
||||
|
||||
fn check_activations(&mut self, location: Location) {
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ use crate::places_conflict;
|
|||
/// Emit `loan_killed_at` and `cfg_edge` facts at the same time.
|
||||
pub(super) fn emit_loan_kills<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &mut AllFacts,
|
||||
location_table: &LocationTable,
|
||||
facts: &mut AllFacts,
|
||||
body: &Body<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
) {
|
||||
let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, all_facts, body };
|
||||
let mut visitor = LoanKillsGenerator { borrow_set, tcx, location_table, facts, body };
|
||||
for (bb, data) in body.basic_blocks.iter_enumerated() {
|
||||
visitor.visit_basic_block_data(bb, data);
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ pub(super) fn emit_loan_kills<'tcx>(
|
|||
|
||||
struct LoanKillsGenerator<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
all_facts: &'a mut AllFacts,
|
||||
facts: &'a mut AllFacts,
|
||||
location_table: &'a LocationTable,
|
||||
borrow_set: &'a BorrowSet<'tcx>,
|
||||
body: &'a Body<'tcx>,
|
||||
|
|
@ -36,12 +36,12 @@ struct LoanKillsGenerator<'a, 'tcx> {
|
|||
impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
|
||||
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
|
||||
// Also record CFG facts here.
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(location.successor_within_block()),
|
||||
));
|
||||
|
|
@ -63,15 +63,15 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanKillsGenerator<'a, 'tcx> {
|
|||
|
||||
fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
|
||||
// Also record CFG facts here.
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.facts.cfg_edge.push((
|
||||
self.location_table.start_index(location),
|
||||
self.location_table.mid_index(location),
|
||||
));
|
||||
|
||||
let successor_blocks = terminator.successors();
|
||||
self.all_facts.cfg_edge.reserve(successor_blocks.size_hint().0);
|
||||
self.facts.cfg_edge.reserve(successor_blocks.size_hint().0);
|
||||
for successor_block in successor_blocks {
|
||||
self.all_facts.cfg_edge.push((
|
||||
self.facts.cfg_edge.push((
|
||||
self.location_table.mid_index(location),
|
||||
self.location_table.start_index(successor_block.start_location()),
|
||||
));
|
||||
|
|
@ -128,7 +128,7 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
|
|||
|
||||
if places_conflict {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
self.all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
self.facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,9 +140,9 @@ impl<'tcx> LoanKillsGenerator<'_, 'tcx> {
|
|||
fn record_killed_borrows_for_local(&mut self, local: Local, location: Location) {
|
||||
if let Some(borrow_indices) = self.borrow_set.local_map.get(&local) {
|
||||
let location_index = self.location_table.mid_index(location);
|
||||
self.all_facts.loan_killed_at.reserve(borrow_indices.len());
|
||||
self.facts.loan_killed_at.reserve(borrow_indices.len());
|
||||
for &borrow_index in borrow_indices {
|
||||
self.all_facts.loan_killed_at.push((borrow_index, location_index));
|
||||
self.facts.loan_killed_at.push((borrow_index, location_index));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,16 +3,23 @@
|
|||
//! Will be removed in the future, once the in-tree `-Zpolonius=next` implementation reaches feature
|
||||
//! parity.
|
||||
|
||||
use rustc_middle::mir::{Body, LocalKind, Location, START_BLOCK};
|
||||
use rustc_middle::ty::TyCtxt;
|
||||
use std::iter;
|
||||
|
||||
use either::Either;
|
||||
use rustc_middle::mir::{Body, Local, LocalKind, Location, START_BLOCK};
|
||||
use rustc_middle::ty::{GenericArg, TyCtxt};
|
||||
use rustc_mir_dataflow::move_paths::{InitKind, InitLocation, MoveData};
|
||||
use tracing::debug;
|
||||
|
||||
use crate::borrow_set::BorrowSet;
|
||||
use crate::constraints::OutlivesConstraint;
|
||||
use crate::facts::{AllFacts, PoloniusRegionVid};
|
||||
use crate::location::LocationTable;
|
||||
use crate::type_check::MirTypeckRegionConstraints;
|
||||
use crate::type_check::free_region_relations::UniversalRegionRelations;
|
||||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
mod accesses;
|
||||
mod loan_invalidations;
|
||||
mod loan_kills;
|
||||
|
||||
|
|
@ -22,6 +29,8 @@ mod loan_kills;
|
|||
/// - CFG points and edges
|
||||
/// - loan kills
|
||||
/// - loan invalidations
|
||||
/// - access facts such as variable definitions, uses, drops, and path accesses
|
||||
/// - outlives constraints
|
||||
///
|
||||
/// The rest of the facts are emitted during typeck and liveness.
|
||||
pub(crate) fn emit_facts<'tcx>(
|
||||
|
|
@ -30,34 +39,42 @@ pub(crate) fn emit_facts<'tcx>(
|
|||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
move_data: &MoveData<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
universal_region_relations: &UniversalRegionRelations<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) {
|
||||
let Some(all_facts) = all_facts else {
|
||||
let Some(facts) = all_facts else {
|
||||
// We don't do anything if there are no facts to fill.
|
||||
return;
|
||||
};
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
emit_move_facts(all_facts, move_data, location_table, body);
|
||||
emit_universal_region_facts(all_facts, borrow_set, universal_region_relations);
|
||||
emit_cfg_and_loan_kills_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
emit_loan_invalidations_facts(all_facts, tcx, location_table, body, borrow_set);
|
||||
emit_move_facts(facts, body, location_table, move_data);
|
||||
emit_universal_region_facts(facts, borrow_set, universal_region_relations);
|
||||
loan_kills::emit_loan_kills(tcx, facts, body, location_table, borrow_set);
|
||||
loan_invalidations::emit_loan_invalidations(tcx, facts, body, location_table, borrow_set);
|
||||
accesses::emit_access_facts(
|
||||
tcx,
|
||||
facts,
|
||||
body,
|
||||
location_table,
|
||||
move_data,
|
||||
&universal_region_relations.universal_regions,
|
||||
);
|
||||
emit_outlives_facts(facts, location_table, constraints);
|
||||
}
|
||||
|
||||
/// Emit facts needed for move/init analysis: moves and assignments.
|
||||
fn emit_move_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
move_data: &MoveData<'_>,
|
||||
location_table: &LocationTable,
|
||||
facts: &mut AllFacts,
|
||||
body: &Body<'_>,
|
||||
location_table: &LocationTable,
|
||||
move_data: &MoveData<'_>,
|
||||
) {
|
||||
all_facts
|
||||
.path_is_var
|
||||
.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
facts.path_is_var.extend(move_data.rev_lookup.iter_locals_enumerated().map(|(l, r)| (r, l)));
|
||||
|
||||
for (child, move_path) in move_data.move_paths.iter_enumerated() {
|
||||
if let Some(parent) = move_path.parent {
|
||||
all_facts.child_path.push((child, parent));
|
||||
facts.child_path.push((child, parent));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -83,14 +100,14 @@ fn emit_move_facts(
|
|||
// The initialization happened in (or rather, when arriving at)
|
||||
// the successors, but not in the unwind block.
|
||||
let first_statement = Location { block: successor, statement_index: 0 };
|
||||
all_facts
|
||||
facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.start_index(first_statement)));
|
||||
}
|
||||
} else {
|
||||
// In all other cases, the initialization just happens at the
|
||||
// midpoint, like any other effect.
|
||||
all_facts
|
||||
facts
|
||||
.path_assigned_at_base
|
||||
.push((init.path, location_table.mid_index(location)));
|
||||
}
|
||||
|
|
@ -98,7 +115,7 @@ fn emit_move_facts(
|
|||
// Arguments are initialized on function entry
|
||||
InitLocation::Argument(local) => {
|
||||
assert!(body.local_kind(local) == LocalKind::Arg);
|
||||
all_facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
facts.path_assigned_at_base.push((init.path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -107,20 +124,20 @@ fn emit_move_facts(
|
|||
if body.local_kind(local) != LocalKind::Arg {
|
||||
// Non-arguments start out deinitialised; we simulate this with an
|
||||
// initial move:
|
||||
all_facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
facts.path_moved_at_base.push((path, fn_entry_start));
|
||||
}
|
||||
}
|
||||
|
||||
// moved_out_at
|
||||
// deinitialisation is assumed to always happen!
|
||||
all_facts
|
||||
facts
|
||||
.path_moved_at_base
|
||||
.extend(move_data.moves.iter().map(|mo| (mo.path, location_table.mid_index(mo.source))));
|
||||
}
|
||||
|
||||
/// Emit universal regions facts, and their relations.
|
||||
fn emit_universal_region_facts(
|
||||
all_facts: &mut AllFacts,
|
||||
facts: &mut AllFacts,
|
||||
borrow_set: &BorrowSet<'_>,
|
||||
universal_region_relations: &UniversalRegionRelations<'_>,
|
||||
) {
|
||||
|
|
@ -131,7 +148,7 @@ fn emit_universal_region_facts(
|
|||
// added to the existing number of loans, as if they succeeded them in the set.
|
||||
//
|
||||
let universal_regions = &universal_region_relations.universal_regions;
|
||||
all_facts
|
||||
facts
|
||||
.universal_region
|
||||
.extend(universal_regions.universal_regions_iter().map(PoloniusRegionVid::from));
|
||||
let borrow_count = borrow_set.len();
|
||||
|
|
@ -144,7 +161,7 @@ fn emit_universal_region_facts(
|
|||
for universal_region in universal_regions.universal_regions_iter() {
|
||||
let universal_region_idx = universal_region.index();
|
||||
let placeholder_loan_idx = borrow_count + universal_region_idx;
|
||||
all_facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
|
||||
facts.placeholder.push((universal_region.into(), placeholder_loan_idx.into()));
|
||||
}
|
||||
|
||||
// 2: the universal region relations `outlives` constraints are emitted as
|
||||
|
|
@ -156,29 +173,51 @@ fn emit_universal_region_facts(
|
|||
fr1={:?}, fr2={:?}",
|
||||
fr1, fr2
|
||||
);
|
||||
all_facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
|
||||
facts.known_placeholder_subset.push((fr1.into(), fr2.into()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Emit facts about loan invalidations.
|
||||
fn emit_loan_invalidations_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
/// For every potentially drop()-touched region `region` in `local`'s type
|
||||
/// (`kind`), emit a `drop_of_var_derefs_origin(local, origin)` fact.
|
||||
pub(crate) fn emit_drop_facts<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
local: Local,
|
||||
kind: &GenericArg<'tcx>,
|
||||
universal_regions: &UniversalRegions<'tcx>,
|
||||
all_facts: &mut Option<AllFacts>,
|
||||
) {
|
||||
loan_invalidations::emit_loan_invalidations(tcx, all_facts, location_table, body, borrow_set);
|
||||
debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind);
|
||||
let Some(facts) = all_facts.as_mut() else { return };
|
||||
let _prof_timer = tcx.prof.generic_activity("polonius_fact_generation");
|
||||
tcx.for_each_free_region(kind, |drop_live_region| {
|
||||
let region_vid = universal_regions.to_region_vid(drop_live_region);
|
||||
facts.drop_of_var_derefs_origin.push((local, region_vid.into()));
|
||||
});
|
||||
}
|
||||
|
||||
/// Emit facts about CFG points and edges, as well as locations where loans are killed.
|
||||
fn emit_cfg_and_loan_kills_facts<'tcx>(
|
||||
all_facts: &mut AllFacts,
|
||||
tcx: TyCtxt<'tcx>,
|
||||
/// Emit facts about the outlives constraints: the `subset` base relation, i.e. not a transitive
|
||||
/// closure.
|
||||
fn emit_outlives_facts<'tcx>(
|
||||
facts: &mut AllFacts,
|
||||
location_table: &LocationTable,
|
||||
body: &Body<'tcx>,
|
||||
borrow_set: &BorrowSet<'tcx>,
|
||||
constraints: &MirTypeckRegionConstraints<'tcx>,
|
||||
) {
|
||||
loan_kills::emit_loan_kills(tcx, all_facts, location_table, body, borrow_set);
|
||||
facts.subset_base.extend(constraints.outlives_constraints.outlives().iter().flat_map(
|
||||
|constraint: &OutlivesConstraint<'_>| {
|
||||
if let Some(from_location) = constraint.locations.from_location() {
|
||||
Either::Left(iter::once((
|
||||
constraint.sup.into(),
|
||||
constraint.sub.into(),
|
||||
location_table.mid_index(from_location),
|
||||
)))
|
||||
} else {
|
||||
Either::Right(
|
||||
location_table.all_points().map(move |location| {
|
||||
(constraint.sup.into(), constraint.sub.into(), location)
|
||||
}),
|
||||
)
|
||||
}
|
||||
},
|
||||
));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ use crate::region_infer::values::LivenessValues;
|
|||
use crate::universal_regions::UniversalRegions;
|
||||
|
||||
mod local_use_map;
|
||||
mod polonius;
|
||||
mod trace;
|
||||
|
||||
/// Combines liveness analysis with initialization analysis to
|
||||
|
|
@ -45,8 +44,6 @@ pub(super) fn generate<'a, 'tcx>(
|
|||
let (relevant_live_locals, boring_locals) =
|
||||
compute_relevant_live_locals(typeck.tcx(), &free_regions, body);
|
||||
|
||||
polonius::emit_access_facts(typeck, body, move_data);
|
||||
|
||||
trace::trace(
|
||||
typeck,
|
||||
body,
|
||||
|
|
|
|||
|
|
@ -1,123 +0,0 @@
|
|||
use rustc_middle::mir::visit::{MutatingUseContext, PlaceContext, Visitor};
|
||||
use rustc_middle::mir::{Body, Local, Location, Place};
|
||||
use rustc_middle::ty::GenericArg;
|
||||
use rustc_mir_dataflow::move_paths::{LookupResult, MoveData, MovePathIndex};
|
||||
use tracing::debug;
|
||||
|
||||
use super::TypeChecker;
|
||||
use crate::def_use::{self, DefUse};
|
||||
use crate::location::{LocationIndex, LocationTable};
|
||||
|
||||
type VarPointRelation = Vec<(Local, LocationIndex)>;
|
||||
type PathPointRelation = Vec<(MovePathIndex, LocationIndex)>;
|
||||
|
||||
/// Emit polonius facts for variable defs, uses, drops, and path accesses.
|
||||
pub(super) fn emit_access_facts<'a, 'tcx>(
|
||||
typeck: &mut TypeChecker<'a, 'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
move_data: &MoveData<'tcx>,
|
||||
) {
|
||||
if let Some(facts) = typeck.all_facts.as_mut() {
|
||||
debug!("emit_access_facts()");
|
||||
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let location_table = typeck.location_table;
|
||||
|
||||
let mut extractor = AccessFactsExtractor {
|
||||
var_defined_at: &mut facts.var_defined_at,
|
||||
var_used_at: &mut facts.var_used_at,
|
||||
var_dropped_at: &mut facts.var_dropped_at,
|
||||
path_accessed_at_base: &mut facts.path_accessed_at_base,
|
||||
location_table,
|
||||
move_data,
|
||||
};
|
||||
extractor.visit_body(body);
|
||||
|
||||
for (local, local_decl) in body.local_decls.iter_enumerated() {
|
||||
debug!(
|
||||
"add use_of_var_derefs_origin facts - local={:?}, type={:?}",
|
||||
local, local_decl.ty
|
||||
);
|
||||
let universal_regions = &typeck.universal_regions;
|
||||
typeck.infcx.tcx.for_each_free_region(&local_decl.ty, |region| {
|
||||
let region_vid = universal_regions.to_region_vid(region);
|
||||
facts.use_of_var_derefs_origin.push((local, region_vid.into()));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// For every potentially drop()-touched region `region` in `local`'s type
|
||||
/// (`kind`), emit a Polonius `use_of_var_derefs_origin(local, origin)` fact.
|
||||
pub(super) fn emit_drop_facts<'tcx>(
|
||||
typeck: &mut TypeChecker<'_, 'tcx>,
|
||||
local: Local,
|
||||
kind: &GenericArg<'tcx>,
|
||||
) {
|
||||
debug!("emit_drop_facts(local={:?}, kind={:?}", local, kind);
|
||||
if let Some(facts) = typeck.all_facts.as_mut() {
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let universal_regions = &typeck.universal_regions;
|
||||
typeck.infcx.tcx.for_each_free_region(kind, |drop_live_region| {
|
||||
let region_vid = universal_regions.to_region_vid(drop_live_region);
|
||||
facts.drop_of_var_derefs_origin.push((local, region_vid.into()));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// MIR visitor extracting point-wise facts about accesses.
|
||||
struct AccessFactsExtractor<'a, 'tcx> {
|
||||
var_defined_at: &'a mut VarPointRelation,
|
||||
var_used_at: &'a mut VarPointRelation,
|
||||
location_table: &'a LocationTable,
|
||||
var_dropped_at: &'a mut VarPointRelation,
|
||||
move_data: &'a MoveData<'tcx>,
|
||||
path_accessed_at_base: &'a mut PathPointRelation,
|
||||
}
|
||||
|
||||
impl<'tcx> AccessFactsExtractor<'_, 'tcx> {
|
||||
fn location_to_index(&self, location: Location) -> LocationIndex {
|
||||
self.location_table.mid_index(location)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Visitor<'tcx> for AccessFactsExtractor<'a, 'tcx> {
|
||||
fn visit_local(&mut self, local: Local, context: PlaceContext, location: Location) {
|
||||
match def_use::categorize(context) {
|
||||
Some(DefUse::Def) => {
|
||||
debug!("AccessFactsExtractor - emit def");
|
||||
self.var_defined_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Use) => {
|
||||
debug!("AccessFactsExtractor - emit use");
|
||||
self.var_used_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
Some(DefUse::Drop) => {
|
||||
debug!("AccessFactsExtractor - emit drop");
|
||||
self.var_dropped_at.push((local, self.location_to_index(location)));
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
|
||||
self.super_place(place, context, location);
|
||||
|
||||
match context {
|
||||
PlaceContext::NonMutatingUse(_)
|
||||
| PlaceContext::MutatingUse(MutatingUseContext::Borrow) => {
|
||||
let path = match self.move_data.rev_lookup.find(place.as_ref()) {
|
||||
LookupResult::Exact(path) | LookupResult::Parent(Some(path)) => path,
|
||||
_ => {
|
||||
// There's no path access to emit.
|
||||
return;
|
||||
}
|
||||
};
|
||||
debug!("AccessFactsExtractor - emit path access ({path:?}, {location:?})");
|
||||
self.path_accessed_at_base.push((path, self.location_to_index(location)));
|
||||
}
|
||||
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -15,9 +15,9 @@ use rustc_trait_selection::traits::query::type_op::{DropckOutlives, TypeOp, Type
|
|||
use tracing::debug;
|
||||
|
||||
use crate::location::RichLocation;
|
||||
use crate::polonius;
|
||||
use crate::region_infer::values::{self, LiveLoans};
|
||||
use crate::type_check::liveness::local_use_map::LocalUseMap;
|
||||
use crate::type_check::liveness::polonius;
|
||||
use crate::type_check::{NormalizeLocation, TypeChecker};
|
||||
|
||||
/// This is the heart of the liveness computation. For each variable X
|
||||
|
|
@ -590,7 +590,13 @@ impl<'tcx> LivenessContext<'_, '_, '_, 'tcx> {
|
|||
// the destructor and must be live at this point.
|
||||
for &kind in &drop_data.dropck_result.kinds {
|
||||
Self::make_all_regions_live(self.elements, self.typeck, kind, live_at);
|
||||
polonius::emit_drop_facts(self.typeck, dropped_local, &kind);
|
||||
polonius::legacy::emit_drop_facts(
|
||||
self.typeck.tcx(),
|
||||
dropped_local,
|
||||
&kind,
|
||||
self.typeck.universal_regions,
|
||||
self.typeck.all_facts,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,6 @@
|
|||
use std::rc::Rc;
|
||||
use std::{fmt, iter, mem};
|
||||
|
||||
use either::Either;
|
||||
use rustc_abi::{FIRST_VARIANT, FieldIdx};
|
||||
use rustc_data_structures::frozen::Frozen;
|
||||
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
|
||||
|
|
@ -107,7 +106,6 @@ mod relate_tys;
|
|||
/// # Parameters
|
||||
///
|
||||
/// - `infcx` -- inference context to use
|
||||
/// - `param_env` -- parameter environment to use for trait solving
|
||||
/// - `body` -- MIR body to type-check
|
||||
/// - `promoted` -- map of promoted constants within `body`
|
||||
/// - `universal_regions` -- the universal regions from `body`s function signature
|
||||
|
|
@ -155,7 +153,7 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
|
||||
debug!(?normalized_inputs_and_output);
|
||||
|
||||
let mut checker = TypeChecker {
|
||||
let mut typeck = TypeChecker {
|
||||
infcx,
|
||||
last_span: body.span,
|
||||
body,
|
||||
|
|
@ -171,24 +169,22 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
constraints: &mut constraints,
|
||||
};
|
||||
|
||||
checker.check_user_type_annotations();
|
||||
typeck.check_user_type_annotations();
|
||||
|
||||
let mut verifier = TypeVerifier { cx: &mut checker, promoted, last_span: body.span };
|
||||
let mut verifier = TypeVerifier { typeck: &mut typeck, promoted, last_span: body.span };
|
||||
verifier.visit_body(body);
|
||||
|
||||
checker.typeck_mir(body);
|
||||
checker.equate_inputs_and_outputs(body, &normalized_inputs_and_output);
|
||||
checker.check_signature_annotation(body);
|
||||
typeck.typeck_mir(body);
|
||||
typeck.equate_inputs_and_outputs(body, &normalized_inputs_and_output);
|
||||
typeck.check_signature_annotation(body);
|
||||
|
||||
liveness::generate(&mut checker, body, &elements, flow_inits, move_data);
|
||||
liveness::generate(&mut typeck, body, &elements, flow_inits, move_data);
|
||||
|
||||
translate_outlives_facts(&mut checker);
|
||||
let opaque_type_values = infcx.take_opaque_types();
|
||||
|
||||
let opaque_type_values = opaque_type_values
|
||||
let opaque_type_values = infcx
|
||||
.take_opaque_types()
|
||||
.into_iter()
|
||||
.map(|(opaque_type_key, decl)| {
|
||||
let _: Result<_, ErrorGuaranteed> = checker.fully_perform_op(
|
||||
let _: Result<_, ErrorGuaranteed> = typeck.fully_perform_op(
|
||||
Locations::All(body.span),
|
||||
ConstraintCategory::OpaqueType,
|
||||
CustomTypeOp::new(
|
||||
|
|
@ -218,11 +214,11 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
match region.kind() {
|
||||
ty::ReVar(_) => region,
|
||||
ty::RePlaceholder(placeholder) => {
|
||||
checker.constraints.placeholder_region(infcx, placeholder)
|
||||
typeck.constraints.placeholder_region(infcx, placeholder)
|
||||
}
|
||||
_ => ty::Region::new_var(
|
||||
infcx.tcx,
|
||||
checker.universal_regions.to_region_vid(region),
|
||||
typeck.universal_regions.to_region_vid(region),
|
||||
),
|
||||
}
|
||||
});
|
||||
|
|
@ -234,30 +230,6 @@ pub(crate) fn type_check<'a, 'tcx>(
|
|||
MirTypeckResults { constraints, universal_region_relations, opaque_type_values }
|
||||
}
|
||||
|
||||
fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) {
|
||||
if let Some(facts) = typeck.all_facts {
|
||||
let _prof_timer = typeck.infcx.tcx.prof.generic_activity("polonius_fact_generation");
|
||||
let location_table = typeck.location_table;
|
||||
facts.subset_base.extend(
|
||||
typeck.constraints.outlives_constraints.outlives().iter().flat_map(
|
||||
|constraint: &OutlivesConstraint<'_>| {
|
||||
if let Some(from_location) = constraint.locations.from_location() {
|
||||
Either::Left(iter::once((
|
||||
constraint.sup.into(),
|
||||
constraint.sub.into(),
|
||||
location_table.mid_index(from_location),
|
||||
)))
|
||||
} else {
|
||||
Either::Right(location_table.all_points().map(move |location| {
|
||||
(constraint.sup.into(), constraint.sub.into(), location)
|
||||
}))
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn mirbug(tcx: TyCtxt<'_>, span: Span, msg: String) {
|
||||
// We sometimes see MIR failures (notably predicate failures) due to
|
||||
|
|
@ -276,7 +248,7 @@ enum FieldAccessError {
|
|||
/// type, calling `span_mirbug` and returning an error type if there
|
||||
/// is a problem.
|
||||
struct TypeVerifier<'a, 'b, 'tcx> {
|
||||
cx: &'a mut TypeChecker<'b, 'tcx>,
|
||||
typeck: &'a mut TypeChecker<'b, 'tcx>,
|
||||
promoted: &'b IndexSlice<Promoted, Body<'tcx>>,
|
||||
last_span: Span,
|
||||
}
|
||||
|
|
@ -298,9 +270,9 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
self.super_const_operand(constant, location);
|
||||
let ty = self.sanitize_type(constant, constant.const_.ty());
|
||||
|
||||
self.cx.infcx.tcx.for_each_free_region(&ty, |live_region| {
|
||||
let live_region_vid = self.cx.universal_regions.to_region_vid(live_region);
|
||||
self.cx.constraints.liveness_constraints.add_location(live_region_vid, location);
|
||||
self.typeck.infcx.tcx.for_each_free_region(&ty, |live_region| {
|
||||
let live_region_vid = self.typeck.universal_regions.to_region_vid(live_region);
|
||||
self.typeck.constraints.liveness_constraints.add_location(live_region_vid, location);
|
||||
});
|
||||
|
||||
// HACK(compiler-errors): Constants that are gathered into Body.required_consts
|
||||
|
|
@ -312,14 +284,14 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(annotation_index) = constant.user_ty {
|
||||
if let Err(terr) = self.cx.relate_type_and_user_type(
|
||||
if let Err(terr) = self.typeck.relate_type_and_user_type(
|
||||
constant.const_.ty(),
|
||||
ty::Invariant,
|
||||
&UserTypeProjection { base: annotation_index, projs: vec![] },
|
||||
locations,
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
let annotation = &self.cx.user_type_annotations[annotation_index];
|
||||
let annotation = &self.typeck.user_type_annotations[annotation_index];
|
||||
span_mirbug!(
|
||||
self,
|
||||
constant,
|
||||
|
|
@ -348,9 +320,12 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
promoted: &Body<'tcx>,
|
||||
ty,
|
||||
san_ty| {
|
||||
if let Err(terr) =
|
||||
verifier.cx.eq_types(ty, san_ty, locations, ConstraintCategory::Boring)
|
||||
{
|
||||
if let Err(terr) = verifier.typeck.eq_types(
|
||||
ty,
|
||||
san_ty,
|
||||
locations,
|
||||
ConstraintCategory::Boring,
|
||||
) {
|
||||
span_mirbug!(
|
||||
verifier,
|
||||
promoted,
|
||||
|
|
@ -368,21 +343,21 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
let promoted_ty = promoted_body.return_ty();
|
||||
check_err(self, promoted_body, ty, promoted_ty);
|
||||
} else {
|
||||
self.cx.ascribe_user_type(
|
||||
self.typeck.ascribe_user_type(
|
||||
constant.const_.ty(),
|
||||
ty::UserType::new(ty::UserTypeKind::TypeOf(uv.def, UserArgs {
|
||||
args: uv.args,
|
||||
user_self_ty: None,
|
||||
})),
|
||||
locations.span(self.cx.body),
|
||||
locations.span(self.typeck.body),
|
||||
);
|
||||
}
|
||||
} else if let Some(static_def_id) = constant.check_static_ptr(tcx) {
|
||||
let unnormalized_ty = tcx.type_of(static_def_id).instantiate_identity();
|
||||
let normalized_ty = self.cx.normalize(unnormalized_ty, locations);
|
||||
let normalized_ty = self.typeck.normalize(unnormalized_ty, locations);
|
||||
let literal_ty = constant.const_.ty().builtin_deref(true).unwrap();
|
||||
|
||||
if let Err(terr) = self.cx.eq_types(
|
||||
if let Err(terr) = self.typeck.eq_types(
|
||||
literal_ty,
|
||||
normalized_ty,
|
||||
locations,
|
||||
|
|
@ -394,7 +369,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
|
||||
if let ty::FnDef(def_id, args) = *constant.const_.ty().kind() {
|
||||
let instantiated_predicates = tcx.predicates_of(def_id).instantiate(tcx, args);
|
||||
self.cx.normalize_and_prove_instantiated_predicates(
|
||||
self.typeck.normalize_and_prove_instantiated_predicates(
|
||||
def_id,
|
||||
instantiated_predicates,
|
||||
locations,
|
||||
|
|
@ -404,7 +379,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
tcx.impl_of_method(def_id).map(|imp| tcx.def_kind(imp)),
|
||||
Some(DefKind::Impl { of_trait: true })
|
||||
));
|
||||
self.cx.prove_predicates(
|
||||
self.typeck.prove_predicates(
|
||||
args.types().map(|ty| ty::ClauseKind::WellFormed(ty.into())),
|
||||
locations,
|
||||
ConstraintCategory::Boring,
|
||||
|
|
@ -438,7 +413,7 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
local_decl.ty
|
||||
};
|
||||
|
||||
if let Err(terr) = self.cx.relate_type_and_user_type(
|
||||
if let Err(terr) = self.typeck.relate_type_and_user_type(
|
||||
ty,
|
||||
ty::Invariant,
|
||||
user_ty,
|
||||
|
|
@ -468,11 +443,11 @@ impl<'a, 'b, 'tcx> Visitor<'tcx> for TypeVerifier<'a, 'b, 'tcx> {
|
|||
|
||||
impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
||||
fn body(&self) -> &Body<'tcx> {
|
||||
self.cx.body
|
||||
self.typeck.body
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.cx.infcx.tcx
|
||||
self.typeck.infcx.tcx
|
||||
}
|
||||
|
||||
fn sanitize_type(&mut self, parent: &dyn fmt::Debug, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
|
|
@ -522,7 +497,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
// whether the bounds fully apply: in effect, the rule is
|
||||
// that if a value of some type could implement `Copy`, then
|
||||
// it must.
|
||||
self.cx.prove_trait_ref(
|
||||
self.typeck.prove_trait_ref(
|
||||
trait_ref,
|
||||
location.to_locations(),
|
||||
ConstraintCategory::CopyBound,
|
||||
|
|
@ -537,7 +512,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
// checker on the promoted MIR, then transfer the constraints back to
|
||||
// the main MIR, changing the locations to the provided location.
|
||||
|
||||
let parent_body = mem::replace(&mut self.cx.body, promoted_body);
|
||||
let parent_body = mem::replace(&mut self.typeck.body, promoted_body);
|
||||
|
||||
// Use new sets of constraints and closure bounds so that we can
|
||||
// modify their locations.
|
||||
|
|
@ -548,18 +523,18 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
// Don't try to add borrow_region facts for the promoted MIR
|
||||
|
||||
let mut swap_constraints = |this: &mut Self| {
|
||||
mem::swap(this.cx.all_facts, all_facts);
|
||||
mem::swap(&mut this.cx.constraints.outlives_constraints, &mut constraints);
|
||||
mem::swap(&mut this.cx.constraints.liveness_constraints, &mut liveness_constraints);
|
||||
mem::swap(this.typeck.all_facts, all_facts);
|
||||
mem::swap(&mut this.typeck.constraints.outlives_constraints, &mut constraints);
|
||||
mem::swap(&mut this.typeck.constraints.liveness_constraints, &mut liveness_constraints);
|
||||
};
|
||||
|
||||
swap_constraints(self);
|
||||
|
||||
self.visit_body(promoted_body);
|
||||
|
||||
self.cx.typeck_mir(promoted_body);
|
||||
self.typeck.typeck_mir(promoted_body);
|
||||
|
||||
self.cx.body = parent_body;
|
||||
self.typeck.body = parent_body;
|
||||
// Merge the outlives constraints back in, at the given location.
|
||||
swap_constraints(self);
|
||||
|
||||
|
|
@ -575,7 +550,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
// temporary from the user's point of view.
|
||||
constraint.category = ConstraintCategory::Boring;
|
||||
}
|
||||
self.cx.constraints.outlives_constraints.push(constraint)
|
||||
self.typeck.constraints.outlives_constraints.push(constraint)
|
||||
}
|
||||
// If the region is live at least one location in the promoted MIR,
|
||||
// then add a liveness constraint to the main MIR for this region
|
||||
|
|
@ -585,7 +560,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
// unordered.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
for region in liveness_constraints.live_regions_unordered() {
|
||||
self.cx.constraints.liveness_constraints.add_location(region, location);
|
||||
self.typeck.constraints.liveness_constraints.add_location(region, location);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -669,13 +644,13 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
},
|
||||
ProjectionElem::Field(field, fty) => {
|
||||
let fty = self.sanitize_type(place, fty);
|
||||
let fty = self.cx.normalize(fty, location);
|
||||
let fty = self.typeck.normalize(fty, location);
|
||||
match self.field_ty(place, base, field, location) {
|
||||
Ok(ty) => {
|
||||
let ty = self.cx.normalize(ty, location);
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
debug!(?fty, ?ty);
|
||||
|
||||
if let Err(terr) = self.cx.relate_types(
|
||||
if let Err(terr) = self.typeck.relate_types(
|
||||
ty,
|
||||
self.get_ambient_variance(context),
|
||||
fty,
|
||||
|
|
@ -707,8 +682,8 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
}
|
||||
ProjectionElem::OpaqueCast(ty) => {
|
||||
let ty = self.sanitize_type(place, ty);
|
||||
let ty = self.cx.normalize(ty, location);
|
||||
self.cx
|
||||
let ty = self.typeck.normalize(ty, location);
|
||||
self.typeck
|
||||
.relate_types(
|
||||
ty,
|
||||
self.get_ambient_variance(context),
|
||||
|
|
@ -817,7 +792,7 @@ impl<'a, 'b, 'tcx> TypeVerifier<'a, 'b, 'tcx> {
|
|||
};
|
||||
|
||||
if let Some(field) = variant.fields.get(field) {
|
||||
Ok(self.cx.normalize(field.ty(tcx, args), location))
|
||||
Ok(self.typeck.normalize(field.ty(tcx, args), location))
|
||||
} else {
|
||||
Err(FieldAccessError::OutOfRange { field_count: variant.fields.len() })
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue