From 75ac0cca3678d5d309c737f1bd8ef237628a278b Mon Sep 17 00:00:00 2001 From: Matthew Jasper Date: Wed, 25 Dec 2019 09:50:07 +0000 Subject: [PATCH] Prepare to use borrowck to resolve opaque types --- src/librustc/mir/query.rs | 8 +- src/librustc_mir/borrow_check/mod.rs | 32 +++--- src/librustc_mir/borrow_check/nll.rs | 32 +++--- .../borrow_check/type_check/mod.rs | 101 +++++++++++------- src/librustc_typeck/collect.rs | 23 +++- 5 files changed, 128 insertions(+), 68 deletions(-) diff --git a/src/librustc/mir/query.rs b/src/librustc/mir/query.rs index 34f58ab89b1..824cdfe55bf 100644 --- a/src/librustc/mir/query.rs +++ b/src/librustc/mir/query.rs @@ -1,8 +1,10 @@ //! Values computed by queries that use MIR. use crate::ty::{self, Ty}; +use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::sync::Lrc; use rustc_hir as hir; +use rustc_hir::def_id::DefId; use rustc_index::bit_set::BitMatrix; use rustc_index::vec::IndexVec; use rustc_span::{Span, Symbol}; @@ -59,8 +61,12 @@ pub struct GeneratorLayout<'tcx> { pub storage_conflicts: BitMatrix, } -#[derive(Clone, Debug, RustcEncodable, RustcDecodable, HashStable)] +#[derive(Debug, RustcEncodable, RustcDecodable, HashStable)] pub struct BorrowCheckResult<'tcx> { + /// All the opaque types that are restricted to concrete types + /// by this function. Unlike the value in `TypeckTables`, this has + /// unerased regions. + pub concrete_opaque_types: FxHashMap>, pub closure_requirements: Option>, pub used_mut_upvars: SmallVec<[Field; 8]>, } diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index 07d3a12fb20..1843d4bf72d 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -195,19 +195,24 @@ fn do_mir_borrowck<'a, 'tcx>( Rc::new(BorrowSet::build(tcx, body, locals_are_invalidated_at_exit, &mdpe.move_data)); // Compute non-lexical lifetimes. - let nll::NllOutput { regioncx, polonius_output, opt_closure_req, nll_errors } = - nll::compute_regions( - infcx, - def_id, - free_regions, - body, - &promoted, - location_table, - param_env, - &mut flow_inits, - &mdpe.move_data, - &borrow_set, - ); + let nll::NllOutput { + regioncx, + opaque_type_values, + polonius_output, + opt_closure_req, + nll_errors, + } = nll::compute_regions( + infcx, + def_id, + free_regions, + body, + &promoted, + location_table, + param_env, + &mut flow_inits, + &mdpe.move_data, + &borrow_set, + ); // Dump MIR results into a file, if that is enabled. This let us // write unit-tests, as well as helping with debugging. @@ -389,6 +394,7 @@ fn do_mir_borrowck<'a, 'tcx>( } let result = BorrowCheckResult { + concrete_opaque_types: opaque_type_values, closure_requirements: opt_closure_req, used_mut_upvars: mbcx.used_mut_upvars, }; diff --git a/src/librustc_mir/borrow_check/nll.rs b/src/librustc_mir/borrow_check/nll.rs index a71dfc9a778..4acf1cf7c2b 100644 --- a/src/librustc_mir/borrow_check/nll.rs +++ b/src/librustc_mir/borrow_check/nll.rs @@ -6,6 +6,7 @@ use rustc::mir::{ Location, Promoted, ReadOnlyBodyAndCache, }; use rustc::ty::{self, RegionKind, RegionVid}; +use rustc_data_structures::fx::FxHashMap; use rustc_errors::Diagnostic; use rustc_hir::def_id::DefId; use rustc_index::vec::IndexVec; @@ -46,6 +47,7 @@ crate type PoloniusOutput = Output; /// closure requirements to propagate, and any generated errors. crate struct NllOutput<'tcx> { pub regioncx: RegionInferenceContext<'tcx>, + pub opaque_type_values: FxHashMap>, pub polonius_output: Option>, pub opt_closure_req: Option>, pub nll_errors: RegionErrors<'tcx>, @@ -160,20 +162,21 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( let elements = &Rc::new(RegionValueElements::new(&body)); // Run the MIR type-checker. - let MirTypeckResults { constraints, universal_region_relations } = type_check::type_check( - infcx, - param_env, - body, - promoted, - def_id, - &universal_regions, - location_table, - borrow_set, - &mut all_facts, - flow_inits, - move_data, - elements, - ); + let MirTypeckResults { constraints, universal_region_relations, opaque_type_values } = + type_check::type_check( + infcx, + param_env, + body, + promoted, + def_id, + &universal_regions, + location_table, + borrow_set, + &mut all_facts, + flow_inits, + move_data, + elements, + ); if let Some(all_facts) = &mut all_facts { let _prof_timer = infcx.tcx.prof.generic_activity("polonius_fact_generation"); @@ -281,6 +284,7 @@ pub(in crate::borrow_check) fn compute_regions<'cx, 'tcx>( NllOutput { regioncx, + opaque_type_values, polonius_output, opt_closure_req: closure_region_requirements, nll_errors, diff --git a/src/librustc_mir/borrow_check/type_check/mod.rs b/src/librustc_mir/borrow_check/type_check/mod.rs index 67dbf8a2b6d..c6809f9e37e 100644 --- a/src/librustc_mir/borrow_check/type_check/mod.rs +++ b/src/librustc_mir/borrow_check/type_check/mod.rs @@ -158,7 +158,7 @@ pub(crate) fn type_check<'mir, 'tcx>( constraints: &mut constraints, }; - type_check_internal( + let opaque_type_values = type_check_internal( infcx, mir_def_id, param_env, @@ -173,10 +173,11 @@ pub(crate) fn type_check<'mir, 'tcx>( liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table); translate_outlives_facts(&mut cx); + cx.opaque_type_values }, ); - MirTypeckResults { constraints, universal_region_relations } + MirTypeckResults { constraints, universal_region_relations, opaque_type_values } } fn type_check_internal<'a, 'tcx, R>( @@ -189,7 +190,7 @@ fn type_check_internal<'a, 'tcx, R>( implicit_region_bound: ty::Region<'tcx>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, universal_region_relations: &'a UniversalRegionRelations<'tcx>, - mut extra: impl FnMut(&mut TypeChecker<'a, 'tcx>) -> R, + extra: impl FnOnce(TypeChecker<'a, 'tcx>) -> R, ) -> R { let mut checker = TypeChecker::new( infcx, @@ -212,7 +213,7 @@ fn type_check_internal<'a, 'tcx, R>( checker.typeck_mir(body); } - extra(&mut checker) + extra(checker) } fn translate_outlives_facts(typeck: &mut TypeChecker<'_, '_>) { @@ -799,6 +800,7 @@ struct TypeChecker<'a, 'tcx> { reported_errors: FxHashSet<(Ty<'tcx>, Span)>, borrowck_context: &'a mut BorrowCheckContext<'a, 'tcx>, universal_region_relations: &'a UniversalRegionRelations<'tcx>, + opaque_type_values: FxHashMap>, } struct BorrowCheckContext<'a, 'tcx> { @@ -812,6 +814,7 @@ struct BorrowCheckContext<'a, 'tcx> { crate struct MirTypeckResults<'tcx> { crate constraints: MirTypeckRegionConstraints<'tcx>, crate universal_region_relations: Rc>, + crate opaque_type_values: FxHashMap>, } /// A collection of region constraints that must be satisfied for the @@ -958,6 +961,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { borrowck_context, reported_errors: Default::default(), universal_region_relations, + opaque_type_values: FxHashMap::default(), }; checker.check_user_type_annotations(); checker @@ -1195,6 +1199,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let tcx = infcx.tcx; let param_env = self.param_env; let body = self.body; + let concrete_opaque_types = &tcx.typeck_tables_of(anon_owner_def_id).concrete_opaque_types; + let mut opaque_type_values = Vec::new(); + debug!("eq_opaque_type_and_type: mir_def_id={:?}", self.mir_def_id); let opaque_type_map = self.fully_perform_op( locations, @@ -1226,47 +1233,65 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ); for (&opaque_def_id, opaque_decl) in &opaque_type_map { - let opaque_defn_ty = tcx.type_of(opaque_def_id); - let opaque_defn_ty = opaque_defn_ty.subst(tcx, opaque_decl.substs); - let opaque_defn_ty = renumber::renumber_regions(infcx, &opaque_defn_ty); - let concrete_is_opaque = infcx - .resolve_vars_if_possible(&opaque_decl.concrete_ty) - .is_impl_trait(); + let resolved_ty = infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty); + let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind { + def_id == opaque_def_id + } else { + false + }; + let opaque_defn_ty = match concrete_opaque_types.get(&opaque_def_id) { + None => { + assert!( + concrete_is_opaque, + "Non-defining use of {:?} with revealed type", + opaque_def_id, + ); + continue; + } + Some(opaque_defn_ty) => opaque_defn_ty, + }; + debug!("opaque_defn_ty = {:?}", opaque_defn_ty); + let subst_opaque_defn_ty = + opaque_defn_ty.concrete_type.subst(tcx, opaque_decl.substs); + let renumbered_opaque_defn_ty = + renumber::renumber_regions(infcx, &subst_opaque_defn_ty); debug!( - "eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?} \ - concrete_is_opaque={}", - opaque_decl.concrete_ty, - infcx.resolve_vars_if_possible(&opaque_decl.concrete_ty), - opaque_defn_ty, - concrete_is_opaque + "eq_opaque_type_and_type: concrete_ty={:?}={:?} opaque_defn_ty={:?}", + opaque_decl.concrete_ty, resolved_ty, renumbered_opaque_defn_ty, ); - // concrete_is_opaque is `true` when we're using an opaque `impl Trait` - // type without 'revealing' it. For example, code like this: - // - // type Foo = impl Debug; - // fn foo1() -> Foo { ... } - // fn foo2() -> Foo { foo1() } - // - // In `foo2`, we're not revealing the type of `Foo` - we're - // just treating it as the opaque type. - // - // When this occurs, we do *not* want to try to equate - // the concrete type with the underlying defining type - // of the opaque type - this will always fail, since - // the defining type of an opaque type is always - // some other type (e.g. not itself) - // Essentially, none of the normal obligations apply here - - // we're just passing around some unknown opaque type, - // without actually looking at the underlying type it - // gets 'revealed' into - if !concrete_is_opaque { obligations.add( infcx .at(&ObligationCause::dummy(), param_env) - .eq(opaque_decl.concrete_ty, opaque_defn_ty)?, + .eq(opaque_decl.concrete_ty, renumbered_opaque_defn_ty)?, + ); + opaque_type_values + .push((opaque_def_id, ty::ResolvedOpaqueTy { ..*opaque_defn_ty })); + } else { + // We're using an opaque `impl Trait` type without + // 'revealing' it. For example, code like this: + // + // type Foo = impl Debug; + // fn foo1() -> Foo { ... } + // fn foo2() -> Foo { foo1() } + // + // In `foo2`, we're not revealing the type of `Foo` - we're + // just treating it as the opaque type. + // + // When this occurs, we do *not* want to try to equate + // the concrete type with the underlying defining type + // of the opaque type - this will always fail, since + // the defining type of an opaque type is always + // some other type (e.g. not itself) + // Essentially, none of the normal obligations apply here - + // we're just passing around some unknown opaque type, + // without actually looking at the underlying type it + // gets 'revealed' into + debug!( + "eq_opaque_type_and_type: non-defining use of {:?}", + opaque_def_id, ); } } @@ -1282,6 +1307,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { ), )?; + self.opaque_type_values.extend(opaque_type_values); + let universal_region_relations = self.universal_region_relations; // Finally, if we instantiated the anon types successfully, we diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 5349c324ad8..cf82789482b 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1464,7 +1464,7 @@ fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } // Opaque types desugared from `impl Trait`. ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: Some(owner), .. }) => { - tcx.typeck_tables_of(owner) + tcx.mir_borrowck(owner) .concrete_opaque_types .get(&def_id) .map(|opaque| opaque.concrete_type) @@ -1687,7 +1687,7 @@ fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { } fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { - use rustc_hir::{ImplItem, Item, TraitItem}; + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; debug!("find_opaque_ty_constraints({:?})", def_id); @@ -1713,7 +1713,17 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { ); return; } - let ty = self.tcx.typeck_tables_of(def_id).concrete_opaque_types.get(&self.def_id); + // Calling `mir_borrowck` can lead to cycle errors through + // const-checking, avoid calling it if we don't have to. + if !self.tcx.typeck_tables_of(def_id).concrete_opaque_types.contains_key(&self.def_id) { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", + self.def_id, def_id, + ); + return; + } + // Use borrowck to get the type with unerased regions. + let ty = self.tcx.mir_borrowck(def_id).concrete_opaque_types.get(&self.def_id); if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { debug!( "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}", @@ -1856,6 +1866,13 @@ fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { intravisit::NestedVisitorMap::All(&self.tcx.hir()) } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure(..) = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } fn visit_item(&mut self, it: &'tcx Item<'tcx>) { debug!("find_existential_constraints: visiting {:?}", it); let def_id = self.tcx.hir().local_def_id(it.hir_id);