From dfb11a8a265d967e55c5515e78e741dfdba4db4d Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 Aug 2021 11:12:11 +0000 Subject: [PATCH 1/6] Add helper function to `InferCtxt` that generates inference vars for unresolved associated types --- compiler/rustc_infer/src/infer/mod.rs | 1 + compiler/rustc_infer/src/infer/projection.rs | 33 +++++++++++++++++++ .../src/traits/project.rs | 12 +------ 3 files changed, 35 insertions(+), 11 deletions(-) create mode 100644 compiler/rustc_infer/src/infer/projection.rs diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 354b8e26d53d5..43d3730c04931 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -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; diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs new file mode 100644 index 0000000000000..aa0fddf9b6f84 --- /dev/null +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -0,0 +1,33 @@ +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> { + pub fn infer_projection( + &self, + param_env: ty::ParamEnv<'tcx>, + projection_ty: ty::ProjectionTy<'tcx>, + cause: ObligationCause<'tcx>, + recursion_depth: usize, + obligations: &mut Vec>, + ) -> 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 + } +} diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 4f22543950c0b..873c058c55c18 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -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) }) } From 5afeed05e84afc5ee9bec6a8cc1003e0b97aa7fb Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 Aug 2021 11:28:54 +0000 Subject: [PATCH 2/6] Use tracing debugging in `fold_opaque_ty` --- .../rustc_trait_selection/src/opaque_types.rs | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 8abcdb5e89b47..221ce0062a6fe 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -863,7 +863,6 @@ struct Instantiator<'a, 'tcx> { } impl<'a, 'tcx> Instantiator<'a, 'tcx> { - #[instrument(level = "debug", skip(self))] fn instantiate_opaque_types_in_map>(&mut self, value: T) -> T { let tcx = self.infcx.tcx; value.fold_with(&mut BottomUpFolder { @@ -954,6 +953,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { }) } + #[instrument(skip(self), level = "debug")] fn fold_opaque_ty( &mut self, ty: Ty<'tcx>, @@ -961,28 +961,20 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { origin: hir::OpaqueTyOrigin, ) -> Ty<'tcx> { let infcx = self.infcx; - 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 = impl Bar;` needs to be - // defined by a function like `fn foo() -> Foo`). - 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`, @@ -999,7 +991,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { infcx.opaque_types_vars.insert(ty_var, ty); } - debug!("instantiate_opaque_types: ty_var={:?}", ty_var); + debug!("generated new type inference var {:?}", ty_var.kind()); self.compute_opaque_type_obligations(opaque_type_key); ty_var @@ -1011,7 +1003,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { let OpaqueTypeKey { def_id, substs } = opaque_type_key; let item_bounds = tcx.explicit_item_bounds(def_id); - debug!("instantiate_opaque_types: bounds={:#?}", item_bounds); + debug!(?item_bounds); let bounds: Vec<_> = item_bounds.iter().map(|(bound, _)| bound.subst(tcx, substs)).collect(); @@ -1023,7 +1015,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { ); self.obligations.extend(obligations); - debug!("instantiate_opaque_types: bounds={:?}", bounds); + debug!(?bounds); for predicate in &bounds { if let ty::PredicateKind::Projection(projection) = predicate.kind().skip_binder() { From 5fb1a652156d2d4d72d933976632aba7eeb3d614 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 Aug 2021 11:31:05 +0000 Subject: [PATCH 3/6] Inline a function that is only called once --- compiler/rustc_trait_selection/src/opaque_types.rs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 221ce0062a6fe..c3c565cc08fbc 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -961,6 +961,7 @@ impl<'a, 'tcx> Instantiator<'a, '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 @@ -992,15 +993,6 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { } debug!("generated new type inference var {:?}", ty_var.kind()); - 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; let item_bounds = tcx.explicit_item_bounds(def_id); debug!(?item_bounds); @@ -1021,7 +1013,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { 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 ty_var; } } } @@ -1040,6 +1032,8 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("instantiate_opaque_types: predicate={:?}", predicate); self.obligations.push(traits::Obligation::new(cause, self.param_env, predicate)); } + + ty_var } } From 34de78fd8131ac8149caab086c9696899d8d2bed Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 Aug 2021 11:45:32 +0000 Subject: [PATCH 4/6] Generate inference vars and obligations for projections in opaque types instead of trying to normalize them. --- .../rustc_trait_selection/src/opaque_types.rs | 43 +++++++++++-------- src/test/ui/impl-trait/issue-72911.rs | 1 + src/test/ui/impl-trait/issue-72911.stderr | 14 ++++-- 3 files changed, 36 insertions(+), 22 deletions(-) diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index c3c565cc08fbc..04ce0c56c2aa0 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -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; @@ -995,31 +994,37 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { debug!("generated new type inference var {:?}", ty_var.kind()); let item_bounds = tcx.explicit_item_bounds(def_id); - debug!(?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!(?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 ty_var; } } - } - - 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`. @@ -1029,7 +1034,7 @@ 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)); } diff --git a/src/test/ui/impl-trait/issue-72911.rs b/src/test/ui/impl-trait/issue-72911.rs index dee5a41f6de37..8761e4cf66a65 100644 --- a/src/test/ui/impl-trait/issue-72911.rs +++ b/src/test/ui/impl-trait/issue-72911.rs @@ -16,6 +16,7 @@ fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator fn lint_files() -> impl Iterator { //~^ ERROR: failed to resolve + //~| ERROR: `()` is not an iterator unimplemented!() } diff --git a/src/test/ui/impl-trait/issue-72911.stderr b/src/test/ui/impl-trait/issue-72911.stderr index 17748ae42770f..6c6d9cbe23e10 100644 --- a/src/test/ui/impl-trait/issue-72911.stderr +++ b/src/test/ui/impl-trait/issue-72911.stderr @@ -28,7 +28,15 @@ LL | fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator impl Iterator { | -------------------------------------- returning this opaque type `FlatMap` -error: aborting due to 3 previous errors +error[E0277]: `()` is not an iterator + --> $DIR/issue-72911.rs:17:20 + | +LL | fn lint_files() -> impl Iterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator + | + = help: the trait `Iterator` is not implemented for `()` + +error: aborting due to 4 previous errors -Some errors have detailed explanations: E0433, E0720. -For more information about an error, try `rustc --explain E0433`. +Some errors have detailed explanations: E0277, E0433, E0720. +For more information about an error, try `rustc --explain E0277`. From b952ada0ae2c957bdb8503366da4717224d3efc3 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Tue, 10 Aug 2021 15:24:48 +0000 Subject: [PATCH 5/6] Use `ty::Error` for opaque types with errors in its bounds. This reduces unhelpful diagnostics down the road. --- .../rustc_trait_selection/src/opaque_types.rs | 2 +- src/test/ui/async-await/issues/issue-65159.rs | 1 - .../ui/async-await/issues/issue-65159.stderr | 11 ++--------- .../issue-82126-mismatched-subst-and-hir.rs | 1 - ...issue-82126-mismatched-subst-and-hir.stderr | 18 ++++-------------- src/test/ui/impl-trait/issue-72911.rs | 1 - src/test/ui/impl-trait/issue-72911.stderr | 14 +++----------- 7 files changed, 10 insertions(+), 38 deletions(-) diff --git a/compiler/rustc_trait_selection/src/opaque_types.rs b/compiler/rustc_trait_selection/src/opaque_types.rs index 04ce0c56c2aa0..c01faae5d6a51 100644 --- a/compiler/rustc_trait_selection/src/opaque_types.rs +++ b/compiler/rustc_trait_selection/src/opaque_types.rs @@ -1022,7 +1022,7 @@ impl<'a, 'tcx> Instantiator<'a, 'tcx> { 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 ty_var; + return tcx.ty_error(); } } // Change the predicate to refer to the type variable, diff --git a/src/test/ui/async-await/issues/issue-65159.rs b/src/test/ui/async-await/issues/issue-65159.rs index 1dbf5db6c32ed..df2ca025705d3 100644 --- a/src/test/ui/async-await/issues/issue-65159.rs +++ b/src/test/ui/async-await/issues/issue-65159.rs @@ -6,7 +6,6 @@ async fn copy() -> Result<()> //~^ ERROR this enum takes 2 generic arguments { Ok(()) - //~^ ERROR type annotations needed } fn main() { } diff --git a/src/test/ui/async-await/issues/issue-65159.stderr b/src/test/ui/async-await/issues/issue-65159.stderr index ff46bcb8983b3..45f5ec40cd758 100644 --- a/src/test/ui/async-await/issues/issue-65159.stderr +++ b/src/test/ui/async-await/issues/issue-65159.stderr @@ -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`. diff --git a/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs b/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs index 569769b8213bf..dd0320bc53ba7 100644 --- a/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs +++ b/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.rs @@ -17,7 +17,6 @@ async fn buy_lock(generator: &Mutex) -> 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); diff --git a/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr b/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr index 4bd0667304397..d2b927fb664c6 100644 --- a/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr +++ b/src/test/ui/borrowck/issue-82126-mismatched-subst-and-hir.stderr @@ -7,7 +7,7 @@ LL | async fn buy_lock(generator: &Mutex) -> 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); | ^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | async fn buy_lock(generator: &Mutex) -> 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); | ^^^^^^^^^^^^ - @@ -28,16 +28,6 @@ help: add missing generic argument LL | async fn buy_lock(generator: &Mutex) -> 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`. diff --git a/src/test/ui/impl-trait/issue-72911.rs b/src/test/ui/impl-trait/issue-72911.rs index 8761e4cf66a65..dee5a41f6de37 100644 --- a/src/test/ui/impl-trait/issue-72911.rs +++ b/src/test/ui/impl-trait/issue-72911.rs @@ -16,7 +16,6 @@ fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator fn lint_files() -> impl Iterator { //~^ ERROR: failed to resolve - //~| ERROR: `()` is not an iterator unimplemented!() } diff --git a/src/test/ui/impl-trait/issue-72911.stderr b/src/test/ui/impl-trait/issue-72911.stderr index 6c6d9cbe23e10..17748ae42770f 100644 --- a/src/test/ui/impl-trait/issue-72911.stderr +++ b/src/test/ui/impl-trait/issue-72911.stderr @@ -28,15 +28,7 @@ LL | fn gather_from_file(dir_entry: &foo::MissingItem) -> impl Iterator impl Iterator { | -------------------------------------- returning this opaque type `FlatMap` -error[E0277]: `()` is not an iterator - --> $DIR/issue-72911.rs:17:20 - | -LL | fn lint_files() -> impl Iterator { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator - | - = help: the trait `Iterator` is not implemented for `()` - -error: aborting due to 4 previous errors +error: aborting due to 3 previous errors -Some errors have detailed explanations: E0277, E0433, E0720. -For more information about an error, try `rustc --explain E0277`. +Some errors have detailed explanations: E0433, E0720. +For more information about an error, try `rustc --explain E0433`. From afb7472bcca54bffcab6841345cbf58e9f5dfa33 Mon Sep 17 00:00:00 2001 From: Oli Scherer Date: Sat, 18 Sep 2021 09:34:06 +0200 Subject: [PATCH 6/6] Add a doc comment to infer_projection --- compiler/rustc_infer/src/infer/projection.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/compiler/rustc_infer/src/infer/projection.rs b/compiler/rustc_infer/src/infer/projection.rs index aa0fddf9b6f84..9b53ab72b00de 100644 --- a/compiler/rustc_infer/src/infer/projection.rs +++ b/compiler/rustc_infer/src/infer/projection.rs @@ -7,6 +7,12 @@ 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>,