Skip to content

Register normalization obligations instead of immediately normalizing in opaque type instantiation #89045

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

Merged
merged 6 commits into from
Sep 21, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment please =) What is the purpose of this function?

&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`.