Skip to content

Commit

Permalink
Auto merge of #67681 - matthewjasper:infer-regions-in-borrowck, r=nik…
Browse files Browse the repository at this point in the history
…omatsakis

Infer regions for opaque types in borrowck

This is a step towards the goal of typeck not doing region inference.

The commits up to `Arena allocate the result of mir_borrowck` are various bug fixes and prerequisites.
The remaining commits move opaque type inference to borrow checking.

r? @nikomatsakis
  • Loading branch information
bors committed Feb 15, 2020
2 parents b92c6ee + d863978 commit 19288dd
Show file tree
Hide file tree
Showing 72 changed files with 1,996 additions and 1,304 deletions.
3 changes: 2 additions & 1 deletion src/librustc/arena.rs
Expand Up @@ -35,7 +35,8 @@ macro_rules! arena_types {
rustc::mir::Promoted,
rustc::mir::BodyAndCache<$tcx>
>,
[] tables: rustc::ty::TypeckTables<$tcx>,
[decode] tables: rustc::ty::TypeckTables<$tcx>,
[decode] borrowck_result: rustc::mir::BorrowCheckResult<$tcx>,
[] const_allocs: rustc::mir::interpret::Allocation,
[] vtable_method: Option<(
rustc_hir::def_id::DefId,
Expand Down
6 changes: 2 additions & 4 deletions src/librustc/infer/error_reporting/mod.rs
Expand Up @@ -405,17 +405,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
}

RegionResolutionError::MemberConstraintFailure {
opaque_type_def_id,
hidden_ty,
member_region,
span: _,
choice_regions: _,
span,
} => {
let hidden_ty = self.resolve_vars_if_possible(&hidden_ty);
opaque_types::unexpected_hidden_region_diagnostic(
self.tcx,
Some(region_scope_tree),
opaque_type_def_id,
span,
hidden_ty,
member_region,
)
Expand Down
11 changes: 1 addition & 10 deletions src/librustc/infer/lexical_region_resolve/mod.rs
Expand Up @@ -18,7 +18,6 @@ use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::graph::implementation::{
Direction, Graph, NodeIndex, INCOMING, OUTGOING,
};
use rustc_hir::def_id::DefId;
use rustc_index::vec::{Idx, IndexVec};
use rustc_span::Span;
use std::fmt;
Expand Down Expand Up @@ -95,13 +94,7 @@ pub enum RegionResolutionError<'tcx> {
/// Indicates a failure of a `MemberConstraint`. These arise during
/// impl trait processing explicitly -- basically, the impl trait's hidden type
/// included some region that it was not supposed to.
MemberConstraintFailure {
span: Span,
opaque_type_def_id: DefId,
hidden_ty: Ty<'tcx>,
member_region: Region<'tcx>,
choice_regions: Vec<Region<'tcx>>,
},
MemberConstraintFailure { span: Span, hidden_ty: Ty<'tcx>, member_region: Region<'tcx> },
}

struct RegionAndOrigin<'tcx> {
Expand Down Expand Up @@ -656,10 +649,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
let span = self.tcx().def_span(member_constraint.opaque_type_def_id);
errors.push(RegionResolutionError::MemberConstraintFailure {
span,
opaque_type_def_id: member_constraint.opaque_type_def_id,
hidden_ty: member_constraint.hidden_ty,
member_region,
choice_regions: choice_regions.collect(),
});
}
}
Expand Down
132 changes: 85 additions & 47 deletions src/librustc/infer/opaque_types/mod.rs
Expand Up @@ -93,6 +93,18 @@ pub struct OpaqueTypeDecl<'tcx> {
pub origin: hir::OpaqueTyOrigin,
}

/// Whether member constraints should be generated for all opaque types
pub enum GenerateMemberConstraints {
/// The default, used by typeck
WhenRequired,
/// The borrow checker needs member constraints in any case where we don't
/// have a `'static` bound. This is because the borrow checker has more
/// flexibility in the values of regions. For example, given `f<'a, 'b>`
/// the borrow checker can have an inference variable outlive `'a` and `'b`,
/// but not be equal to `'static`.
IfNoStaticBound,
}

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// Replaces all opaque types in `value` with fresh inference variables
/// and creates appropriate obligations. For example, given the input:
Expand Down Expand Up @@ -315,7 +327,12 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
debug!("constrain_opaque_types()");

for (&def_id, opaque_defn) in opaque_types {
self.constrain_opaque_type(def_id, opaque_defn, free_region_relations);
self.constrain_opaque_type(
def_id,
opaque_defn,
GenerateMemberConstraints::WhenRequired,
free_region_relations,
);
}
}

Expand All @@ -324,6 +341,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
&self,
def_id: DefId,
opaque_defn: &OpaqueTypeDecl<'tcx>,
mode: GenerateMemberConstraints,
free_region_relations: &FRR,
) {
debug!("constrain_opaque_type()");
Expand Down Expand Up @@ -358,6 +376,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
op: |r| self.sub_regions(infer::CallReturn(span), required_region, r),
});
}
if let GenerateMemberConstraints::IfNoStaticBound = mode {
self.generate_member_constraint(
concrete_ty,
opaque_type_generics,
opaque_defn,
def_id,
);
}
return;
}

Expand Down Expand Up @@ -398,13 +424,15 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// we will create a "in bound" like `'r in
// ['a, 'b, 'c]`, where `'a..'c` are the
// regions that appear in the impl trait.

// For now, enforce a feature gate outside of async functions.
self.member_constraint_feature_gate(opaque_defn, def_id, lr, subst_arg);

return self.generate_member_constraint(
concrete_ty,
opaque_type_generics,
opaque_defn,
def_id,
lr,
subst_arg,
);
}
}
Expand All @@ -414,6 +442,16 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
let least_region = least_region.unwrap_or(tcx.lifetimes.re_static);
debug!("constrain_opaque_types: least_region={:?}", least_region);

if let GenerateMemberConstraints::IfNoStaticBound = mode {
if least_region != tcx.lifetimes.re_static {
self.generate_member_constraint(
concrete_ty,
opaque_type_generics,
opaque_defn,
def_id,
);
}
}
concrete_ty.visit_with(&mut ConstrainOpaqueTypeRegionVisitor {
tcx: self.tcx,
op: |r| self.sub_regions(infer::CallReturn(span), least_region, r),
Expand All @@ -434,19 +472,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
opaque_type_generics: &ty::Generics,
opaque_defn: &OpaqueTypeDecl<'tcx>,
opaque_type_def_id: DefId,
conflict1: ty::Region<'tcx>,
conflict2: ty::Region<'tcx>,
) {
// For now, enforce a feature gate outside of async functions.
if self.member_constraint_feature_gate(
opaque_defn,
opaque_type_def_id,
conflict1,
conflict2,
) {
return;
}

// Create the set of choice regions: each region in the hidden
// type can be equal to any of the region parameters of the
// opaque type definition.
Expand Down Expand Up @@ -500,8 +526,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
hir::OpaqueTyOrigin::AsyncFn => return false,

// Otherwise, generate the label we'll use in the error message.
hir::OpaqueTyOrigin::TypeAlias => "impl Trait",
hir::OpaqueTyOrigin::FnReturn => "impl Trait",
hir::OpaqueTyOrigin::TypeAlias
| hir::OpaqueTyOrigin::FnReturn
| hir::OpaqueTyOrigin::Misc => "impl Trait",
};
let msg = format!("ambiguous lifetime bound in `{}`", context_name);
let mut err = self.tcx.sess.struct_span_err(span, &msg);
Expand Down Expand Up @@ -549,13 +576,13 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// # Parameters
///
/// - `def_id`, the `impl Trait` type
/// - `opaque_defn`, the opaque definition created in `instantiate_opaque_types`
/// - `substs`, the substs used to instantiate this opaque type
/// - `instantiated_ty`, the inferred type C1 -- fully resolved, lifted version of
/// `opaque_defn.concrete_ty`
pub fn infer_opaque_definition_from_instantiation(
&self,
def_id: DefId,
opaque_defn: &OpaqueTypeDecl<'tcx>,
substs: SubstsRef<'tcx>,
instantiated_ty: Ty<'tcx>,
span: Span,
) -> Ty<'tcx> {
Expand All @@ -571,12 +598,8 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
// `impl Trait` return type, resulting in the parameters
// shifting.
let id_substs = InternalSubsts::identity_for_item(self.tcx, def_id);
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> = opaque_defn
.substs
.iter()
.enumerate()
.map(|(index, subst)| (*subst, id_substs[index]))
.collect();
let map: FxHashMap<GenericArg<'tcx>, GenericArg<'tcx>> =
substs.iter().enumerate().map(|(index, subst)| (*subst, id_substs[index])).collect();

// Convert the type from the function into a type valid outside
// the function, by replacing invalid regions with 'static,
Expand All @@ -598,11 +621,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
pub fn unexpected_hidden_region_diagnostic(
tcx: TyCtxt<'tcx>,
region_scope_tree: Option<&region::ScopeTree>,
opaque_type_def_id: DefId,
span: Span,
hidden_ty: Ty<'tcx>,
hidden_region: ty::Region<'tcx>,
) -> DiagnosticBuilder<'tcx> {
let span = tcx.def_span(opaque_type_def_id);
let mut err = struct_span_err!(
tcx.sess,
span,
Expand Down Expand Up @@ -817,32 +839,48 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {

fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> {
match r {
// ignore bound regions that appear in the type (e.g., this
// would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
ty::ReLateBound(..) |

// ignore `'static`, as that can appear anywhere
ty::ReStatic => return r,

_ => { }
// Ignore bound regions and `'static` regions that appear in the
// type, we only need to remap regions that reference lifetimes
// from the function declaraion.
// This would ignore `'r` in a type like `for<'r> fn(&'r u32)`.
ty::ReLateBound(..) | ty::ReStatic => return r,

// If regions have been erased (by writeback), don't try to unerase
// them.
ty::ReErased => return r,

// The regions that we expect from borrow checking.
ty::ReEarlyBound(_) | ty::ReFree(_) | ty::ReEmpty(ty::UniverseIndex::ROOT) => {}

ty::ReEmpty(_)
| ty::RePlaceholder(_)
| ty::ReVar(_)
| ty::ReScope(_)
| ty::ReClosureBound(_) => {
// All of the regions in the type should either have been
// erased by writeback, or mapped back to named regions by
// borrow checking.
bug!("unexpected region kind in opaque type: {:?}", r);
}
}

let generics = self.tcx().generics_of(self.opaque_type_def_id);
match self.map.get(&r.into()).map(|k| k.unpack()) {
Some(GenericArgKind::Lifetime(r1)) => r1,
Some(u) => panic!("region mapped to unexpected kind: {:?}", u),
None if self.map_missing_regions_to_empty || self.tainted_by_errors => {
self.tcx.lifetimes.re_root_empty
}
None if generics.parent.is_some() => {
if !self.map_missing_regions_to_empty && !self.tainted_by_errors {
if let Some(hidden_ty) = self.hidden_ty.take() {
unexpected_hidden_region_diagnostic(
self.tcx,
None,
self.opaque_type_def_id,
hidden_ty,
r,
)
.emit();
}
if let Some(hidden_ty) = self.hidden_ty.take() {
unexpected_hidden_region_diagnostic(
self.tcx,
None,
self.tcx.def_span(self.opaque_type_def_id),
hidden_ty,
r,
)
.emit();
}
self.tcx.lifetimes.re_root_empty
}
Expand All @@ -860,7 +898,7 @@ impl TypeFolder<'tcx> for ReverseMapper<'tcx> {
)
.emit();

self.tcx().mk_region(ty::ReStatic)
self.tcx().lifetimes.re_static
}
}
}
Expand Down
8 changes: 7 additions & 1 deletion 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};
Expand Down Expand Up @@ -59,8 +61,12 @@ pub struct GeneratorLayout<'tcx> {
pub storage_conflicts: BitMatrix<GeneratorSavedLocal, GeneratorSavedLocal>,
}

#[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<DefId, ty::ResolvedOpaqueTy<'tcx>>,
pub closure_requirements: Option<ClosureRegionRequirements<'tcx>>,
pub used_mut_upvars: SmallVec<[Field; 8]>,
}
Expand Down
22 changes: 12 additions & 10 deletions src/librustc/query/mod.rs
Expand Up @@ -125,7 +125,9 @@ rustc_queries! {

/// Fetch the MIR for a given `DefId` right after it's built - this includes
/// unreachable code.
query mir_built(_: DefId) -> &'tcx Steal<mir::BodyAndCache<'tcx>> {}
query mir_built(_: DefId) -> &'tcx Steal<mir::BodyAndCache<'tcx>> {
desc { "building MIR for" }
}

/// Fetch the MIR for a given `DefId` up till the point where it is
/// ready for const evaluation.
Expand Down Expand Up @@ -345,6 +347,7 @@ rustc_queries! {
TypeChecking {
/// The result of unsafety-checking this `DefId`.
query unsafety_check_result(key: DefId) -> mir::UnsafetyCheckResult {
desc { |tcx| "unsafety-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
}

Expand Down Expand Up @@ -414,14 +417,8 @@ rustc_queries! {
}

query typeck_tables_of(key: DefId) -> &'tcx ty::TypeckTables<'tcx> {
desc { |tcx| "type-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if { key.is_local() }
load_cached(tcx, id) {
let typeck_tables: Option<ty::TypeckTables<'tcx>> = tcx
.queries.on_disk_cache
.try_load_query_result(tcx, id);

typeck_tables.map(|tables| &*tcx.arena.alloc(tables))
}
}
query diagnostic_only_typeck_tables_of(key: DefId) -> &'tcx ty::TypeckTables<'tcx> {
cache_on_disk_if { key.is_local() }
Expand Down Expand Up @@ -452,8 +449,13 @@ rustc_queries! {
BorrowChecking {
/// Borrow-checks the function body. If this is a closure, returns
/// additional requirements that the closure's creator must verify.
query mir_borrowck(key: DefId) -> mir::BorrowCheckResult<'tcx> {
cache_on_disk_if(tcx, _) { key.is_local() && tcx.is_closure(key) }
query mir_borrowck(key: DefId) -> &'tcx mir::BorrowCheckResult<'tcx> {
desc { |tcx| "borrow-checking `{}`", tcx.def_path_str(key) }
cache_on_disk_if(tcx, opt_result) {
key.is_local()
&& (tcx.is_closure(key)
|| opt_result.map_or(false, |r| !r.concrete_opaque_types.is_empty()))
}
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/ty/flags.rs
Expand Up @@ -138,7 +138,7 @@ impl FlagComputation {
}

&ty::Opaque(_, substs) => {
self.add_flags(TypeFlags::HAS_PROJECTION);
self.add_flags(TypeFlags::HAS_PROJECTION | TypeFlags::HAS_TY_OPAQUE);
self.add_substs(substs);
}

Expand Down

0 comments on commit 19288dd

Please sign in to comment.