Skip to content

Commit

Permalink
Auto merge of rust-lang#87587 - oli-obk:lazy_tait, r=spastorino
Browse files Browse the repository at this point in the history
Various refactorings of the TAIT infrastructure

Before this PR we used to store the opaque type knowledge outside the `InferCtxt`, so it got recomputed on every opaque type instantiation.

I also removed a feature gate check that makes no sense in the planned lazy TAIT resolution scheme

Each commit passes all tests, so this PR is best reviewed commit by commit.

r? `@spastorino`
  • Loading branch information
bors committed Aug 11, 2021
2 parents 47b41b7 + 93c4aa8 commit d488de8
Show file tree
Hide file tree
Showing 33 changed files with 339 additions and 482 deletions.
44 changes: 42 additions & 2 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*;

use self::opaque_types::OpaqueTypeMap;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};

use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine};

use hir::def_id::CRATE_DEF_ID;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::undo_log::Rollback;
Expand Down Expand Up @@ -59,6 +61,7 @@ pub mod lattice;
mod lexical_region_resolve;
mod lub;
pub mod nll_relate;
pub mod opaque_types;
pub mod outlives;
pub mod region_constraints;
pub mod resolve;
Expand Down Expand Up @@ -191,6 +194,19 @@ pub struct InferCtxtInner<'tcx> {
region_obligations: Vec<(hir::HirId, RegionObligation<'tcx>)>,

undo_log: InferCtxtUndoLogs<'tcx>,

// Opaque types found in explicit return types and their
// associated fresh inference variable. Writeback resolves these
// variables to get the concrete type, which can be used to
// 'de-opaque' OpaqueTypeDecl, after typeck is done with all functions.
pub opaque_types: OpaqueTypeMap<'tcx>,

/// A map from inference variables created from opaque
/// type instantiations (`ty::Infer`) to the actual opaque
/// type (`ty::Opaque`). Used during fallback to map unconstrained
/// opaque type inference variables to their corresponding
/// opaque type.
pub opaque_types_vars: FxHashMap<Ty<'tcx>, Ty<'tcx>>,
}

impl<'tcx> InferCtxtInner<'tcx> {
Expand All @@ -204,6 +220,8 @@ impl<'tcx> InferCtxtInner<'tcx> {
float_unification_storage: ut::UnificationTableStorage::new(),
region_constraint_storage: Some(RegionConstraintStorage::new()),
region_obligations: vec![],
opaque_types: Default::default(),
opaque_types_vars: Default::default(),
}
}

Expand Down Expand Up @@ -273,6 +291,10 @@ impl<'tcx> InferCtxtInner<'tcx> {
pub struct InferCtxt<'a, 'tcx> {
pub tcx: TyCtxt<'tcx>,

/// The `DefId` of the item in whose context we are performing inference or typeck.
/// It is used to check whether an opaque type use is a defining use.
pub defining_use_anchor: LocalDefId,

/// During type-checking/inference of a body, `in_progress_typeck_results`
/// contains a reference to the typeck results being built up, which are
/// used for reading closure kinds/signatures as they are inferred,
Expand Down Expand Up @@ -531,6 +553,7 @@ impl<'tcx> fmt::Display for FixupError<'tcx> {
pub struct InferCtxtBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
defining_use_anchor: LocalDefId,
}

pub trait TyCtxtInferExt<'tcx> {
Expand All @@ -539,15 +562,31 @@ pub trait TyCtxtInferExt<'tcx> {

impl TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
InferCtxtBuilder { tcx: self, fresh_typeck_results: None }
InferCtxtBuilder {
tcx: self,
defining_use_anchor: CRATE_DEF_ID,
fresh_typeck_results: None,
}
}
}

impl<'tcx> InferCtxtBuilder<'tcx> {
/// Used only by `rustc_typeck` during body type-checking/inference,
/// will initialize `in_progress_typeck_results` with fresh `TypeckResults`.
/// Will also change the scope for opaque type defining use checks to the given owner.
pub fn with_fresh_in_progress_typeck_results(mut self, table_owner: LocalDefId) -> Self {
self.fresh_typeck_results = Some(RefCell::new(ty::TypeckResults::new(table_owner)));
self.with_opaque_type_inference(table_owner)
}

/// Whenever the `InferCtxt` should be able to handle defining uses of opaque types,
/// you need to call this function. Otherwise the opaque type will be treated opaquely.
///
/// It is only meant to be called in two places, for typeck
/// (via `with_fresh_in_progress_typeck_results`) and for the inference context used
/// in mir borrowck.
pub fn with_opaque_type_inference(mut self, defining_use_anchor: LocalDefId) -> Self {
self.defining_use_anchor = defining_use_anchor;
self
}

Expand Down Expand Up @@ -575,10 +614,11 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
}

pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R {
let InferCtxtBuilder { tcx, ref fresh_typeck_results } = *self;
let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self;
let in_progress_typeck_results = fresh_typeck_results.as_ref();
f(InferCtxt {
tcx,
defining_use_anchor,
in_progress_typeck_results,
inner: RefCell::new(InferCtxtInner::new()),
lexical_region_resolutions: RefCell::new(None),
Expand Down
47 changes: 47 additions & 0 deletions compiler/rustc_infer/src/infer/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use rustc_data_structures::vec_map::VecMap;
use rustc_hir as hir;
use rustc_middle::ty::{OpaqueTypeKey, Ty};
use rustc_span::Span;

pub type OpaqueTypeMap<'tcx> = VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>;

/// Information about the opaque types whose values we
/// are inferring in this function (these are the `impl Trait` that
/// appear in the return type).
#[derive(Copy, Clone, Debug)]
pub struct OpaqueTypeDecl<'tcx> {
/// The opaque type (`ty::Opaque`) for this declaration.
pub opaque_type: Ty<'tcx>,

/// The span of this particular definition of the opaque type. So
/// for example:
///
/// ```ignore (incomplete snippet)
/// type Foo = impl Baz;
/// fn bar() -> Foo {
/// // ^^^ This is the span we are looking for!
/// }
/// ```
///
/// In cases where the fn returns `(impl Trait, impl Trait)` or
/// other such combinations, the result is currently
/// over-approximated, but better than nothing.
pub definition_span: Span,

/// The type variable that represents the value of the opaque type
/// that we require. In other words, after we compile this function,
/// we will be created a constraint like:
///
/// Foo<'a, T> = ?C
///
/// where `?C` is the value of this type variable. =) It may
/// naturally refer to the type and lifetime parameters in scope
/// in this function, though ultimately it should only reference
/// those that are arguments to `Foo` in the constraint above. (In
/// other words, `?C` should not include `'b`, even though it's a
/// lifetime parameter on `foo`.)
pub concrete_ty: Ty<'tcx>,

/// The origin of the opaque type.
pub origin: hir::OpaqueTyOrigin,
}
2 changes: 1 addition & 1 deletion compiler/rustc_mir/src/borrow_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ fn mir_borrowck<'tcx>(
let (input_body, promoted) = tcx.mir_promoted(def);
debug!("run query mir_borrowck: {}", tcx.def_path_str(def.did.to_def_id()));

let opt_closure_req = tcx.infer_ctxt().enter(|infcx| {
let opt_closure_req = tcx.infer_ctxt().with_opaque_type_inference(def.did).enter(|infcx| {
let input_body: &Body<'_> = &input_body.borrow();
let promoted: &IndexVec<_, _> = &promoted.borrow();
do_mir_borrowck(&infcx, input_body, promoted)
Expand Down
164 changes: 74 additions & 90 deletions compiler/rustc_mir/src/borrow_check/type_check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -179,54 +179,55 @@ pub(crate) fn type_check<'mir, 'tcx>(
liveness::generate(&mut cx, body, elements, flow_inits, move_data, location_table);

translate_outlives_facts(&mut cx);
let mut opaque_type_values = cx.opaque_type_values;

for (_, revealed_ty) in &mut opaque_type_values {
*revealed_ty = infcx.resolve_vars_if_possible(*revealed_ty);
if revealed_ty.has_infer_types_or_consts() {
infcx.tcx.sess.delay_span_bug(
body.span,
&format!("could not resolve {:#?}", revealed_ty.kind()),
);
*revealed_ty = infcx.tcx.ty_error();
}
}

opaque_type_values.retain(|(opaque_type_key, resolved_ty)| {
let concrete_is_opaque = if let ty::Opaque(def_id, _) = resolved_ty.kind() {
*def_id == opaque_type_key.def_id
} else {
false
};
let opaque_type_values = mem::take(&mut infcx.inner.borrow_mut().opaque_types);

if concrete_is_opaque {
// 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_type_key.def_id,
);
}
!concrete_is_opaque
});
opaque_type_values
.into_iter()
.filter_map(|(opaque_type_key, decl)| {
let mut revealed_ty = infcx.resolve_vars_if_possible(decl.concrete_ty);
if revealed_ty.has_infer_types_or_consts() {
infcx.tcx.sess.delay_span_bug(
body.span,
&format!("could not resolve {:#?}", revealed_ty.kind()),
);
revealed_ty = infcx.tcx.ty_error();
}
let concrete_is_opaque = if let ty::Opaque(def_id, _) = revealed_ty.kind() {
*def_id == opaque_type_key.def_id
} else {
false
};

if concrete_is_opaque {
// 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_type_key.def_id,
);
None
} else {
Some((opaque_type_key, revealed_ty))
}
})
.collect()
},
);

Expand Down Expand Up @@ -865,7 +866,6 @@ 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: VecMap<OpaqueTypeKey<'tcx>, Ty<'tcx>>,
}

struct BorrowCheckContext<'a, 'tcx> {
Expand Down Expand Up @@ -1025,7 +1025,6 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
borrowck_context,
reported_errors: Default::default(),
universal_region_relations,
opaque_type_values: VecMap::default(),
};
checker.check_user_type_annotations();
checker
Expand Down Expand Up @@ -1289,10 +1288,8 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
let body = self.body;
let mir_def_id = body.source.def_id().expect_local();

let mut opaque_type_values = VecMap::new();

debug!("eq_opaque_type_and_type: mir_def_id={:?}", mir_def_id);
let opaque_type_map = self.fully_perform_op(
self.fully_perform_op(
locations,
category,
CustomTypeOp::new(
Expand All @@ -1307,20 +1304,17 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
// to `Box<?T>`, returning an `opaque_type_map` mapping `{Foo<T> -> ?T}`.
// (Note that the key of the map is both the def-id of `Foo` along with
// any generic parameters.)
let (output_ty, opaque_type_map) =
obligations.add(infcx.instantiate_opaque_types(
mir_def_id,
dummy_body_id,
param_env,
anon_ty,
locations.span(body),
));
let output_ty = obligations.add(infcx.instantiate_opaque_types(
dummy_body_id,
param_env,
anon_ty,
locations.span(body),
));
debug!(
"eq_opaque_type_and_type: \
instantiated output_ty={:?} \
opaque_type_map={:#?} \
revealed_ty={:?}",
output_ty, opaque_type_map, revealed_ty
output_ty, revealed_ty
);

// Make sure that the inferred types are well-formed. I'm
Expand All @@ -1338,48 +1332,38 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
.eq(output_ty, revealed_ty)?,
);

for &(opaque_type_key, opaque_decl) in &opaque_type_map {
opaque_type_values.insert(opaque_type_key, opaque_decl.concrete_ty);
}

debug!("eq_opaque_type_and_type: equated");

Ok(InferOk {
value: Some(opaque_type_map),
obligations: obligations.into_vec(),
})
Ok(InferOk { value: (), obligations: obligations.into_vec() })
},
|| "input_output".to_string(),
),
)?;

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
// have to solve any bounds (e.g., `-> impl Iterator` needs to
// prove that `T: Iterator` where `T` is the type we
// instantiated it with).
if let Some(opaque_type_map) = opaque_type_map {
for (opaque_type_key, opaque_decl) in opaque_type_map {
self.fully_perform_op(
locations,
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|infcx| {
infcx.constrain_opaque_type(
opaque_type_key,
&opaque_decl,
GenerateMemberConstraints::IfNoStaticBound,
universal_region_relations,
);
Ok(InferOk { value: (), obligations: vec![] })
},
|| "opaque_type_map".to_string(),
),
)?;
}
let opaque_type_map = self.infcx.inner.borrow().opaque_types.clone();
for (opaque_type_key, opaque_decl) in opaque_type_map {
self.fully_perform_op(
locations,
ConstraintCategory::OpaqueType,
CustomTypeOp::new(
|infcx| {
infcx.constrain_opaque_type(
opaque_type_key,
&opaque_decl,
GenerateMemberConstraints::IfNoStaticBound,
universal_region_relations,
);
Ok(InferOk { value: (), obligations: vec![] })
},
|| "opaque_type_map".to_string(),
),
)?;
}
Ok(())
}
Expand Down
Loading

0 comments on commit d488de8

Please sign in to comment.