Skip to content

Commit

Permalink
Auto merge of #89045 - oli-obk:lazy_normalization_in_opaque_types, r=…
Browse files Browse the repository at this point in the history
…nikomatsakis

Register normalization obligations instead of immediately normalizing in opaque type instantiation

For lazy TAIT we will need to instantiate opaque types from within `rustc_infer`, which cannot invoke normalization methods (they are in `rustc_trait_resolution`). So before we move the logic over to `rustc_infer`, we need make sure no normalization happens anymore. This PR resolves that by just registering normalization obligations and continuing.

This PR is best reviewed commit by commit

I also included f7ad36e which is just an independent cleanup that touches the same code and reduces diagnostics noise a bit

r? `@nikomatsakis` cc `@spastorino`
  • Loading branch information
bors committed Sep 21, 2021
2 parents 49c0861 + afb7472 commit dda2a0e
Show file tree
Hide file tree
Showing 8 changed files with 78 additions and 76 deletions.
1 change: 1 addition & 0 deletions compiler/rustc_infer/src/infer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ mod lub;
pub mod nll_relate;
pub mod opaque_types;
pub mod outlives;
mod projection;
pub mod region_constraints;
pub mod resolve;
mod sub;
Expand Down
39 changes: 39 additions & 0 deletions compiler/rustc_infer/src/infer/projection.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
use rustc_middle::traits::ObligationCause;
use rustc_middle::ty::{self, ToPredicate, Ty};

use crate::traits::{Obligation, PredicateObligation};

use super::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
use super::InferCtxt;

impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
/// Instead of normalizing an associated type projection,
/// this function generates an inference variable and registers
/// an obligation that this inference variable must be the result
/// of the given projection. This allows us to proceed with projections
/// while they cannot be resolved yet due to missing information or
/// simply due to the lack of access to the trait resolution machinery.
pub fn infer_projection(
&self,
param_env: ty::ParamEnv<'tcx>,
projection_ty: ty::ProjectionTy<'tcx>,
cause: ObligationCause<'tcx>,
recursion_depth: usize,
obligations: &mut Vec<PredicateObligation<'tcx>>,
) -> Ty<'tcx> {
let def_id = projection_ty.item_def_id;
let ty_var = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::NormalizeProjectionType,
span: self.tcx.def_span(def_id),
});
let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var });
let obligation = Obligation::with_depth(
cause,
recursion_depth,
param_env,
projection.to_predicate(self.tcx),
);
obligations.push(obligation);
ty_var
}
}
71 changes: 31 additions & 40 deletions compiler/rustc_trait_selection/src/opaque_types.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use crate::infer::InferCtxtExt as _;
use crate::traits::{self, ObligationCause, PredicateObligation};
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sync::Lrc;
Expand Down Expand Up @@ -863,7 +862,6 @@ struct Instantiator<'a, 'tcx> {
}

impl<'a, 'tcx> Instantiator<'a, 'tcx> {
#[instrument(level = "debug", skip(self))]
fn instantiate_opaque_types_in_map<T: TypeFoldable<'tcx>>(&mut self, value: T) -> T {
let tcx = self.infcx.tcx;
value.fold_with(&mut BottomUpFolder {
Expand Down Expand Up @@ -954,6 +952,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
})
}

#[instrument(skip(self), level = "debug")]
fn fold_opaque_ty(
&mut self,
ty: Ty<'tcx>,
Expand All @@ -964,25 +963,18 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
let tcx = infcx.tcx;
let OpaqueTypeKey { def_id, substs } = opaque_type_key;

debug!("instantiate_opaque_types: Opaque(def_id={:?}, substs={:?})", def_id, substs);

// 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!("instantiate_opaque_types: returning concrete ty {:?}", opaque_defn.concrete_ty);
debug!("re-using cached concrete type {:?}", opaque_defn.concrete_ty.kind());
return opaque_defn.concrete_ty;
}

let ty_var = infcx.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::TypeInference,
span: self.value_span,
});

// Make sure that we are in fact defining the *entire* type
// (e.g., `type Foo<T: Bound> = impl Bar;` needs to be
// defined by a function like `fn foo<T: Bound>() -> Foo<T>`).
debug!("instantiate_opaque_types: param_env={:#?}", self.param_env,);
debug!("instantiate_opaque_types: generics={:#?}", tcx.generics_of(def_id),);

// 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`,
Expand All @@ -999,43 +991,40 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
infcx.opaque_types_vars.insert(ty_var, ty);
}

debug!("instantiate_opaque_types: ty_var={:?}", ty_var);
self.compute_opaque_type_obligations(opaque_type_key);

ty_var
}

fn compute_opaque_type_obligations(&mut self, opaque_type_key: OpaqueTypeKey<'tcx>) {
let infcx = self.infcx;
let tcx = infcx.tcx;
let OpaqueTypeKey { def_id, substs } = opaque_type_key;
debug!("generated new type inference var {:?}", ty_var.kind());

let item_bounds = tcx.explicit_item_bounds(def_id);
debug!("instantiate_opaque_types: bounds={:#?}", item_bounds);
let bounds: Vec<_> =
item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect();

let param_env = tcx.param_env(def_id);
let InferOk { value: bounds, obligations } = infcx.partially_normalize_associated_types_in(
ObligationCause::misc(self.value_span, self.body_id),
param_env,
bounds,
);
self.obligations.extend(obligations);

debug!("instantiate_opaque_types: bounds={:?}", bounds);
self.obligations.reserve(item_bounds.len());
for (predicate, _) in item_bounds {
debug!(?predicate);
let predicate = predicate.subst(tcx, substs);
debug!(?predicate);

// 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) => infcx.infer_projection(
self.param_env,
*projection_ty,
ObligationCause::misc(self.value_span, self.body_id),
0,
&mut self.obligations,
),
_ => ty,
},
lt_op: |lt| lt,
ct_op: |ct| ct,
});
debug!(?predicate);

for predicate in &bounds {
if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() {
if projection.ty.references_error() {
// No point on adding these obligations since there's a type error involved.
return;
return tcx.ty_error();
}
}
}

self.obligations.reserve(bounds.len());
for predicate in bounds {
// Change the predicate to refer to the type variable,
// which will be the concrete type instead of the opaque type.
// This also instantiates nested instances of `impl Trait`.
Expand All @@ -1045,9 +1034,11 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> {
traits::ObligationCause::new(self.value_span, self.body_id, traits::OpaqueType);

// Require that the predicate holds for the concrete type.
debug!("instantiate_opaque_types: predicate={:?}", predicate);
debug!(?predicate);
self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate));
}

ty_var
}
}

Expand Down
12 changes: 1 addition & 11 deletions compiler/rustc_trait_selection/src/traits/project.rs
Original file line number Diff line number Diff line change
Expand Up @@ -810,17 +810,7 @@ pub fn normalize_projection_type<'a, 'b, 'tcx>(
// and a deferred predicate to resolve this when more type
// information is available.

let tcx = selcx.infcx().tcx;
let def_id = projection_ty.item_def_id;
let ty_var = selcx.infcx().next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::NormalizeProjectionType,
span: tcx.def_span(def_id),
});
let projection = ty::Binder::dummy(ty::ProjectionPredicate { projection_ty, ty: ty_var });
let obligation =
Obligation::with_depth(cause, depth + 1, param_env, projection.to_predicate(tcx));
obligations.push(obligation);
ty_var
selcx.infcx().infer_projection(param_env, projection_ty, cause, depth + 1, obligations)
})
}

Expand Down
1 change: 0 additions & 1 deletion src/test/ui/async-await/issues/issue-65159.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ async fn copy() -> Result<()>
//~^ ERROR this enum takes 2 generic arguments
{
Ok(())
//~^ ERROR type annotations needed
}

fn main() { }
11 changes: 2 additions & 9 deletions src/test/ui/async-await/issues/issue-65159.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,6 @@ help: add missing generic argument
LL | async fn copy() -> Result<(), E>
| +++

error[E0282]: type annotations needed
--> $DIR/issue-65159.rs:8:5
|
LL | Ok(())
| ^^ cannot infer type for type parameter `E` declared on the enum `Result`

error: aborting due to 2 previous errors
error: aborting due to previous error

Some errors have detailed explanations: E0107, E0282.
For more information about an error, try `rustc --explain E0107`.
For more information about this error, try `rustc --explain E0107`.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ async fn buy_lock(generator: &Mutex<MarketMultiplier>) -> LockedMarket<'_> {
//~^ ERROR this struct takes 0 lifetime arguments but 1 lifetime argument was supplied
//~^^ ERROR this struct takes 1 generic argument but 0 generic arguments were supplied
LockedMarket(generator.lock().unwrap().buy())
//~^ ERROR cannot return value referencing temporary value
}

struct LockedMarket<T>(T);
Expand Down
18 changes: 4 additions & 14 deletions src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ LL | async fn buy_lock(generator: &Mutex<MarketMultiplier>) -> LockedMarket<'_>
| expected 0 lifetime arguments
|
note: struct defined here, with 0 lifetime parameters
--> $DIR/issue-82126-mismatched-subst-and-hir.rs:23:8
--> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8
|
LL | struct LockedMarket<T>(T);
| ^^^^^^^^^^^^
Expand All @@ -19,7 +19,7 @@ LL | async fn buy_lock(generator: &Mutex<MarketMultiplier>) -> LockedMarket<'_>
| ^^^^^^^^^^^^ expected 1 generic argument
|
note: struct defined here, with 1 generic parameter: `T`
--> $DIR/issue-82126-mismatched-subst-and-hir.rs:23:8
--> $DIR/issue-82126-mismatched-subst-and-hir.rs:22:8
|
LL | struct LockedMarket<T>(T);
| ^^^^^^^^^^^^ -
Expand All @@ -28,16 +28,6 @@ help: add missing generic argument
LL | async fn buy_lock(generator: &Mutex<MarketMultiplier>) -> LockedMarket<'_, T> {
| +++

error[E0515]: cannot return value referencing temporary value
--> $DIR/issue-82126-mismatched-subst-and-hir.rs:19:5
|
LL | LockedMarket(generator.lock().unwrap().buy())
| ^^^^^^^^^^^^^-------------------------^^^^^^^
| | |
| | temporary value created here
| returns a value referencing data owned by the current function

error: aborting due to 3 previous errors
error: aborting due to 2 previous errors

Some errors have detailed explanations: E0107, E0515.
For more information about an error, try `rustc --explain E0107`.
For more information about this error, try `rustc --explain E0107`.

0 comments on commit dda2a0e

Please sign in to comment.