Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Infer regions for opaque types in borrowck #67681

Merged
merged 25 commits into from
Feb 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
033bd8c
Explain a test
matthewjasper Dec 26, 2019
7f41cf4
Check associated opaque types don't use unconstrained lifetimes
matthewjasper Dec 26, 2019
4af0952
Call `is_freeze` less in unsafety-checking
matthewjasper Dec 28, 2019
60970be
Distinguish RPIT from other impl trait
matthewjasper Dec 28, 2019
43dae91
Give some more queries descriptions
matthewjasper Dec 28, 2019
9664122
Simplify function signature in opaque_types
matthewjasper Dec 26, 2019
378b5b4
Generate more accurate MIR in `construct_error`
matthewjasper Dec 26, 2019
43a3348
Arena allocate the result of mir_borrowck
matthewjasper Dec 26, 2019
75ac0cc
Prepare to use borrowck to resolve opaque types
matthewjasper Dec 25, 2019
d9b9f00
Infer opaque type regions in borrow checking
matthewjasper Dec 26, 2019
edee23e
Avoid unnecessary opaque type errors in borrowck
matthewjasper Dec 28, 2019
2fb0254
Ensure RPIT types get recorded in borrowck
matthewjasper Dec 28, 2019
bb8c991
Erase regions in opaque types in typeck
matthewjasper Dec 28, 2019
93ac5bc
Update tests
matthewjasper Dec 28, 2019
f23bca7
Address review comments
matthewjasper Dec 29, 2019
728224d
Show inferred opaque types with `#[rustc_regions]`
matthewjasper Jan 5, 2020
5cfa7d1
Handle equal regions in opaque type inference
matthewjasper Jan 8, 2020
2bd16f3
Improve opaque type lifetime errors
matthewjasper Jan 11, 2020
dd1687e
Always check upper bounds when choosing member regions
matthewjasper Jan 11, 2020
e3e5d27
Use member constraint for most opaque types in NLL
matthewjasper Jan 11, 2020
78e0ab5
Update tests
matthewjasper Jan 11, 2020
6d9e270
Fix and test nested impl Trait
matthewjasper Jan 12, 2020
223a2ee
Add fast path to eq_opaque_type_and_type
matthewjasper Jan 17, 2020
edddb62
Split `type_of` out of collect.rs
matthewjasper Jan 17, 2020
d863978
Fix tests after rebase
matthewjasper Feb 14, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/librustc/arena.rs
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Loading