From 717ed4764cc6dc22c4e2559a4bbd89642df3aa55 Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Thu, 6 Jan 2022 23:08:27 -0500 Subject: [PATCH 1/2] An ugly, awful hack that works --- compiler/rustc_borrowck/src/type_check/mod.rs | 10 +- compiler/rustc_infer/src/infer/mod.rs | 4 + compiler/rustc_infer/src/infer/undo_log.rs | 31 +++++ .../rustc_trait_selection/src/traits/mod.rs | 5 +- .../src/traits/project.rs | 106 ++++++++++++------ .../src/traits/select/mod.rs | 31 +++++ compiler/rustc_typeck/src/check/callee.rs | 23 +++- compiler/rustc_typeck/src/check/coercion.rs | 71 +++++++++++- compiler/rustc_typeck/src/check/expr.rs | 2 +- .../rustc_typeck/src/check/method/confirm.rs | 13 +-- .../rustc_typeck/src/check/method/probe.rs | 5 +- .../issue-91139.migrate.stderr | 18 ++- .../generic-associated-types/issue-91139.rs | 4 +- .../generic-associated-types/issue-91395.rs | 33 ++++++ .../issue-91395.stderr | 17 +++ .../generic-associated-types/issue-91693.rs | 41 +++++++ .../issue-91693.stderr | 18 +++ .../generic-associated-types/issue-91762.rs | 22 ++-- .../issue-91762.stderr | 2 +- 19 files changed, 389 insertions(+), 67 deletions(-) create mode 100644 src/test/ui/generic-associated-types/issue-91395.rs create mode 100644 src/test/ui/generic-associated-types/issue-91395.stderr create mode 100644 src/test/ui/generic-associated-types/issue-91693.rs create mode 100644 src/test/ui/generic-associated-types/issue-91693.stderr diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 95018e51b0e7a..5ae479739b620 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2101,8 +2101,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.mk_fn_ptr(fn_sig); + let ty = self.normalize(*ty, location); if let Err(terr) = self.eq_types( - *ty, + ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, @@ -2124,9 +2125,11 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { _ => bug!(), }; let ty_fn_ptr_from = tcx.mk_fn_ptr(tcx.signature_unclosure(sig, *unsafety)); + let ty_fn_ptr_from = self.normalize(ty_fn_ptr_from, location); + let ty = self.normalize(*ty, location); if let Err(terr) = self.eq_types( - *ty, + ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, @@ -2154,8 +2157,9 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { let ty_fn_ptr_from = tcx.safe_to_unsafe_fn_ty(fn_sig); + let ty = self.normalize(*ty, location); if let Err(terr) = self.eq_types( - *ty, + ty, ty_fn_ptr_from, location.to_locations(), ConstraintCategory::Cast, diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index 5697e73f73a70..47573b6bc46bc 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -920,6 +920,10 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> { .region_constraints_added_in_snapshot(&snapshot.undo_snapshot) } + pub fn any_instantiations(&self, snapshot: &CombinedSnapshot<'a, 'tcx>) -> bool { + self.inner.borrow_mut().any_instantiations(&snapshot.undo_snapshot) + } + pub fn add_given(&self, sub: ty::Region<'tcx>, sup: ty::RegionVid) { self.inner.borrow_mut().unwrap_region_constraints().add_given(sub, sup); } diff --git a/compiler/rustc_infer/src/infer/undo_log.rs b/compiler/rustc_infer/src/infer/undo_log.rs index ecd886b547834..613a6e0013ba6 100644 --- a/compiler/rustc_infer/src/infer/undo_log.rs +++ b/compiler/rustc_infer/src/infer/undo_log.rs @@ -29,6 +29,23 @@ pub(crate) enum UndoLog<'tcx> { PushRegionObligation, } +impl<'tcx> std::fmt::Debug for UndoLog<'tcx> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Self::TypeVariables(_) => f.debug_tuple("TypeVariables").finish(), + Self::ConstUnificationTable(_) => f.debug_tuple("ConstUnificationTable").finish(), + Self::IntUnificationTable(_) => f.debug_tuple("IntUnificationTable").finish(), + Self::FloatUnificationTable(_) => f.debug_tuple("FloatUnificationTable").finish(), + Self::RegionConstraintCollector(_) => { + f.debug_tuple("RegionConstraintCollector").finish() + } + Self::RegionUnificationTable(_) => f.debug_tuple("RegionUnificationTable").finish(), + Self::ProjectionCache(_) => f.debug_tuple("ProjectionCache").finish(), + Self::PushRegionObligation => write!(f, "PushRegionObligation"), + } + } +} + macro_rules! impl_from { ($($ctor: ident ($ty: ty),)*) => { $( @@ -165,6 +182,10 @@ impl<'tcx> InferCtxtInner<'tcx> { self.undo_log.num_open_snapshots -= 1; } + + pub fn any_instantiations(&mut self, snapshot: &Snapshot<'tcx>) -> bool { + self.undo_log.instantiations_in_snapshot(snapshot).next().is_some() + } } impl<'tcx> InferCtxtUndoLogs<'tcx> { @@ -173,6 +194,16 @@ impl<'tcx> InferCtxtUndoLogs<'tcx> { Snapshot { undo_len: self.logs.len(), _marker: PhantomData } } + pub(crate) fn instantiations_in_snapshot( + &self, + s: &Snapshot<'tcx>, + ) -> impl Iterator> + Clone { + self.logs[s.undo_len..].iter().filter_map(|log| match log { + UndoLog::TypeVariables(log) => Some(log), + _ => None, + }) + } + pub(crate) fn region_constraints_in_snapshot( &self, s: &Snapshot<'tcx>, diff --git a/compiler/rustc_trait_selection/src/traits/mod.rs b/compiler/rustc_trait_selection/src/traits/mod.rs index bf94c61e6c2ce..944f29f6a1ab5 100644 --- a/compiler/rustc_trait_selection/src/traits/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/mod.rs @@ -55,7 +55,10 @@ pub use self::object_safety::is_vtable_safe_method; pub use self::object_safety::MethodViolationCode; pub use self::object_safety::ObjectSafetyViolation; pub use self::on_unimplemented::{OnUnimplementedDirective, OnUnimplementedNote}; -pub use self::project::{normalize, normalize_projection_type, normalize_to}; +pub use self::project::{ + normalize, normalize_projection_type, normalize_to, normalize_with_depth_to, + project_and_unify_type, +}; pub use self::select::{EvaluationCache, SelectionCache, SelectionContext}; pub use self::select::{EvaluationResult, IntercrateAmbiguityCause, OverflowError}; pub use self::specialize::specialization_graph::FutureCompatOverlapError; diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index ea48fab1cebb8..7110f943e7baa 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -42,7 +42,7 @@ pub type ProjectionObligation<'tcx> = Obligation<'tcx, ty::ProjectionPredicate<' pub type ProjectionTyObligation<'tcx> = Obligation<'tcx, ty::ProjectionTy<'tcx>>; -pub(super) struct InProgress; +pub struct InProgress; /// When attempting to resolve `::Name` ... #[derive(Debug)] @@ -188,7 +188,7 @@ pub(super) fn poly_project_and_unify_type<'cx, 'tcx>( /// If successful, this may result in additional obligations. /// /// See [poly_project_and_unify_type] for an explanation of the return value. -fn project_and_unify_type<'cx, 'tcx>( +pub fn project_and_unify_type<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionObligation<'tcx>, ) -> Result< @@ -484,7 +484,7 @@ impl<'a, 'b, 'tcx> TypeFolder<'tcx> for AssocTypeNormalizer<'a, 'b, 'tcx> { // there won't be bound vars there. let data = data.super_fold_with(self); - let normalized_ty = if self.eager_inference_replacement { + let normalized_ty = if self.selcx.normalization_mode.eager_inference_replacement { normalize_projection_type( self.selcx, self.param_env, @@ -914,7 +914,8 @@ fn opt_normalize_projection_type<'a, 'b, 'tcx>( // Don't use the projection cache in intercrate mode - // the `infcx` may be re-used between intercrate in non-intercrate // mode, which could lead to using incorrect cache results. - let use_cache = !selcx.is_intercrate(); + let use_cache = + !selcx.is_intercrate() && selcx.normalization_mode.allow_infer_constraint_during_projection; let projection_ty = infcx.resolve_vars_if_possible(projection_ty); let cache_key = ProjectionCacheKey::new(projection_ty); @@ -1168,7 +1169,7 @@ fn project<'cx, 'tcx>( match candidates { ProjectionCandidateSet::Single(candidate) => { - Ok(Projected::Progress(confirm_candidate(selcx, obligation, candidate))) + Ok(confirm_candidate(selcx, obligation, candidate)) } ProjectionCandidateSet::None => Ok(Projected::NoProgress( // FIXME(associated_const_generics): this may need to change in the future? @@ -1583,7 +1584,7 @@ fn confirm_candidate<'cx, 'tcx>( selcx: &mut SelectionContext<'cx, 'tcx>, obligation: &ProjectionTyObligation<'tcx>, candidate: ProjectionCandidate<'tcx>, -) -> Progress<'tcx> { +) -> Projected<'tcx> { debug!(?obligation, ?candidate, "confirm_candidate"); let mut progress = match candidate { ProjectionCandidate::ParamEnv(poly_projection) @@ -1596,7 +1597,7 @@ fn confirm_candidate<'cx, 'tcx>( } ProjectionCandidate::Select(impl_source) => { - confirm_select_candidate(selcx, obligation, impl_source) + Projected::Progress(confirm_select_candidate(selcx, obligation, impl_source)) } }; @@ -1605,9 +1606,11 @@ fn confirm_candidate<'cx, 'tcx>( // with new region variables, we need to resolve them to existing variables // when possible for this to work. See `auto-trait-projection-recursion.rs` // for a case where this matters. - if progress.term.has_infer_regions() { - progress.term = - progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx())); + if let Projected::Progress(progress) = &mut progress { + if progress.term.has_infer_regions() { + progress.term = + progress.term.fold_with(&mut OpportunisticRegionResolver::new(selcx.infcx())); + } } progress } @@ -1688,9 +1691,12 @@ fn confirm_generator_candidate<'cx, 'tcx>( } }); - confirm_param_env_candidate(selcx, obligation, predicate, false) - .with_addl_obligations(impl_source.nested) - .with_addl_obligations(obligations) + let progress = confirm_param_env_candidate(selcx, obligation, predicate, false); + let progress = match progress { + Projected::Progress(progress) => progress, + Projected::NoProgress(_) => bug!(), + }; + progress.with_addl_obligations(impl_source.nested).with_addl_obligations(obligations) } fn confirm_discriminant_kind_candidate<'cx, 'tcx>( @@ -1715,7 +1721,12 @@ fn confirm_discriminant_kind_candidate<'cx, 'tcx>( // We get here from `poly_project_and_unify_type` which replaces bound vars // with placeholders, so dummy is okay here. - confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) + let progress = + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false); + match progress { + Projected::Progress(progress) => progress, + Projected::NoProgress(_) => bug!(), + } } fn confirm_pointee_candidate<'cx, 'tcx>( @@ -1746,8 +1757,12 @@ fn confirm_pointee_candidate<'cx, 'tcx>( term: metadata_ty.into(), }; - confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false) - .with_addl_obligations(obligations) + let progress = + confirm_param_env_candidate(selcx, obligation, ty::Binder::dummy(predicate), false); + match progress { + Projected::Progress(progress) => progress.with_addl_obligations(obligations), + Projected::NoProgress(_) => bug!(), + } } fn confirm_fn_pointer_candidate<'cx, 'tcx>( @@ -1819,7 +1834,11 @@ fn confirm_callable_candidate<'cx, 'tcx>( term: ret_type.into(), }); - confirm_param_env_candidate(selcx, obligation, predicate, true) + let progress = confirm_param_env_candidate(selcx, obligation, predicate, true); + match progress { + Projected::Progress(progress) => progress, + Projected::NoProgress(_) => bug!(), + } } fn confirm_param_env_candidate<'cx, 'tcx>( @@ -1827,7 +1846,7 @@ fn confirm_param_env_candidate<'cx, 'tcx>( obligation: &ProjectionTyObligation<'tcx>, poly_cache_entry: ty::PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidate: bool, -) -> Progress<'tcx> { +) -> Projected<'tcx> { let infcx = selcx.infcx(); let cause = &obligation.cause; let param_env = obligation.param_env; @@ -1868,23 +1887,42 @@ fn confirm_param_env_candidate<'cx, 'tcx>( debug!(?cache_projection, ?obligation_projection); - match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) { - Ok(InferOk { value: _, obligations }) => { - nested_obligations.extend(obligations); - assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); - // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take - // a term instead. - Progress { term: cache_entry.term, obligations: nested_obligations } - } - Err(e) => { - let msg = format!( - "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", - obligation, poly_cache_entry, e, - ); - debug!("confirm_param_env_candidate: {}", msg); - let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg); - Progress { term: err.into(), obligations: vec![] } + match infcx.commit_if_ok(|snapshot| { + let progress = match infcx.at(cause, param_env).eq(cache_projection, obligation_projection) + { + Ok(InferOk { value: _, obligations }) => { + nested_obligations.extend(obligations); + assoc_ty_own_obligations(selcx, obligation, &mut nested_obligations); + // FIXME(associated_const_equality): Handle consts here as well? Maybe this progress type should just take + // a term instead. + Progress { term: cache_entry.term, obligations: nested_obligations } + } + Err(e) => { + let msg = format!( + "Failed to unify obligation `{:?}` with poly_projection `{:?}`: {:?}", + obligation, poly_cache_entry, e, + ); + debug!("confirm_param_env_candidate: {}", msg); + let err = infcx.tcx.ty_error_with_message(obligation.cause.span, &msg); + Progress { term: err.into(), obligations: vec![] } + } + }; + + let any_instantiations = infcx.any_instantiations(&snapshot); + + if any_instantiations && !selcx.normalization_mode.allow_infer_constraint_during_projection + { + Err(ty::Term::Ty( + infcx + .tcx + .mk_projection(obligation_projection.item_def_id, obligation_projection.substs), + )) + } else { + Ok(progress) } + }) { + Ok(p) => Projected::Progress(p), + Err(p) => Projected::NoProgress(p), } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 8af4606db8520..bf6ead7bad876 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -132,6 +132,17 @@ pub struct SelectionContext<'cx, 'tcx> { /// policy. In essence, canonicalized queries need their errors propagated /// rather than immediately reported because we do not have accurate spans. query_mode: TraitQueryMode, + + pub normalization_mode: NormalizationMode, +} + +#[derive(Copy, Clone)] +pub struct NormalizationMode { + pub allow_infer_constraint_during_projection: bool, + /// If true, when a projection is unable to be completed, an inference + /// variable will be created and an obligation registered to project to that + /// inference variable. Also, constants will be eagerly evaluated. + pub eager_inference_replacement: bool, } // A stack that walks back up the stack frame. @@ -221,6 +232,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { intercrate_ambiguity_causes: None, allow_negative_impls: false, query_mode: TraitQueryMode::Standard, + normalization_mode: NormalizationMode { + allow_infer_constraint_during_projection: true, + eager_inference_replacement: true, + }, } } @@ -232,6 +247,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { intercrate_ambiguity_causes: None, allow_negative_impls: false, query_mode: TraitQueryMode::Standard, + normalization_mode: NormalizationMode { + allow_infer_constraint_during_projection: true, + eager_inference_replacement: true, + }, } } @@ -247,6 +266,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { intercrate_ambiguity_causes: None, allow_negative_impls, query_mode: TraitQueryMode::Standard, + normalization_mode: NormalizationMode { + allow_infer_constraint_during_projection: true, + eager_inference_replacement: true, + }, } } @@ -262,6 +285,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { intercrate_ambiguity_causes: None, allow_negative_impls: false, query_mode, + normalization_mode: NormalizationMode { + allow_infer_constraint_during_projection: true, + eager_inference_replacement: true, + }, } } @@ -297,6 +324,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { self.intercrate } + pub fn normalization_mode(&self) -> NormalizationMode { + self.normalization_mode + } + /////////////////////////////////////////////////////////////////////////// // Selection // diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs index d5187c109e33d..93c33e99e6e68 100644 --- a/compiler/rustc_typeck/src/check/callee.rs +++ b/compiler/rustc_typeck/src/check/callee.rs @@ -330,6 +330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { false } + #[tracing::instrument(level = "debug", skip(self))] fn confirm_builtin_call( &self, call_expr: &'tcx hir::Expr<'tcx>, @@ -508,7 +509,27 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // have been normalized before. let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig).0; - let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); + + debug!(?fn_sig); + + //let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); + + let mut selcx = traits::SelectionContext::new(self.inh); + selcx.normalization_mode.allow_infer_constraint_during_projection = false; + let cause = ObligationCause::misc(call_expr.span, self.body_id); + use rustc_trait_selection::traits; + let mut obligations = Vec::new(); + let fn_sig = traits::normalize_with_depth_to( + &mut selcx, + self.param_env, + cause, + 0, + fn_sig, + &mut obligations, + ); + self.inh.register_predicates(obligations); + + debug!(?fn_sig); // Call the generic checker. let expected_arg_tys = self.expected_inputs_for_expected_output( diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs index c0eda02a99967..93354165aa580 100644 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ b/compiler/rustc_typeck/src/check/coercion.rs @@ -86,6 +86,21 @@ impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { type CoerceResult<'tcx> = InferResult<'tcx, (Vec>, Ty<'tcx>)>; +trait CoerceResultExt<'tcx>: Sized { + fn with_addl_obligations(self, obligations: Vec>) -> Self; +} +impl<'tcx> CoerceResultExt<'tcx> for CoerceResult<'tcx> { + fn with_addl_obligations(self, obligations: Vec>) -> Self { + match self { + Ok(mut ok) => { + ok.obligations.extend(obligations.into_iter()); + Ok(ok) + } + Err(err) => Err(err), + } + } +} + /// Coercing a mutable reference to an immutable works, while /// coercing `&T` to `&mut T` should be forbidden. fn coerce_mutbls<'tcx>( @@ -172,6 +187,51 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { return self.coerce_from_inference_variable(a, b, identity); } + let mut selcx = traits::SelectionContext::new(self); + selcx.normalization_mode.eager_inference_replacement = false; + let mut normalize_obligations = Vec::new(); + + let b = traits::normalize_with_depth_to( + &mut selcx, + self.param_env, + self.cause.clone(), + 0, + b, + &mut normalize_obligations, + ); + let a = match a.kind() { + ty::Projection(projection_ty) => { + let normalize_obligation = Obligation::new( + self.cause.clone(), + self.param_env, + ty::ProjectionPredicate { + projection_ty: *projection_ty, + term: ty::Term::Ty(b), + }, + ); + let infcx = selcx.infcx(); + let result = + infcx.commit_if_ok(|_| -> Result<_, traits::MismatchedProjectionTypes<'tcx>> { + traits::project_and_unify_type(&mut selcx, &normalize_obligation) + }); + match result { + Ok(Ok(Some(obligations))) => { + normalize_obligations.extend(obligations.into_iter()); + b + } + _ => a, + } + } + _ => traits::normalize_with_depth_to( + &mut selcx, + self.param_env, + self.cause.clone(), + 0, + a, + &mut normalize_obligations, + ), + }; + // Consider coercing the subtype to a DST // // NOTE: this is wrapped in a `commit_if_ok` because it creates @@ -181,7 +241,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { match unsize { Ok(_) => { debug!("coerce: unsize successful"); - return unsize; + return unsize.with_addl_obligations(normalize_obligations); } Err(TypeError::ObjectUnsafeCoercion(did)) => { debug!("coerce: unsize not object safe"); @@ -195,10 +255,14 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { // Examine the supertype and consider auto-borrowing. match *b.kind() { ty::RawPtr(mt_b) => { - return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); + return self + .coerce_unsafe_ptr(a, b, mt_b.mutbl) + .with_addl_obligations(normalize_obligations); } ty::Ref(r_b, _, mutbl_b) => { - return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); + return self + .coerce_borrowed_pointer(a, b, r_b, mutbl_b) + .with_addl_obligations(normalize_obligations); } _ => {} } @@ -228,6 +292,7 @@ impl<'f, 'tcx> Coerce<'f, 'tcx> { self.unify_and(a, b, identity) } } + .with_addl_obligations(normalize_obligations) } /// Coercing *from* an inference variable. In this case, we have no information diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 7e7104f62fdc6..6c2d5c33c2e39 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -159,7 +159,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Note that inspecting a type's structure *directly* may expose the fact /// that there are actually multiple representations for `Error`, so avoid /// that when err needs to be handled differently. - #[instrument(skip(self, expr), level = "debug")] pub(super) fn check_expr_with_expectation( &self, expr: &'tcx hir::Expr<'tcx>, @@ -170,6 +169,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`. + #[instrument(skip(self, expr), level = "debug")] pub(super) fn check_expr_with_expectation_and_args( &self, expr: &'tcx hir::Expr<'tcx>, diff --git a/compiler/rustc_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs index 3fa873e46abbb..4a7cfeb7691ac 100644 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ b/compiler/rustc_typeck/src/check/method/confirm.rs @@ -39,6 +39,7 @@ pub struct ConfirmResult<'tcx> { } impl<'a, 'tcx> FnCtxt<'a, 'tcx> { + #[tracing::instrument(level = "debug", skip(self, span, self_expr, call_expr, segment))] pub fn confirm_method( &self, span: Span, @@ -48,10 +49,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { pick: probe::Pick<'tcx>, segment: &hir::PathSegment<'_>, ) -> ConfirmResult<'tcx> { - debug!( - "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})", - unadjusted_self_ty, pick, segment.args, - ); + debug!(?segment.args); let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr); confirm_cx.confirm(unadjusted_self_ty, pick, segment) @@ -81,7 +79,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick); let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs); - debug!("all_substs={:?}", all_substs); + debug!(?all_substs); // Create the final signature for the method, replacing late-bound regions. let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs); @@ -93,10 +91,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { // could alter our Self-type, except for normalizing the receiver from the // signature (which is also done during probing). let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]); - debug!( - "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", - self_ty, method_sig_rcvr, method_sig, method_predicates - ); + debug!(?self_ty, ?method_sig_rcvr, ?method_sig, ?method_predicates); self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); let (method_sig, method_predicates) = diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs index 876c575781cf6..08fb766f3f360 100644 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ b/compiler/rustc_typeck/src/check/method/probe.rs @@ -468,12 +468,11 @@ pub fn provide(providers: &mut ty::query::Providers) { providers.method_autoderef_steps = method_autoderef_steps; } +#[tracing::instrument(level = "debug", skip(tcx))] fn method_autoderef_steps<'tcx>( tcx: TyCtxt<'tcx>, goal: CanonicalTyGoal<'tcx>, ) -> MethodAutoderefStepsResult<'tcx> { - debug!("method_autoderef_steps({:?})", goal); - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { let ParamEnvAnd { param_env, value: self_ty } = goal; @@ -529,7 +528,7 @@ fn method_autoderef_steps<'tcx>( _ => None, }; - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); + debug!(?steps, ?opt_bad_ty); MethodAutoderefStepsResult { steps: tcx.arena.alloc_from_iter(steps), diff --git a/src/test/ui/generic-associated-types/issue-91139.migrate.stderr b/src/test/ui/generic-associated-types/issue-91139.migrate.stderr index a27d811023834..87e433c31eb64 100644 --- a/src/test/ui/generic-associated-types/issue-91139.migrate.stderr +++ b/src/test/ui/generic-associated-types/issue-91139.migrate.stderr @@ -6,5 +6,21 @@ LL | fn foo() { LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds -error: aborting due to previous error +error[E0311]: the parameter type `T` may not live long enough + --> $DIR/issue-91139.rs:27:58 + | +LL | fn foo() { + | - help: consider adding an explicit lifetime bound...: `T: 'a` +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + +error[E0311]: the parameter type `T` may not live long enough + --> $DIR/issue-91139.rs:27:58 + | +LL | fn foo() { + | - help: consider adding an explicit lifetime bound...: `T: 'a` +LL | let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); + | ^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds + +error: aborting due to 3 previous errors diff --git a/src/test/ui/generic-associated-types/issue-91139.rs b/src/test/ui/generic-associated-types/issue-91139.rs index 78b2b63dadc5e..c1d609abfa661 100644 --- a/src/test/ui/generic-associated-types/issue-91139.rs +++ b/src/test/ui/generic-associated-types/issue-91139.rs @@ -25,7 +25,9 @@ impl Foo for () { fn foo() { let _: for<'a> fn(<() as Foo>::Type<'a>, &'a T) = |_, _| (); - //[migrate]~^ the parameter type `T` may not live long enough + //[migrate]~^ the parameter type + //[migrate]~| the parameter type + //[migrate]~| the parameter type } pub fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-91395.rs b/src/test/ui/generic-associated-types/issue-91395.rs new file mode 100644 index 0000000000000..b4a7d91d7f318 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-91395.rs @@ -0,0 +1,33 @@ +// check-fail +// FIXME(generic_associated_types): This *should* pass, but fails +// because we pick some region `'_#1r` instead of `'a` + +#![feature(generic_associated_types)] + +trait Foo { + type Item; + fn item(&self) -> Self::Item; +} + +trait Table { + type Blocks<'a>: Foo + where + Self: 'a; + + fn blocks(&self) -> Self::Blocks<'_>; +} + +fn box_static_block(_block: U) {} + +fn clone_static_table<'a, T>(table: &'a T) +where + T: Table, + <::Blocks<'a> as Foo>::Item: 'static, + //for<'x> <::Blocks<'x> as Foo>::Item: 'static, // This also doesn't work +{ + let block = table.blocks().item(); + box_static_block(block); + //~^ the associated type +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-91395.stderr b/src/test/ui/generic-associated-types/issue-91395.stderr new file mode 100644 index 0000000000000..a449d62f710fd --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-91395.stderr @@ -0,0 +1,17 @@ +error[E0310]: the associated type `<::Blocks<'_> as Foo>::Item` may not live long enough + --> $DIR/issue-91395.rs:29:5 + | +LL | box_static_block(block); + | ^^^^^^^^^^^^^^^^ + | + = help: consider adding an explicit lifetime bound `<::Blocks<'_> as Foo>::Item: 'static`... + = note: ...so that the type `<::Blocks<'_> as Foo>::Item` will meet its required lifetime bounds... +note: ...that is required by this bound + --> $DIR/issue-91395.rs:20:24 + | +LL | fn box_static_block(_block: U) {} + | ^^^^^^^ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0310`. diff --git a/src/test/ui/generic-associated-types/issue-91693.rs b/src/test/ui/generic-associated-types/issue-91693.rs new file mode 100644 index 0000000000000..b85d90d9167f6 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-91693.rs @@ -0,0 +1,41 @@ +// check-fail +// FIXME(generic_associated_types): This *should* pass, but fails likely due to +// leak check/universe related things + +#![feature(generic_associated_types)] + +pub trait LendingIterator { + type Item<'a> + where + Self: 'a; + fn next(&mut self) -> Option>; + + fn for_each(mut self, mut f: F) + where + Self: Sized, + F: FnMut(Self::Item<'_>), + { + while let Some(item) = self.next() { + f(item) + } + } +} + +pub struct Mutator(T); + +impl LendingIterator for Mutator { + type Item<'a> = &'a mut T + where + Self: 'a; + fn next(&mut self) -> Option> { + Some(&mut self.0) + } +} + +pub fn bar(m: Mutator) { + m.for_each(|_: &mut T| {}); + //~^ ERROR the parameter type + //~| ERROR the parameter type +} + +fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-91693.stderr b/src/test/ui/generic-associated-types/issue-91693.stderr new file mode 100644 index 0000000000000..e2b4a4d71e6ec --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-91693.stderr @@ -0,0 +1,18 @@ +error[E0311]: the parameter type `T` may not live long enough + --> $DIR/issue-91693.rs:36:7 + | +LL | pub fn bar(m: Mutator) { + | - help: consider adding an explicit lifetime bound...: `T: 'a` +LL | m.for_each(|_: &mut T| {}); + | ^^^^^^^^ ...so that the type `Mutator` will meet its required lifetime bounds + +error[E0311]: the parameter type `T` may not live long enough + --> $DIR/issue-91693.rs:36:7 + | +LL | pub fn bar(m: Mutator) { + | - help: consider adding an explicit lifetime bound...: `T: 'a` +LL | m.for_each(|_: &mut T| {}); + | ^^^^^^^^ ...so that the type `Mutator` will meet its required lifetime bounds + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/generic-associated-types/issue-91762.rs b/src/test/ui/generic-associated-types/issue-91762.rs index b259a3c6e06bc..ab7fd22827b83 100644 --- a/src/test/ui/generic-associated-types/issue-91762.rs +++ b/src/test/ui/generic-associated-types/issue-91762.rs @@ -1,12 +1,17 @@ // check-fail - -// FIXME(generic_associated_types): We almost certaintly want this to pass, but -// it's particularly difficult currently, because we need a way of specifying -// that `::With = Self` without using that when we have -// a `U`. See `https://github.com/rust-lang/rust/pull/92728` for a (hacky) -// solution. This might be better to just wait for Chalk. +// compile-flags: -Zverbose #![feature(generic_associated_types)] +#![feature(lang_items)] +#![feature(no_core)] +#![no_core] +#![crate_type = "rlib"] + +#[lang = "sized"] +pub trait Sized {} + +#[lang = "copy"] +pub trait Copy {} pub trait Functor { type With; @@ -23,8 +28,7 @@ pub trait FunctorExt: Sized { arg = self; ret = ::fmap(arg); - //~^ type annotations needed + //~^ cannot infer type + //~| type annotations needed } } - -fn main() {} diff --git a/src/test/ui/generic-associated-types/issue-91762.stderr b/src/test/ui/generic-associated-types/issue-91762.stderr index a9c465cdd7ea2..f422bb5caec99 100644 --- a/src/test/ui/generic-associated-types/issue-91762.stderr +++ b/src/test/ui/generic-associated-types/issue-91762.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed - --> $DIR/issue-91762.rs:25:15 + --> $DIR/issue-91762.rs:30:15 | LL | ret = ::fmap(arg); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `fmap` From 0d6ca1dc4fbef1f217b49e9fb0b622ffc75b906e Mon Sep 17 00:00:00 2001 From: Jack Huey <31162821+jackh726@users.noreply.github.com> Date: Fri, 11 Mar 2022 20:07:32 -0500 Subject: [PATCH 2/2] Revert previous hacks --- .../src/traits/project.rs | 48 ++++++++----------- .../src/traits/select/mod.rs | 38 ++------------- .../generic-associated-types/issue-91762.rs | 3 +- .../issue-91762.stderr | 20 ++++++-- 4 files changed, 41 insertions(+), 68 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index 7110f943e7baa..4933bf2aa72d3 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -19,7 +19,6 @@ use super::{Normalized, NormalizedTy, ProjectionCacheEntry, ProjectionCacheKey}; use crate::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; use crate::infer::{InferCtxt, InferOk, LateBoundRegionConversionTime}; use crate::traits::error_reporting::InferCtxtExt as _; -use crate::traits::select::ProjectionMatchesProjection; use rustc_data_structures::sso::SsoHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::ErrorGuaranteed; @@ -1244,7 +1243,7 @@ fn assemble_candidates_from_trait_def<'cx, 'tcx>( ProjectionCandidate::TraitDef, bounds.iter(), true, - ); + ) } /// In the case of a trait object like @@ -1309,35 +1308,28 @@ fn assemble_candidates_from_predicates<'cx, 'tcx>( let bound_predicate = predicate.kind(); if let ty::PredicateKind::Projection(data) = predicate.kind().skip_binder() { let data = bound_predicate.rebind(data); - if data.projection_def_id() != obligation.predicate.item_def_id { - continue; - } + let same_def_id = data.projection_def_id() == obligation.predicate.item_def_id; - let is_match = infcx.probe(|_| { - selcx.match_projection_projections( - obligation, - data, - potentially_unnormalized_candidates, - ) - }); + let is_match = same_def_id + && infcx.probe(|_| { + selcx.match_projection_projections( + obligation, + data, + potentially_unnormalized_candidates, + ) + }); - match is_match { - ProjectionMatchesProjection::Yes => { - candidate_set.push_candidate(ctor(data)); - - if potentially_unnormalized_candidates - && !obligation.predicate.has_infer_types_or_consts() - { - // HACK: Pick the first trait def candidate for a fully - // inferred predicate. This is to allow duplicates that - // differ only in normalization. - return; - } - } - ProjectionMatchesProjection::Ambiguous => { - candidate_set.mark_ambiguous(); + if is_match { + candidate_set.push_candidate(ctor(data)); + + if potentially_unnormalized_candidates + && !obligation.predicate.has_infer_types_or_consts() + { + // HACK: Pick the first trait def candidate for a fully + // inferred predicate. This is to allow duplicates that + // differ only in normalization. + return; } - ProjectionMatchesProjection::No => {} } } } diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index bf6ead7bad876..4b94ab6ddffda 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1523,18 +1523,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }) } - /// Return `Yes` if the obligation's predicate type applies to the env_predicate, and - /// `No` if it does not. Return `Ambiguous` in the case that the projection type is a GAT, - /// and applying this env_predicate constrains any of the obligation's GAT substitutions. - /// - /// This behavior is a somewhat of a hack to prevent overconstraining inference variables - /// in cases like #91762. pub(super) fn match_projection_projections( &mut self, obligation: &ProjectionTyObligation<'tcx>, env_predicate: PolyProjectionPredicate<'tcx>, potentially_unnormalized_candidates: bool, - ) -> ProjectionMatchesProjection { + ) -> bool { let mut nested_obligations = Vec::new(); let (infer_predicate, _) = self.infcx.replace_bound_vars_with_fresh_vars( obligation.cause.span, @@ -1556,8 +1550,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { infer_predicate.projection_ty }; - let is_match = self - .infcx + self.infcx .at(&obligation.cause, obligation.param_env) .sup(obligation.predicate, infer_projection) .map_or(false, |InferOk { obligations, value: () }| { @@ -1566,26 +1559,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { nested_obligations.into_iter().chain(obligations), ) .map_or(false, |res| res.may_apply()) - }); - - if is_match { - let generics = self.tcx().generics_of(obligation.predicate.item_def_id); - // FIXME(generic-associated-types): Addresses aggressive inference in #92917. - // If this type is a GAT, and of the GAT substs resolve to something new, - // that means that we must have newly inferred something about the GAT. - // We should give up in that case. - if !generics.params.is_empty() - && obligation.predicate.substs[generics.parent_count..] - .iter() - .any(|&p| p.has_infer_types_or_consts() && self.infcx.shallow_resolve(p) != p) - { - ProjectionMatchesProjection::Ambiguous - } else { - ProjectionMatchesProjection::Yes - } - } else { - ProjectionMatchesProjection::No - } + }) } /////////////////////////////////////////////////////////////////////////// @@ -2743,9 +2717,3 @@ impl<'o, 'tcx> fmt::Debug for TraitObligationStack<'o, 'tcx> { write!(f, "TraitObligationStack({:?})", self.obligation) } } - -pub enum ProjectionMatchesProjection { - Yes, - Ambiguous, - No, -} diff --git a/src/test/ui/generic-associated-types/issue-91762.rs b/src/test/ui/generic-associated-types/issue-91762.rs index ab7fd22827b83..3931a8324e924 100644 --- a/src/test/ui/generic-associated-types/issue-91762.rs +++ b/src/test/ui/generic-associated-types/issue-91762.rs @@ -28,7 +28,6 @@ pub trait FunctorExt: Sized { arg = self; ret = ::fmap(arg); - //~^ cannot infer type - //~| type annotations needed + //~^ mismatched types } } diff --git a/src/test/ui/generic-associated-types/issue-91762.stderr b/src/test/ui/generic-associated-types/issue-91762.stderr index f422bb5caec99..0cacedb47ba83 100644 --- a/src/test/ui/generic-associated-types/issue-91762.stderr +++ b/src/test/ui/generic-associated-types/issue-91762.stderr @@ -1,9 +1,23 @@ -error[E0282]: type annotations needed +error[E0308]: mismatched types --> $DIR/issue-91762.rs:30:15 | +LL | pub trait FunctorExt: Sized { + | - found type parameter +... +LL | fn fmap(self) { + | - expected type parameter +LL | let arg: ::With; +LL | let ret: ::With; + | -------------------------------- expected due to this type +... LL | ret = ::fmap(arg); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the associated function `fmap` + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `U`, found type parameter `T` + | + = note: expected associated type `<>::Base as Functor>::With` + found associated type `<>::Base as Functor>::With` + = note: a type parameter was expected, but a different one was found; you might be missing a type parameter or trait bound + = note: for more information, visit https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters error: aborting due to previous error -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0308`.