Skip to content

Commit

Permalink
Auto merge of rust-lang#89045 - oli-obk:lazy_normalization_in_opaque_…
Browse files Browse the repository at this point in the history
…types, r=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.