1
Fork 0

Auto merge of #92007 - oli-obk:lazy_tait2, r=nikomatsakis

Lazy type-alias-impl-trait

Previously opaque types were processed by

1. replacing all mentions of them with inference variables
2. memorizing these inference variables in a side-table
3. at the end of typeck, resolve the inference variables in the side table and use the resolved type as the hidden type of the opaque type

This worked okayish for `impl Trait` in return position, but required lots of roundabout type inference hacks and processing.

This PR instead stops this process of replacing opaque types with inference variables, and just keeps the opaque types around.
Whenever an opaque type `O` is compared with another type `T`, we make the comparison succeed and record `T` as the hidden type. If `O` is compared to `U` while there is a recorded hidden type for it, we grab the recorded type (`T`) and compare that against `U`. This makes implementing

* https://github.com/rust-lang/rfcs/pull/2515

much simpler (previous attempts on the inference based scheme were very prone to ICEs and general misbehaviour that was not explainable except by random implementation defined oddities).

r? `@nikomatsakis`

fixes #93411
fixes #88236
This commit is contained in:
bors 2022-02-07 23:40:26 +00:00
commit e7cc3bddbe
359 changed files with 3311 additions and 2447 deletions

View file

@ -34,6 +34,12 @@ pub struct At<'a, 'tcx> {
pub infcx: &'a InferCtxt<'a, 'tcx>,
pub cause: &'a ObligationCause<'tcx>,
pub param_env: ty::ParamEnv<'tcx>,
/// Whether we should define opaque types
/// or just treat them opaquely.
/// Currently only used to prevent predicate
/// matching from matching anything against opaque
/// types.
pub define_opaque_types: bool,
}
pub struct Trace<'a, 'tcx> {
@ -49,7 +55,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
cause: &'a ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
) -> At<'a, 'tcx> {
At { infcx: self, cause, param_env }
At { infcx: self, cause, param_env, define_opaque_types: true }
}
}
@ -64,6 +70,10 @@ pub trait ToTrace<'tcx>: Relate<'tcx> + Copy {
}
impl<'a, 'tcx> At<'a, 'tcx> {
pub fn define_opaque_types(self, define_opaque_types: bool) -> Self {
Self { define_opaque_types, ..self }
}
/// Hacky routine for equating two impl headers in coherence.
pub fn eq_impl_headers(
self,
@ -194,7 +204,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
{
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types);
fields
.sub(a_is_expected)
.relate(a, b)
@ -211,7 +221,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
{
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types);
fields
.equate(a_is_expected)
.relate(a, b)
@ -226,7 +236,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
{
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types);
fields
.lub(a_is_expected)
.relate(a, b)
@ -241,7 +251,7 @@ impl<'a, 'tcx> Trace<'a, 'tcx> {
{
let Trace { at, trace, a_is_expected } = self;
at.infcx.commit_if_ok(|_| {
let mut fields = at.infcx.combine_fields(trace, at.param_env);
let mut fields = at.infcx.combine_fields(trace, at.param_env, at.define_opaque_types);
fields
.glb(a_is_expected)
.relate(a, b)

View file

@ -26,6 +26,7 @@ use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::relate::TypeRelation;
use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
use rustc_middle::ty::{self, BoundVar, Const, ToPredicate, Ty, TyCtxt};
use rustc_span::Span;
use std::fmt::Debug;
use std::iter;
@ -89,6 +90,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
var_values: inference_vars,
region_constraints: QueryRegionConstraints::default(),
certainty: Certainty::Proven, // Ambiguities are OK!
opaque_types: vec![],
value: answer,
})
}
@ -133,14 +135,27 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
let certainty =
if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
let opaque_types = self.take_opaque_types_for_query_response();
Ok(QueryResponse {
var_values: inference_vars,
region_constraints,
certainty,
value: answer,
opaque_types,
})
}
fn take_opaque_types_for_query_response(&self) -> Vec<(Ty<'tcx>, Ty<'tcx>)> {
self.inner
.borrow_mut()
.opaque_type_storage
.take_opaque_types()
.into_iter()
.map(|(k, v)| (self.tcx.mk_opaque(k.def_id, k.substs), v.hidden_type.ty))
.collect()
}
/// Given the (canonicalized) result to a canonical query,
/// instantiates the result so it can be used, plugging in the
/// values from the canonical query. (Note that the result may
@ -223,13 +238,12 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
where
R: Debug + TypeFoldable<'tcx>,
{
let result_subst =
self.query_response_substitution_guess(cause, original_values, query_response);
let InferOk { value: result_subst, mut obligations } = self
.query_response_substitution_guess(cause, param_env, original_values, query_response)?;
// Compute `QueryOutlivesConstraint` values that unify each of
// the original values `v_o` that was canonicalized into a
// variable...
let mut obligations = vec![];
for (index, original_value) in original_values.var_values.iter().enumerate() {
// ...with the value `v_r` of that variable from the query.
@ -344,20 +358,25 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
original_values, query_response,
);
let result_subst =
self.query_response_substitution_guess(cause, original_values, query_response);
let mut value = self.query_response_substitution_guess(
cause,
param_env,
original_values,
query_response,
)?;
let obligations = self
.unify_query_response_substitution_guess(
value.obligations.extend(
self.unify_query_response_substitution_guess(
cause,
param_env,
original_values,
&result_subst,
&value.value,
query_response,
)?
.into_obligations();
.into_obligations(),
);
Ok(InferOk { value: result_subst, obligations })
Ok(value)
}
/// Given the original values and the (canonicalized) result from
@ -372,9 +391,10 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
fn query_response_substitution_guess<R>(
&self,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
original_values: &OriginalQueryValues<'tcx>,
query_response: &Canonical<'tcx, QueryResponse<'tcx, R>>,
) -> CanonicalVarValues<'tcx>
) -> InferResult<'tcx, CanonicalVarValues<'tcx>>
where
R: Debug + TypeFoldable<'tcx>,
{
@ -474,7 +494,16 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
.collect(),
};
result_subst
let mut obligations = vec![];
// Carry all newly resolved opaque types to the caller's scope
for &(a, b) in &query_response.value.opaque_types {
let a = substitute_value(self.tcx, &result_subst, a);
let b = substitute_value(self.tcx, &result_subst, b);
obligations.extend(self.handle_opaque_type(a, b, cause, param_env)?.obligations);
}
Ok(InferOk { value: result_subst, obligations })
}
/// Given a "guess" at the values for the canonical variables in
@ -631,6 +660,10 @@ struct QueryTypeRelatingDelegate<'a, 'tcx> {
}
impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
fn span(&self) -> Span {
self.cause.span
}
fn param_env(&self) -> ty::ParamEnv<'tcx> {
self.param_env
}
@ -686,4 +719,14 @@ impl<'tcx> TypeRelatingDelegate<'tcx> for QueryTypeRelatingDelegate<'_, 'tcx> {
fn forbid_inference_vars() -> bool {
true
}
fn register_opaque_type(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, a_is_expected: bool) {
self.obligations.push(self.infcx.opaque_ty_obligation(
a,
b,
a_is_expected,
self.param_env,
self.cause.clone(),
));
}
}

View file

@ -51,6 +51,12 @@ pub struct CombineFields<'infcx, 'tcx> {
pub cause: Option<ty::relate::Cause>,
pub param_env: ty::ParamEnv<'tcx>,
pub obligations: PredicateObligations<'tcx>,
/// Whether we should define opaque types
/// or just treat them opaquely.
/// Currently only used to prevent predicate
/// matching from matching anything against opaque
/// types.
pub define_opaque_types: bool,
}
#[derive(Copy, Clone, Debug)]
@ -322,6 +328,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
/// will first instantiate `b_vid` with a *generalized* version
/// of `a_ty`. Generalization introduces other inference
/// variables wherever subtyping could occur.
#[instrument(skip(self), level = "debug")]
pub fn instantiate(
&mut self,
a_ty: Ty<'tcx>,
@ -334,8 +341,6 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
// Get the actual variable that b_vid has been inferred to
debug_assert!(self.infcx.inner.borrow_mut().type_variables().probe(b_vid).is_unknown());
debug!("instantiate(a_ty={:?} dir={:?} b_vid={:?})", a_ty, dir, b_vid);
// Generalize type of `a_ty` appropriately depending on the
// direction. As an example, assume:
//
@ -348,10 +353,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
// variables. (Down below, we will relate `a_ty <: b_ty`,
// adding constraints like `'x: '?2` and `?1 <: ?3`.)
let Generalization { ty: b_ty, needs_wf } = self.generalize(a_ty, b_vid, dir)?;
debug!(
"instantiate(a_ty={:?}, dir={:?}, b_vid={:?}, generalized b_ty={:?})",
a_ty, dir, b_vid, b_ty
);
debug!(?b_ty);
self.infcx.inner.borrow_mut().type_variables().instantiate(b_vid, b_ty);
if needs_wf {
@ -392,13 +394,13 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
/// Preconditions:
///
/// - `for_vid` is a "root vid"
#[instrument(skip(self), level = "trace")]
fn generalize(
&self,
ty: Ty<'tcx>,
for_vid: ty::TyVid,
dir: RelationDir,
) -> RelateResult<'tcx, Generalization<'tcx>> {
debug!("generalize(ty={:?}, for_vid={:?}, dir={:?}", ty, for_vid, dir);
// Determine the ambient variance within which `ty` appears.
// The surrounding equation is:
//
@ -412,7 +414,7 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
RelationDir::SupertypeOf => ty::Contravariant,
};
debug!("generalize: ambient_variance = {:?}", ambient_variance);
trace!(?ambient_variance);
let for_universe = match self.infcx.inner.borrow_mut().type_variables().probe(for_vid) {
v @ TypeVariableValue::Known { .. } => {
@ -421,8 +423,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
TypeVariableValue::Unknown { universe } => universe,
};
debug!("generalize: for_universe = {:?}", for_universe);
debug!("generalize: trace = {:?}", self.trace);
trace!(?for_universe);
trace!(?self.trace);
let mut generalize = Generalizer {
infcx: self.infcx,
@ -439,12 +441,12 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
let ty = match generalize.relate(ty, ty) {
Ok(ty) => ty,
Err(e) => {
debug!("generalize: failure {:?}", e);
debug!(?e, "failure");
return Err(e);
}
};
let needs_wf = generalize.needs_wf;
debug!("generalize: success {{ {:?}, {:?} }}", ty, needs_wf);
trace!(?ty, ?needs_wf, "success");
Ok(Generalization { ty, needs_wf })
}

View file

@ -66,18 +66,19 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
self.relate(a, b)
}
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug!("{}.tys({:?}, {:?})", self.tag(), a, b);
if a == b {
return Ok(a);
}
trace!(a = ?a.kind(), b = ?b.kind());
let infcx = self.fields.infcx;
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
debug!("{}.tys: replacements ({:?}, {:?})", self.tag(), a, b);
match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(a_id)), &ty::Infer(TyVar(b_id))) => {
infcx.inner.borrow_mut().type_variables().equate(a_id, b_id);
@ -91,6 +92,21 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
self.fields.instantiate(a, RelationDir::EqTo, b_id, self.a_is_expected)?;
}
(&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => {
self.fields.infcx.super_combine_tys(self, a, b)?;
}
(&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..))
if self.fields.define_opaque_types && did.is_local() =>
{
self.fields.obligations.push(infcx.opaque_ty_obligation(
a,
b,
self.a_is_expected(),
self.param_env(),
self.fields.trace.cause.clone(),
));
}
_ => {
self.fields.infcx.super_combine_tys(self, a, b)?;
}

View file

@ -4,7 +4,7 @@ use super::InferCtxt;
use super::Subtype;
use crate::infer::combine::ConstEquateRelation;
use crate::traits::ObligationCause;
use crate::traits::{ObligationCause, PredicateObligation};
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt};
@ -111,12 +111,20 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
&self.fields.trace.cause
}
fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) {
self.fields.obligations.extend(obligations)
}
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
let mut sub = self.fields.sub(self.a_is_expected);
sub.relate(v, a)?;
sub.relate(v, b)?;
Ok(())
}
fn define_opaque_types(&self) -> bool {
self.fields.define_opaque_types
}
}
impl<'tcx> ConstEquateRelation<'tcx> for Glb<'_, '_, 'tcx> {

View file

@ -22,7 +22,7 @@
use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use super::InferCtxt;
use crate::traits::ObligationCause;
use crate::traits::{ObligationCause, PredicateObligation};
use rustc_middle::ty::relate::{RelateResult, TypeRelation};
use rustc_middle::ty::TyVar;
use rustc_middle::ty::{self, Ty};
@ -32,6 +32,10 @@ pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> {
fn cause(&self) -> &ObligationCause<'tcx>;
fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>);
fn define_opaque_types(&self) -> bool;
// Relates the type `v` to `a` and `b` such that `v` represents
// the LUB/GLB of `a` and `b` as appropriate.
//
@ -41,6 +45,7 @@ pub trait LatticeDir<'f, 'tcx>: TypeRelation<'tcx> {
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()>;
}
#[instrument(skip(this), level = "debug")]
pub fn super_lattice_tys<'a, 'tcx: 'a, L>(
this: &mut L,
a: Ty<'tcx>,
@ -49,15 +54,17 @@ pub fn super_lattice_tys<'a, 'tcx: 'a, L>(
where
L: LatticeDir<'a, 'tcx>,
{
debug!("{}.lattice_tys({:?}, {:?})", this.tag(), a, b);
debug!("{}", this.tag());
if a == b {
return Ok(a);
}
let infcx = this.infcx();
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
match (a.kind(), b.kind()) {
// If one side is known to be a variable and one is not,
// create a variable (`v`) to represent the LUB. Make sure to
@ -94,6 +101,22 @@ where
Ok(v)
}
(&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => {
infcx.super_combine_tys(this, a, b)
}
(&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..))
if this.define_opaque_types() && did.is_local() =>
{
this.add_obligations(vec![infcx.opaque_ty_obligation(
a,
b,
this.a_is_expected(),
this.param_env(),
this.cause().clone(),
)]);
Ok(a)
}
_ => infcx.super_combine_tys(this, a, b),
}
}

View file

@ -4,7 +4,7 @@ use super::InferCtxt;
use super::Subtype;
use crate::infer::combine::ConstEquateRelation;
use crate::traits::ObligationCause;
use crate::traits::{ObligationCause, PredicateObligation};
use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, Ty, TyCtxt};
@ -117,10 +117,18 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
&self.fields.trace.cause
}
fn add_obligations(&mut self, obligations: Vec<PredicateObligation<'tcx>>) {
self.fields.obligations.extend(obligations)
}
fn relate_bound(&mut self, v: Ty<'tcx>, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, ()> {
let mut sub = self.fields.sub(self.a_is_expected);
sub.relate(a, v)?;
sub.relate(b, v)?;
Ok(())
}
fn define_opaque_types(&self) -> bool {
self.fields.define_opaque_types
}
}

View file

@ -5,7 +5,7 @@ pub use self::RegionVariableOrigin::*;
pub use self::SubregionOrigin::*;
pub use self::ValuePairs::*;
use self::opaque_types::OpaqueTypeMap;
use self::opaque_types::OpaqueTypeStorage;
pub(crate) use self::undo_log::{InferCtxtUndoLogs, Snapshot, UndoLog};
use crate::traits::{self, ObligationCause, PredicateObligations, TraitEngine};
@ -192,18 +192,8 @@ pub struct InferCtxtInner<'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 outside of type inference.
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>>,
/// Caches for opaque type inference.
pub opaque_type_storage: OpaqueTypeStorage<'tcx>,
}
impl<'tcx> InferCtxtInner<'tcx> {
@ -217,8 +207,7 @@ 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(),
opaque_type_storage: Default::default(),
}
}
@ -237,6 +226,11 @@ impl<'tcx> InferCtxtInner<'tcx> {
self.type_variable_storage.with_log(&mut self.undo_log)
}
#[inline]
pub fn opaque_types(&mut self) -> opaque_types::OpaqueTypeTable<'_, 'tcx> {
self.opaque_type_storage.with_log(&mut self.undo_log)
}
#[inline]
fn int_unification_table(
&mut self,
@ -297,6 +291,10 @@ pub struct InferCtxt<'a, 'tcx> {
/// to the outside until the end up in an `InferCtxt` for typeck or borrowck.
pub defining_use_anchor: Option<LocalDefId>,
/// Used by WF-checking to not have to figure out hidden types itself, but
/// to just invoke type_of to get the already computed hidden type from typeck.
pub reveal_defining_opaque_types: bool,
/// 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,
@ -552,6 +550,7 @@ pub struct InferCtxtBuilder<'tcx> {
tcx: TyCtxt<'tcx>,
fresh_typeck_results: Option<RefCell<ty::TypeckResults<'tcx>>>,
defining_use_anchor: Option<LocalDefId>,
reveal_defining_opaque_types: bool,
}
pub trait TyCtxtInferExt<'tcx> {
@ -560,7 +559,12 @@ pub trait TyCtxtInferExt<'tcx> {
impl<'tcx> TyCtxtInferExt<'tcx> for TyCtxt<'tcx> {
fn infer_ctxt(self) -> InferCtxtBuilder<'tcx> {
InferCtxtBuilder { tcx: self, defining_use_anchor: None, fresh_typeck_results: None }
InferCtxtBuilder {
tcx: self,
defining_use_anchor: None,
fresh_typeck_results: None,
reveal_defining_opaque_types: false,
}
}
}
@ -584,6 +588,13 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
self
}
/// WF-checking doesn't need to recompute opaque types and can instead use
/// the type_of query to get them from typeck.
pub fn reveal_defining_opaque_types(mut self) -> Self {
self.reveal_defining_opaque_types = true;
self
}
/// Given a canonical value `C` as a starting point, create an
/// inference context that contains each of the bound values
/// within instantiated as a fresh variable. The `f` closure is
@ -608,11 +619,17 @@ impl<'tcx> InferCtxtBuilder<'tcx> {
}
pub fn enter<R>(&mut self, f: impl for<'a> FnOnce(InferCtxt<'a, 'tcx>) -> R) -> R {
let InferCtxtBuilder { tcx, defining_use_anchor, ref fresh_typeck_results } = *self;
let InferCtxtBuilder {
tcx,
defining_use_anchor,
reveal_defining_opaque_types,
ref fresh_typeck_results,
} = *self;
let in_progress_typeck_results = fresh_typeck_results.as_ref();
f(InferCtxt {
tcx,
defining_use_anchor,
reveal_defining_opaque_types,
in_progress_typeck_results,
inner: RefCell::new(InferCtxtInner::new()),
lexical_region_resolutions: RefCell::new(None),
@ -734,6 +751,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
&'a self,
trace: TypeTrace<'tcx>,
param_env: ty::ParamEnv<'tcx>,
define_opaque_types: bool,
) -> CombineFields<'a, 'tcx> {
CombineFields {
infcx: self,
@ -741,6 +759,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
cause: None,
param_env,
obligations: PredicateObligations::new(),
define_opaque_types,
}
}
@ -1056,12 +1075,20 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
self.tcx.mk_ty_var(self.next_ty_var_id(origin))
}
pub fn next_ty_var_id_in_universe(
&self,
origin: TypeVariableOrigin,
universe: ty::UniverseIndex,
) -> TyVid {
self.inner.borrow_mut().type_variables().new_var(universe, origin)
}
pub fn next_ty_var_in_universe(
&self,
origin: TypeVariableOrigin,
universe: ty::UniverseIndex,
) -> Ty<'tcx> {
let vid = self.inner.borrow_mut().type_variables().new_var(universe, origin);
let vid = self.next_ty_var_id_in_universe(origin, universe);
self.tcx.mk_ty_var(vid)
}

View file

@ -24,11 +24,13 @@
use crate::infer::combine::ConstEquateRelation;
use crate::infer::InferCtxt;
use crate::infer::{ConstVarValue, ConstVariableValue};
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::fold::{TypeFoldable, TypeVisitor};
use rustc_middle::ty::relate::{self, Relate, RelateResult, TypeRelation};
use rustc_middle::ty::{self, InferConst, Ty, TyCtxt};
use rustc_span::Span;
use std::fmt::Debug;
use std::ops::ControlFlow;
@ -75,6 +77,7 @@ where
pub trait TypeRelatingDelegate<'tcx> {
fn param_env(&self) -> ty::ParamEnv<'tcx>;
fn span(&self) -> Span;
/// Push a constraint `sup: sub` -- this constraint must be
/// satisfied for the two types to be related. `sub` and `sup` may
@ -87,6 +90,8 @@ pub trait TypeRelatingDelegate<'tcx> {
info: ty::VarianceDiagInfo<'tcx>,
);
fn register_opaque_type(&mut self, a: Ty<'tcx>, b: Ty<'tcx>, a_is_expected: bool);
fn const_equate(&mut self, a: &'tcx ty::Const<'tcx>, b: &'tcx ty::Const<'tcx>);
/// Creates a new universe index. Used when instantiating placeholders.
@ -277,7 +282,6 @@ where
projection_ty: ty::ProjectionTy<'tcx>,
value_ty: Ty<'tcx>,
) -> Ty<'tcx> {
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use rustc_span::DUMMY_SP;
match *value_ty.kind() {
@ -286,6 +290,8 @@ where
kind: TypeVariableOriginKind::MiscVariable,
span: DUMMY_SP,
});
// FIXME(lazy-normalization): This will always ICE, because the recursive
// call will end up in the _ arm below.
self.relate_projection_ty(projection_ty, var);
self.relate_projection_ty(other_projection_ty, var);
var
@ -531,6 +537,8 @@ where
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, mut b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
let infcx = self.infcx;
let a = self.infcx.shallow_resolve(a);
if !D::forbid_inference_vars() {
@ -559,6 +567,35 @@ where
(&ty::Infer(ty::TyVar(vid)), _) => self.relate_ty_var((vid, b)),
(&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => {
self.infcx.super_combine_tys(self, a, b)
}
(&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..)) if did.is_local() => {
let (a, b) = if self.a_is_expected() { (a, b) } else { (b, a) };
let mut generalize = |ty, ty_is_expected| {
let var = infcx.next_ty_var_id_in_universe(
TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: self.delegate.span(),
},
ty::UniverseIndex::ROOT,
);
if ty_is_expected {
self.relate_ty_var((ty, var))
} else {
self.relate_ty_var((var, ty))
}
};
let (a, b) = match (a.kind(), b.kind()) {
(&ty::Opaque(..), _) => (a, generalize(b, false)?),
(_, &ty::Opaque(..)) => (generalize(a, true)?, b),
_ => unreachable!(),
};
self.delegate.register_opaque_type(a, b, true);
trace!(a = ?a.kind(), b = ?b.kind(), "opaque type instantiated");
Ok(a)
}
(&ty::Projection(projection_ty), _)
if D::normalization() == NormalizationStrategy::Lazy =>
{

View file

@ -1,10 +1,11 @@
use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::infer::{InferCtxt, InferOk};
use crate::traits;
use crate::traits::{self, PredicateObligation};
use hir::def_id::{DefId, LocalDefId};
use hir::OpaqueTyOrigin;
use rustc_data_structures::sync::Lrc;
use rustc_data_structures::vec_map::VecMap;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::fold::BottomUpFolder;
use rustc_middle::ty::subst::{GenericArgKind, Subst};
use rustc_middle::ty::{self, OpaqueTypeKey, Ty, TyCtxt, TypeFoldable, TypeVisitor};
@ -14,14 +15,28 @@ use std::ops::ControlFlow;
pub type OpaqueTypeMap<'tcx> = VecMap<OpaqueTypeKey<'tcx>, OpaqueTypeDecl<'tcx>>;
mod table;
pub use table::{OpaqueTypeStorage, OpaqueTypeTable};
use super::InferResult;
/// 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)]
#[derive(Clone, Debug)]
pub struct OpaqueTypeDecl<'tcx> {
/// The opaque type (`ty::Opaque`) for this declaration.
pub opaque_type: Ty<'tcx>,
/// The hidden types that have been inferred for this opaque type.
/// There can be multiple, but they are all `lub`ed together at the end
/// to obtain the canonical hidden type.
pub hidden_type: OpaqueHiddenType<'tcx>,
/// The origin of the opaque type.
pub origin: hir::OpaqueTyOrigin,
}
#[derive(Copy, Clone, Debug, TypeFoldable)]
pub struct OpaqueHiddenType<'tcx> {
/// The span of this particular definition of the opaque type. So
/// for example:
///
@ -35,7 +50,7 @@ pub struct OpaqueTypeDecl<'tcx> {
/// 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,
pub span: Span,
/// The type variable that represents the value of the opaque type
/// that we require. In other words, after we compile this function,
@ -49,54 +64,132 @@ pub struct OpaqueTypeDecl<'tcx> {
/// 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,
pub ty: Ty<'tcx>,
}
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:
///
/// impl Iterator<Item = impl Debug>
///
/// this method would create two type variables, `?0` and `?1`. It would
/// return the type `?0` but also the obligations:
///
/// ?0: Iterator<Item = ?1>
/// ?1: Debug
///
/// Moreover, it returns an `OpaqueTypeMap` that would map `?0` to
/// info about the `impl Iterator<..>` type and `?1` to info about
/// the `impl Debug` type.
///
/// # Parameters
///
/// - `parent_def_id` -- the `DefId` of the function in which the opaque type
/// is defined
/// - `body_id` -- the body-id with which the resulting obligations should
/// be associated
/// - `param_env` -- the in-scope parameter environment to be used for
/// obligations
/// - `value` -- the value within which we are instantiating opaque types
/// - `value_span` -- the span where the value came from, used in error reporting
pub fn instantiate_opaque_types<T: TypeFoldable<'tcx>>(
pub fn handle_opaque_type(
&self,
body_id: hir::HirId,
a: Ty<'tcx>,
b: Ty<'tcx>,
cause: &ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
value: T,
value_span: Span,
) -> InferOk<'tcx, T> {
debug!(
"instantiate_opaque_types(value={:?}, body_id={:?}, \
param_env={:?}, value_span={:?})",
value, body_id, param_env, value_span,
);
let mut instantiator =
Instantiator { infcx: self, body_id, param_env, value_span, obligations: vec![] };
let value = instantiator.instantiate_opaque_types_in_map(value);
InferOk { value, obligations: instantiator.obligations }
) -> InferResult<'tcx, ()> {
if a.references_error() || b.references_error() {
return Ok(InferOk { value: (), obligations: vec![] });
}
if self.defining_use_anchor.is_some() {
let process = |a: Ty<'tcx>, b: Ty<'tcx>| match *a.kind() {
ty::Opaque(def_id, substs) => {
if let ty::Opaque(did2, _) = *b.kind() {
// We could accept this, but there are various ways to handle this situation, and we don't
// want to make a decision on it right now. Likely this case is so super rare anyway, that
// no one encounters it in practice.
// It does occur however in `fn fut() -> impl Future<Output = i32> { async { 42 } }`,
// where it is of no concern, so we only check for TAITs.
if let Some(OpaqueTyOrigin::TyAlias) =
self.opaque_type_origin(did2, cause.span)
{
self.tcx
.sess
.struct_span_err(
cause.span,
"opaque type's hidden type cannot be another opaque type from the same scope",
)
.span_label(cause.span, "one of the two opaque types used here has to be outside its defining scope")
.span_note(
self.tcx.def_span(def_id),
"opaque type whose hidden type is being assigned",
)
.span_note(
self.tcx.def_span(did2),
"opaque type being used as hidden type",
)
.emit();
}
}
Some(self.register_hidden_type(
OpaqueTypeKey { def_id, substs },
cause.clone(),
param_env,
b,
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
// value we are inferring. At present, this is
// always true during the first phase of
// type-check, but not always true later on during
// NLL. Once we support named opaque types more fully,
// this same scenario will be able to arise during all phases.
//
// Here is an example using type alias `impl Trait`
// that indicates the distinction we are checking for:
//
// ```rust
// mod a {
// pub type Foo = impl Iterator;
// pub fn make_foo() -> Foo { .. }
// }
//
// mod b {
// fn foo() -> a::Foo { a::make_foo() }
// }
// ```
//
// Here, the return type of `foo` references an
// `Opaque` indeed, but not one whose value is
// presently being inferred. You can get into a
// similar situation with closure return types
// today:
//
// ```rust
// fn foo() -> impl Iterator { .. }
// fn bar() {
// let x = || foo(); // returns the Opaque assoc with `foo`
// }
// ```
self.opaque_type_origin(def_id, cause.span)?,
))
}
_ => None,
};
if let Some(res) = process(a, b) {
res
} else if let Some(res) = process(b, a) {
res
} else {
// Rerun equality check, but this time error out due to
// different types.
match self.at(cause, param_env).define_opaque_types(false).eq(a, b) {
Ok(_) => span_bug!(
cause.span,
"opaque types are never equal to anything but themselves: {:#?}",
(a, b)
),
Err(e) => Err(e),
}
}
} else {
let (opaque_type, hidden_ty) = match (a.kind(), b.kind()) {
(ty::Opaque(..), _) => (a, b),
(_, ty::Opaque(..)) => (b, a),
types => span_bug!(
cause.span,
"opaque type obligations only work for opaque types: {:#?}",
types
),
};
let key = opaque_type.expect_opaque_type();
let origin = self.opaque_ty_origin_unchecked(key.def_id, cause.span);
let prev = self.inner.borrow_mut().opaque_types().register(
key,
OpaqueHiddenType { ty: hidden_ty, span: cause.span },
origin,
);
match prev {
Some(prev) => self.at(cause, param_env).eq(prev, hidden_ty),
None => Ok(InferOk { value: (), obligations: vec![] }),
}
}
}
/// Given the map `opaque_types` containing the opaque
@ -231,51 +324,23 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// but this is not necessary, because the opaque type we
/// create will be allowed to reference `T`. So we only generate a
/// constraint that `'0: 'a`.
///
/// # The `free_region_relations` parameter
///
/// The `free_region_relations` argument is used to find the
/// "minimum" of the regions supplied to a given opaque type.
/// It must be a relation that can answer whether `'a <= 'b`,
/// where `'a` and `'b` are regions that appear in the "substs"
/// for the opaque type references (the `<'a>` in `Foo1<'a>`).
///
/// Note that we do not impose the constraints based on the
/// generic regions from the `Foo1` definition (e.g., `'x`). This
/// is because the constraints we are imposing here is basically
/// the concern of the one generating the constraining type C1,
/// which is the current function. It also means that we can
/// take "implied bounds" into account in some cases:
///
/// ```text
/// trait SomeTrait<'a, 'b> { }
/// fn foo<'a, 'b>(_: &'a &'b u32) -> impl SomeTrait<'a, 'b> { .. }
/// ```
///
/// Here, the fact that `'b: 'a` is known only because of the
/// implied bounds from the `&'a &'b u32` parameter, and is not
/// "inherent" to the opaque type definition.
///
/// # Parameters
///
/// - `opaque_types` -- the map produced by `instantiate_opaque_types`
/// - `free_region_relations` -- something that can be used to relate
/// the free regions (`'a`) that appear in the impl trait.
#[instrument(level = "debug", skip(self))]
pub fn constrain_opaque_type(
pub fn register_member_constraints(
&self,
param_env: ty::ParamEnv<'tcx>,
opaque_type_key: OpaqueTypeKey<'tcx>,
opaque_defn: &OpaqueTypeDecl<'tcx>,
concrete_ty: Ty<'tcx>,
span: Span,
) {
let def_id = opaque_type_key.def_id;
let tcx = self.tcx;
let concrete_ty = self.resolve_vars_if_possible(opaque_defn.concrete_ty);
let concrete_ty = self.resolve_vars_if_possible(concrete_ty);
debug!(?concrete_ty);
let first_own_region = match opaque_defn.origin {
let first_own_region = match self.opaque_ty_origin_unchecked(def_id, span) {
hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {
// We lower
//
@ -319,7 +384,7 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
op: |r| {
self.member_constraint(
opaque_type_key.def_id,
opaque_defn.definition_span,
span,
concrete_ty,
r,
&choice_regions,
@ -328,15 +393,34 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
});
}
fn opaque_type_origin(&self, def_id: LocalDefId) -> Option<hir::OpaqueTyOrigin> {
let tcx = self.tcx;
let opaque_hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
pub fn opaque_ty_obligation(
&self,
a: Ty<'tcx>,
b: Ty<'tcx>,
a_is_expected: bool,
param_env: ty::ParamEnv<'tcx>,
cause: ObligationCause<'tcx>,
) -> PredicateObligation<'tcx> {
let (a, b) = if a_is_expected { (a, b) } else { (b, a) };
PredicateObligation::new(
cause,
param_env,
self.tcx.mk_predicate(ty::Binder::dummy(ty::PredicateKind::OpaqueType(a, b))),
)
}
#[instrument(skip(self), level = "trace")]
pub fn opaque_type_origin(&self, opaque_def_id: DefId, span: Span) -> Option<OpaqueTyOrigin> {
let def_id = opaque_def_id.as_local()?;
let opaque_hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
let parent_def_id = self.defining_use_anchor?;
let item_kind = &tcx.hir().expect_item(def_id).kind;
let item_kind = &self.tcx.hir().expect_item(def_id).kind;
let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item_kind else {
span_bug!(
tcx.def_span(def_id),
"weird opaque type: {:#?}",
span,
"weird opaque type: {:#?}, {:#?}",
opaque_def_id,
item_kind
)
};
@ -347,11 +431,29 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
hir::OpaqueTyOrigin::FnReturn(parent) => parent == parent_def_id,
// Named `type Foo = impl Bar;`
hir::OpaqueTyOrigin::TyAlias => {
may_define_opaque_type(tcx, parent_def_id, opaque_hir_id)
may_define_opaque_type(self.tcx, parent_def_id, opaque_hir_id)
}
};
trace!(?origin);
in_definition_scope.then_some(*origin)
}
#[instrument(skip(self), level = "trace")]
fn opaque_ty_origin_unchecked(&self, opaque_def_id: DefId, span: Span) -> OpaqueTyOrigin {
let def_id = opaque_def_id.as_local().unwrap();
let origin = match self.tcx.hir().expect_item(def_id).kind {
hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => origin,
ref itemkind => {
span_bug!(span, "weird opaque type: {:?}, {:#?}", opaque_def_id, itemkind)
}
};
trace!(?origin);
origin
}
pub fn opaque_types(&self) -> OpaqueTypeMap<'tcx> {
self.inner.borrow().opaque_type_storage.opaque_types()
}
}
// Visitor that requires that (almost) all regions in the type visited outlive
@ -426,180 +528,93 @@ where
}
}
struct Instantiator<'a, 'tcx> {
infcx: &'a InferCtxt<'a, 'tcx>,
body_id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
value_span: Span,
obligations: Vec<traits::PredicateObligation<'tcx>>,
pub enum UseKind {
DefiningUse,
OpaqueUse,
}
impl<'a, 'tcx> Instantiator<'a, 'tcx> {
fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
let tcx = self.infcx.tcx;
value.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |ty| {
if ty.references_error() {
return tcx.ty_error();
} else if let ty::Opaque(def_id, substs) = ty.kind() {
// Check that this is `impl Trait` type is
// declared by `parent_def_id` -- i.e., one whose
// value we are inferring. At present, this is
// always true during the first phase of
// type-check, but not always true later on during
// NLL. Once we support named opaque types more fully,
// this same scenario will be able to arise during all phases.
//
// Here is an example using type alias `impl Trait`
// that indicates the distinction we are checking for:
//
// ```rust
// mod a {
// pub type Foo = impl Iterator;
// pub fn make_foo() -> Foo { .. }
// }
//
// mod b {
// fn foo() -> a::Foo { a::make_foo() }
// }
// ```
//
// Here, the return type of `foo` references an
// `Opaque` indeed, but not one whose value is
// presently being inferred. You can get into a
// similar situation with closure return types
// today:
//
// ```rust
// fn foo() -> impl Iterator { .. }
// fn bar() {
// let x = || foo(); // returns the Opaque assoc with `foo`
// }
// ```
if let Some(def_id) = def_id.as_local() {
if let Some(origin) = self.infcx.opaque_type_origin(def_id) {
let opaque_type_key =
OpaqueTypeKey { def_id: def_id.to_def_id(), substs };
return self.fold_opaque_ty(ty, opaque_type_key, origin);
}
debug!(
"instantiate_opaque_types_in_map: \
encountered opaque outside its definition scope \
def_id={:?}",
def_id,
);
}
}
ty
},
lt_op: |lt| lt,
ct_op: |ct| ct,
})
}
#[instrument(skip(self), level = "debug")]
fn fold_opaque_ty(
&mut self,
ty: Ty<'tcx>,
opaque_type_key: OpaqueTypeKey<'tcx>,
origin: hir::OpaqueTyOrigin,
) -> Ty<'tcx> {
let infcx = self.infcx;
let tcx = infcx.tcx;
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
// Use the same type variable if the exact same opaque type appears more
// than once in the return type (e.g., if it's passed to a type alias).
if let Some(opaque_defn) = infcx.inner.borrow().opaque_types.get(&opaque_type_key) {
debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind());
return opaque_defn.concrete_ty;
impl UseKind {
pub fn is_defining(self) -> bool {
match self {
UseKind::DefiningUse => true,
UseKind::OpaqueUse => false,
}
}
}
let ty_var = infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span: self.value_span,
});
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
fn register_hidden_type(
&self,
opaque_type_key: OpaqueTypeKey<'tcx>,
cause: ObligationCause<'tcx>,
param_env: ty::ParamEnv<'tcx>,
hidden_ty: Ty<'tcx>,
origin: hir::OpaqueTyOrigin,
) -> InferResult<'tcx, ()> {
let tcx = self.tcx;
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
// Ideally, we'd get the span where *this specific `ty` came
// from*, but right now we just use the span from the overall
// value being folded. In simple cases like `-> impl Foo`,
// these are the same span, but not in cases like `-> (impl
// Foo, impl Bar)`.
let definition_span = self.value_span;
let span = cause.span;
{
let mut infcx = self.infcx.inner.borrow_mut();
infcx.opaque_types.insert(
OpaqueTypeKey { def_id, substs },
OpaqueTypeDecl { opaque_type: ty, definition_span, concrete_ty: ty_var, origin },
);
infcx.opaque_types_vars.insert(ty_var, ty);
let mut obligations = vec![];
let prev = self.inner.borrow_mut().opaque_types().register(
OpaqueTypeKey { def_id, substs },
OpaqueHiddenType { ty: hidden_ty, span },
origin,
);
if let Some(prev) = prev {
obligations = self.at(&cause, param_env).eq(prev, hidden_ty)?.obligations;
}
debug!("generated new type inference var {:?}", ty_var.kind());
let item_bounds = tcx.explicit_item_bounds(def_id);
self.obligations.reserve(item_bounds.len());
for (predicate, _) in item_bounds {
debug!(?predicate);
let predicate = predicate.subst(tcx, substs);
debug!(?predicate);
let predicate = predicate.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |ty| match *ty.kind() {
// We can't normalize associated types from `rustc_infer`,
// but we can eagerly register inference variables for them.
ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => {
self.infer_projection(
param_env,
projection_ty,
cause.clone(),
0,
&mut obligations,
)
}
// Replace all other mentions of the same opaque type with the hidden type,
// as the bounds must hold on the hidden type after all.
ty::Opaque(def_id2, substs2) if def_id == def_id2 && substs == substs2 => {
ty_var
}
// Instantiate nested instances of `impl Trait`.
ty::Opaque(..) => self.instantiate_opaque_types_in_map(ty),
_ => ty,
},
lt_op: |lt| lt,
ct_op: |ct| ct,
});
// We can't normalize associated types from `rustc_infer`, but we can eagerly register inference variables for them.
let predicate = predicate.fold_with(&mut BottomUpFolder {
tcx,
ty_op: |ty| match ty.kind() {
ty::Projection(projection_ty) if !projection_ty.has_escaping_bound_vars() => {
infcx.infer_projection(
self.param_env,
*projection_ty,
traits::ObligationCause::misc(self.value_span, self.body_id),
0,
&mut self.obligations,
)
hidden_ty
}
_ => ty,
},
lt_op: |lt| lt,
ct_op: |ct| ct,
});
debug!(?predicate);
if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() {
if projection.term.references_error() {
return tcx.ty_error();
// No point on adding these obligations since there's a type error involved.
return Ok(InferOk { value: (), obligations: vec![] });
}
trace!("{:#?}", projection.term);
}
let cause =
traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType);
// Require that the predicate holds for the concrete type.
debug!(?predicate);
self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate));
obligations.push(traits::Obligation::new(cause.clone(), param_env, predicate));
}
ty_var
Ok(InferOk { value: (), obligations })
}
}

View file

@ -0,0 +1,88 @@
use rustc_data_structures::undo_log::UndoLogs;
use rustc_hir::OpaqueTyOrigin;
use rustc_middle::ty::{self, OpaqueTypeKey, Ty};
use rustc_span::DUMMY_SP;
use crate::infer::{InferCtxtUndoLogs, UndoLog};
use super::{OpaqueHiddenType, OpaqueTypeDecl, OpaqueTypeMap};
#[derive(Default, Debug)]
pub struct OpaqueTypeStorage<'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>,
}
impl<'tcx> OpaqueTypeStorage<'tcx> {
#[instrument(level = "debug")]
pub(crate) fn remove(&mut self, key: OpaqueTypeKey<'tcx>, idx: Option<OpaqueHiddenType<'tcx>>) {
if let Some(idx) = idx {
self.opaque_types.get_mut(&key).unwrap().hidden_type = idx;
} else {
match self.opaque_types.remove(&key) {
None => bug!("reverted opaque type inference that was never registered: {:?}", key),
Some(_) => {}
}
}
}
pub fn get_decl(&self, key: &OpaqueTypeKey<'tcx>) -> Option<&OpaqueTypeDecl<'tcx>> {
self.opaque_types.get(key)
}
pub fn opaque_types(&self) -> OpaqueTypeMap<'tcx> {
self.opaque_types.clone()
}
#[instrument(level = "debug")]
pub fn take_opaque_types(&mut self) -> OpaqueTypeMap<'tcx> {
std::mem::take(&mut self.opaque_types)
}
#[inline]
pub(crate) fn with_log<'a>(
&'a mut self,
undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
) -> OpaqueTypeTable<'a, 'tcx> {
OpaqueTypeTable { storage: self, undo_log }
}
}
impl<'tcx> Drop for OpaqueTypeStorage<'tcx> {
fn drop(&mut self) {
if !self.opaque_types.is_empty() {
ty::tls::with(|tcx| {
tcx.sess.delay_span_bug(DUMMY_SP, &format!("{:?}", self.opaque_types))
});
}
}
}
pub struct OpaqueTypeTable<'a, 'tcx> {
storage: &'a mut OpaqueTypeStorage<'tcx>,
undo_log: &'a mut InferCtxtUndoLogs<'tcx>,
}
impl<'a, 'tcx> OpaqueTypeTable<'a, 'tcx> {
#[instrument(skip(self), level = "debug")]
pub fn register(
&mut self,
key: OpaqueTypeKey<'tcx>,
hidden_type: OpaqueHiddenType<'tcx>,
origin: OpaqueTyOrigin,
) -> Option<Ty<'tcx>> {
if let Some(decl) = self.storage.opaque_types.get_mut(&key) {
let prev = std::mem::replace(&mut decl.hidden_type, hidden_type);
self.undo_log.push(UndoLog::OpaqueTypes(key, Some(prev)));
return Some(prev.ty);
}
let decl = OpaqueTypeDecl { hidden_type, origin };
self.storage.opaque_types.insert(key, decl);
self.undo_log.push(UndoLog::OpaqueTypes(key, None));
None
}
}

View file

@ -28,6 +28,7 @@ pub fn explicit_outlives_bounds<'tcx>(
| ty::PredicateKind::TypeOutlives(..)
| ty::PredicateKind::ConstEvaluatable(..)
| ty::PredicateKind::ConstEquate(..)
| ty::PredicateKind::OpaqueType(..)
| ty::PredicateKind::TypeWellFormedFromEnv(..) => None,
ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(r_a, r_b)) => {
Some(OutlivesBound::RegionSubRegion(r_b, r_a))

View file

@ -153,6 +153,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
/// This function may have to perform normalizations, and hence it
/// returns an `InferOk` with subobligations that must be
/// processed.
#[instrument(level = "debug", skip(self, region_bound_pairs_map))]
pub fn process_registered_region_obligations(
&self,
region_bound_pairs_map: &FxHashMap<hir::HirId, RegionBoundPairs<'tcx>>,
@ -164,8 +165,6 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
"cannot process registered region obligations in a snapshot"
);
debug!(?param_env, "process_registered_region_obligations()");
let my_region_obligations = self.take_registered_region_obligations();
for (body_id, RegionObligation { sup_type, sub_region, origin }) in my_region_obligations {

View file

@ -2,6 +2,7 @@ use super::combine::{CombineFields, RelationDir};
use super::SubregionOrigin;
use crate::infer::combine::ConstEquateRelation;
use crate::infer::{TypeVariableOrigin, TypeVariableOriginKind};
use crate::traits::Obligation;
use rustc_middle::ty::fold::TypeFoldable;
use rustc_middle::ty::relate::{Cause, Relate, RelateResult, TypeRelation};
@ -74,9 +75,8 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
}
}
#[instrument(skip(self), level = "debug")]
fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> {
debug!("{}.tys({:?}, {:?})", self.tag(), a, b);
if a == b {
return Ok(a);
}
@ -84,6 +84,7 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
let infcx = self.fields.infcx;
let a = infcx.inner.borrow_mut().type_variables().replace_if_possible(a);
let b = infcx.inner.borrow_mut().type_variables().replace_if_possible(b);
match (a.kind(), b.kind()) {
(&ty::Infer(TyVar(_)), &ty::Infer(TyVar(_))) => {
// Shouldn't have any LBR here, so we can safely put
@ -121,6 +122,40 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
Ok(self.tcx().ty_error())
}
(&ty::Opaque(a_def_id, _), &ty::Opaque(b_def_id, _)) if a_def_id == b_def_id => {
self.fields.infcx.super_combine_tys(self, a, b)?;
Ok(a)
}
(&ty::Opaque(did, ..), _) | (_, &ty::Opaque(did, ..))
if self.fields.define_opaque_types && did.is_local() =>
{
let mut generalize = |ty, ty_is_expected| {
let var = infcx.next_ty_var_id_in_universe(
TypeVariableOrigin {
kind: TypeVariableOriginKind::MiscVariable,
span: self.fields.trace.cause.span,
},
ty::UniverseIndex::ROOT,
);
self.fields.instantiate(ty, RelationDir::SubtypeOf, var, ty_is_expected)?;
Ok(infcx.tcx.mk_ty_var(var))
};
let (a, b) = if self.a_is_expected { (a, b) } else { (b, a) };
let (a, b) = match (a.kind(), b.kind()) {
(&ty::Opaque(..), _) => (a, generalize(b, true)?),
(_, &ty::Opaque(..)) => (generalize(a, false)?, b),
_ => unreachable!(),
};
self.fields.obligations.push(infcx.opaque_ty_obligation(
a,
b,
true,
self.param_env(),
self.fields.trace.cause.clone(),
));
Ok(a)
}
_ => {
self.fields.infcx.super_combine_tys(self, a, b)?;
Ok(a)

View file

@ -4,13 +4,15 @@ use rustc_data_structures::snapshot_vec as sv;
use rustc_data_structures::undo_log::{Rollback, UndoLogs};
use rustc_data_structures::unify as ut;
use rustc_middle::infer::unify_key::RegionVidKey;
use rustc_middle::ty;
use rustc_middle::ty::{self, OpaqueTypeKey};
use crate::{
infer::{region_constraints, type_variable, InferCtxtInner},
traits,
};
use super::opaque_types::OpaqueHiddenType;
pub struct Snapshot<'tcx> {
pub(crate) undo_len: usize,
_marker: PhantomData<&'tcx ()>,
@ -18,6 +20,7 @@ pub struct Snapshot<'tcx> {
/// Records the "undo" data for a single operation that affects some form of inference variable.
pub(crate) enum UndoLog<'tcx> {
OpaqueTypes(OpaqueTypeKey<'tcx>, Option<OpaqueHiddenType<'tcx>>),
TypeVariables(type_variable::UndoLog<'tcx>),
ConstUnificationTable(sv::UndoLog<ut::Delegate<ty::ConstVid<'tcx>>>),
IntUnificationTable(sv::UndoLog<ut::Delegate<ty::IntVid>>),
@ -64,6 +67,7 @@ impl_from! {
impl<'tcx> Rollback<UndoLog<'tcx>> for InferCtxtInner<'tcx> {
fn reverse(&mut self, undo: UndoLog<'tcx>) {
match undo {
UndoLog::OpaqueTypes(key, idx) => self.opaque_type_storage.remove(key, idx),
UndoLog::TypeVariables(undo) => self.type_variable_storage.reverse(undo),
UndoLog::ConstUnificationTable(undo) => self.const_unification_storage.reverse(undo),
UndoLog::IntUnificationTable(undo) => self.int_unification_storage.reverse(undo),

View file

@ -167,6 +167,9 @@ impl<'tcx> Elaborator<'tcx> {
// Currently, we do not elaborate WF predicates,
// although we easily could.
}
ty::PredicateKind::OpaqueType(..) => {
todo!("{:#?}", obligation)
}
ty::PredicateKind::ObjectSafe(..) => {
// Currently, we do not elaborate object-safe
// predicates.