From b005f292e6795705c6e9e03b2d35df36846f3c41 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Mon, 19 Jul 2021 20:22:59 -0500 Subject: [PATCH 1/4] Display new error message on recursively `Sized` GAT --- .../src/traits/error_reporting/mod.rs | 111 +++++++++++++++++- .../src/traits/project.rs | 3 +- .../src/traits/select/confirmation.rs | 15 ++- .../src/traits/select/mod.rs | 8 +- compiler/rustc_typeck/src/collect.rs | 11 ++ 5 files changed, 142 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 5c4aef529e5ac..a68ffdb05cfd1 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -55,6 +55,12 @@ pub trait InferCtxtExt<'tcx> { fn report_overflow_error_cycle(&self, cycle: &[PredicateObligation<'tcx>]) -> !; + fn maybe_report_recursive_gat( + &self, + begin: &PredicateObligation<'tcx>, + end: &PredicateObligation<'tcx>, + ) -> bool; + /// The `root_obligation` parameter should be the `root_obligation` field /// from a `FulfillmentError`. If no `FulfillmentError` is available, /// then it should be the same as `obligation`. @@ -213,6 +219,103 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { bug!(); } + fn maybe_report_recursive_gat( + &self, + begin: &PredicateObligation<'tcx>, + end: &PredicateObligation<'tcx>, + ) -> bool { + let (head, tail) = + match (begin.predicate.kind().skip_binder(), end.predicate.kind().skip_binder()) { + (ty::PredicateKind::Trait(head, ..), ty::PredicateKind::Trait(tail, ..)) => { + (head, tail) + } + _ => return false, + }; + + if Some(head.clone().def_id()) != self.tcx.lang_items().sized_trait() { + return false; + } + + let head_ty = head.self_ty(); + let tail_ty = tail.self_ty(); + debug!("report_overflow_error_cycle: head_ty = {:?}, tail_ty = {:?}", head_ty, tail_ty); + + let head_kind = head_ty.kind(); + let tail_kind = tail_ty.kind(); + debug!( + "report_overflow_error_cycle: head_kind = {:?}, tail_kind = {:?}", + head_kind, tail_kind + ); + + let (substs, item_def_id) = match head_kind { + ty::Projection(ty::ProjectionTy { substs, item_def_id }) => (substs, item_def_id), + _ => return false, + }; + + let hir_id = self.tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local()); + let node = self.tcx.hir().get(hir_id); + debug!("report_overflow_error_cycle: node = {:?}", node); + + let (assoc_generics, _assoc_span) = match node { + Node::TraitItem(hir::TraitItem { + generics, + kind: hir::TraitItemKind::Type(..), + span, + .. + }) => (generics, span), + _ => return false, + }; + + let parameter_size = assoc_generics.params.len(); + if parameter_size == 0 { + return false; + } + + assert!(substs.len() >= parameter_size); + + let index = match substs + .get(substs.len() - parameter_size..) + .map(|gens| { + gens.iter() + .enumerate() + .find_map(|(i, param)| if param == &tail_ty.into() { Some(i) } else { None }) + }) + .flatten() + { + Some(index) => index, + None => return false, + }; + debug!("report_overflow_error_cycle: index = {:?}", index); + + let param = &assoc_generics.params[index]; + debug!("report_overflow_error_cycle: param = {:?}", param); + + let mut err = struct_span_err!( + self.tcx.sess, + begin.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + head + ); + + err.note("detected recursive derive for `Sized` in a generic associated type"); + + let (span, separator) = match param.bounds { + [] => (param.span.shrink_to_hi(), ":"), + [.., bound] => (bound.span().shrink_to_hi(), " +"), + }; + err.span_suggestion_verbose( + span, + "consider relaxing the implicit `Sized` restriction", + format!("{} ?Sized", separator), + Applicability::MachineApplicable, + ); + + err.emit(); + self.tcx.sess.abort_if_errors(); + true + } + /// Reports that a cycle was detected which led to overflow and halts /// compilation. This is equivalent to `report_overflow_error` except /// that we can give a more helpful error message (and, in particular, @@ -224,7 +327,13 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!("report_overflow_error_cycle: cycle={:?}", cycle); - self.report_overflow_error(&cycle[0], false); + let begin = &cycle[0]; + let end = &cycle[cycle.len() - 1]; + + if !self.maybe_report_recursive_gat(begin, end) { + self.report_overflow_error(begin, false); + } + bug!() } fn report_selection_error( diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index d1ab9fa025ed6..efdc4b89c2fae 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -327,8 +327,7 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> { fn fold>(&mut self, value: T) -> T { let value = self.selcx.infcx().resolve_vars_if_possible(value); - debug!(?value); - + debug!(?value, "fold"); assert!( !value.has_escaping_bound_vars(), "Normalizing {:?} without wrapping in a `Binder`", diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index f8297ee3a0718..43f2ff6926916 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -130,8 +130,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let tcx = self.tcx(); let trait_predicate = self.infcx.shallow_resolve(obligation.predicate); + debug!(?trait_predicate); + let placeholder_trait_predicate = self.infcx().replace_bound_vars_with_placeholders(trait_predicate); + debug!(?placeholder_trait_predicate); + let placeholder_self_ty = placeholder_trait_predicate.self_ty(); let (def_id, substs) = match *placeholder_self_ty.kind() { ty::Projection(proj) => (proj.item_def_id, proj.substs), @@ -140,9 +144,13 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { }; let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs); + debug!("candidate predicate before poly trait = {:?}", candidate_predicate); + let candidate = candidate_predicate .to_opt_poly_trait_ref() .expect("projection candidate is not a trait predicate"); + debug!("candidate predicate after poly trait = {:?}", candidate_predicate); + let mut obligations = Vec::new(); let candidate = normalize_with_depth_to( self, @@ -162,7 +170,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { })?); if let ty::Projection(..) = placeholder_self_ty.kind() { - for predicate in tcx.predicates_of(def_id).instantiate_own(tcx, substs).predicates { + let predicates = tcx.predicates_of(def_id); + debug!(?predicates); + let instantiated_predicates = predicates.instantiate_own(tcx, substs); + debug!(?instantiated_predicates); + for predicate in instantiated_predicates.predicates { let normalized = normalize_with_depth_to( self, obligation.param_env, @@ -179,6 +191,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { )); } } + debug!(?obligations); Ok(obligations) }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 564c63ef30cb6..53e1cae07052b 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -868,7 +868,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { where I: Iterator>, { - cycle.all(|predicate| self.coinductive_predicate(predicate)) + cycle.all(|predicate| { + debug!(?predicate, "coinductive_match"); + self.coinductive_predicate(predicate) + }) } fn coinductive_predicate(&self, predicate: ty::Predicate<'tcx>) -> bool { @@ -1572,8 +1575,9 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // NOTE: binder moved to (*) let self_ty = self.infcx.shallow_resolve(obligation.predicate.skip_binder().self_ty()); + debug!(?self_ty, "sized_conditions"); - match self_ty.kind() { + match *self_ty.kind() { ty::Infer(ty::IntVar(_) | ty::FloatVar(_)) | ty::Uint(_) | ty::Int(_) diff --git a/compiler/rustc_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs index 506ca98b96026..d4689c2e2de68 100644 --- a/compiler/rustc_typeck/src/collect.rs +++ b/compiler/rustc_typeck/src/collect.rs @@ -1978,6 +1978,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); let node = tcx.hir().get(hir_id); + debug!("explicit_predicates_of: node = {:?}", node); let mut is_trait = None; let mut is_default_impl_trait = None; @@ -2070,6 +2071,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP if let Some(_trait_ref) = is_trait { predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned()); } + debug!("explicit_predicates_of: super predicates = {:?}", predicates); // In default impls, we can assume that the self type implements // the trait. So in: @@ -2085,6 +2087,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP tcx.def_span(def_id), )); } + debug!("explicit_predicates_of: default impls = {:?}", predicates); // Collect the region predicates that were declared inline as // well. In the case of parameters declared on a fn or method, we @@ -2113,6 +2116,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } } + debug!("explicit_predicates_of: region predicates = {:?}", predicates); + // Collect the predicates that were written inline by the user on each // type parameter (e.g., ``). for param in ast_generics.params { @@ -2132,6 +2137,7 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP sized, param.span, ); + debug!("explicit_predicates_of: bounds = {:?}", bounds); predicates.extend(bounds.predicates(tcx, param_ty)); } GenericParamKind::Const { .. } => { @@ -2142,6 +2148,8 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } } + debug!("explicit_predicates_of: inline predicates = {:?}", predicates); + // Add in the bounds that appear in the where-clause. let where_clause = &ast_generics.where_clause; for predicate in where_clause.predicates { @@ -2251,8 +2259,11 @@ fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericP } } + debug!("explicit_predicates_of: where predicates = {:?}", predicates); + if tcx.features().const_evaluatable_checked { predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local())); + debug!("explicit_predicates_of: const predicates = {:?}", predicates); } let mut predicates: Vec<_> = predicates.into_iter().collect(); From 220c9cd712e719a3b761446f0b200a4605a207b9 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Mon, 19 Jul 2021 22:09:07 -0500 Subject: [PATCH 2/4] Fix `Sized` constraint just applying for last type on adt --- compiler/rustc_ty_utils/src/ty.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs index b0d644ae028ce..edf4ee0c28b32 100644 --- a/compiler/rustc_ty_utils/src/ty.rs +++ b/compiler/rustc_ty_utils/src/ty.rs @@ -193,7 +193,7 @@ fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstrain let result = tcx.mk_type_list( def.variants .iter() - .flat_map(|v| v.fields.last()) + .flat_map(|v| &v.fields) .flat_map(|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did))), ); From 1668dab19f4867a62b548413163f5f098d72262e Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Tue, 20 Jul 2021 00:26:23 -0500 Subject: [PATCH 3/4] Add tests for recursive GATs --- .../issue-80626-pass.rs | 60 +++++++++++++++++++ .../generic-associated-types/issue-80626.rs | 60 +++++++++++++++++++ .../issue-80626.stderr | 27 +++++++++ 3 files changed, 147 insertions(+) create mode 100644 src/test/ui/generic-associated-types/issue-80626-pass.rs create mode 100644 src/test/ui/generic-associated-types/issue-80626.rs create mode 100644 src/test/ui/generic-associated-types/issue-80626.stderr diff --git a/src/test/ui/generic-associated-types/issue-80626-pass.rs b/src/test/ui/generic-associated-types/issue-80626-pass.rs new file mode 100644 index 0000000000000..9951d9c388a21 --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-80626-pass.rs @@ -0,0 +1,60 @@ +#![feature(generic_associated_types)] + +// check-pass + +trait Allocator { + type Allocated; +} + +enum LinkedList { + Head, + Next(u8, A::Allocated), +} + +enum LinkedList2 { + Head, + Next(A::Allocated, u8), +} + +impl Allocator for () { + type Allocated = Box; +} + +fn main() { + { + use LinkedList::{Head, Next}; + let mut ll: LinkedList<()> = Next( + 8, + Box::new(Next( + 0, + Box::new(Next( + 6, + Box::new(Next(2, Box::new(Next(6, Box::new(Head))))), + )), + )), + ); + + while let Next(num, next) = ll { + println!("{}", num); + ll = *next; + } + } + { + use LinkedList2::{Head, Next}; + let mut ll: LinkedList2<()> = Next( + Box::new(Next( + Box::new(Next( + Box::new(Next(Box::new(Next(Box::new(Head), 6)), 2)), + 6, + )), + 0, + )), + 8, + ); + + while let Next(next, num) = ll { + println!("{}", num); + ll = *next; + } + } +} diff --git a/src/test/ui/generic-associated-types/issue-80626.rs b/src/test/ui/generic-associated-types/issue-80626.rs new file mode 100644 index 0000000000000..234180c25c98a --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-80626.rs @@ -0,0 +1,60 @@ +#![feature(generic_associated_types)] + +trait Allocator { + type Allocated; + //~^ HELP consider relaxing + //~| HELP consider relaxing +} + +enum LinkedList { + Head, + Next(u8, A::Allocated), //~ ERROR overflow evaluating the requirement +} + +enum LinkedList2 { + Head, + Next(A::Allocated, u8), //~ ERROR overflow evaluating the requirement +} + +impl Allocator for () { + type Allocated = Box; +} + +fn main() { + { + use LinkedList::{Head, Next}; + let mut ll: LinkedList<()> = Next( + 8, + Box::new(Next( + 0, + Box::new(Next( + 6, + Box::new(Next(2, Box::new(Next(6, Box::new(Head))))), + )), + )), + ); + + while let Next(num, next) = ll { + println!("{}", num); + ll = *next; + } + } + { + use LinkedList2::{Head, Next}; + let mut ll: LinkedList2<()> = Next( + Box::new(Next( + Box::new(Next( + Box::new(Next(Box::new(Next(Box::new(Head), 6)), 2)), + 6, + )), + 0, + )), + 8, + ); + + while let Next(next, num) = ll { + println!("{}", num); + ll = *next; + } + } +} diff --git a/src/test/ui/generic-associated-types/issue-80626.stderr b/src/test/ui/generic-associated-types/issue-80626.stderr new file mode 100644 index 0000000000000..13e9c62a7dfab --- /dev/null +++ b/src/test/ui/generic-associated-types/issue-80626.stderr @@ -0,0 +1,27 @@ +error[E0275]: overflow evaluating the requirement `::Allocated>: Sized` + --> $DIR/issue-80626.rs:11:14 + | +LL | Next(u8, A::Allocated), + | ^^^^^^^^^^^^^^^^^^ + | + = note: detected recursive derive for `Sized` in a generic associated type +help: consider relaxing the implicit `Sized` restriction + | +LL | type Allocated; + | ^^^^^^^^ + +error[E0275]: overflow evaluating the requirement `::Allocated>: Sized` + --> $DIR/issue-80626.rs:16:10 + | +LL | Next(A::Allocated, u8), + | ^^^^^^^^^^^^^^^^^^ + | + = note: detected recursive derive for `Sized` in a generic associated type +help: consider relaxing the implicit `Sized` restriction + | +LL | type Allocated; + | ^^^^^^^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0275`. From c18350d1fff6b9e4bf2bd3516c7ab539b2d494a3 Mon Sep 17 00:00:00 2001 From: jedel1043 Date: Tue, 20 Jul 2021 12:15:43 -0500 Subject: [PATCH 4/4] Report all valid errors on an indirect GAT recursion --- .../src/traits/error_reporting/mod.rs | 67 ++++++++++--------- 1 file changed, 36 insertions(+), 31 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index a68ffdb05cfd1..445f3c6fae964 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -57,8 +57,9 @@ pub trait InferCtxtExt<'tcx> { fn maybe_report_recursive_gat( &self, - begin: &PredicateObligation<'tcx>, - end: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + parent: &PredicateObligation<'tcx>, + child: &PredicateObligation<'tcx>, ) -> bool; /// The `root_obligation` parameter should be the `root_obligation` field @@ -221,33 +222,37 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { fn maybe_report_recursive_gat( &self, - begin: &PredicateObligation<'tcx>, - end: &PredicateObligation<'tcx>, + err: &mut DiagnosticBuilder<'_>, + parent: &PredicateObligation<'tcx>, + child: &PredicateObligation<'tcx>, ) -> bool { - let (head, tail) = - match (begin.predicate.kind().skip_binder(), end.predicate.kind().skip_binder()) { + let (parent, child) = + match (parent.predicate.kind().skip_binder(), child.predicate.kind().skip_binder()) { (ty::PredicateKind::Trait(head, ..), ty::PredicateKind::Trait(tail, ..)) => { (head, tail) } _ => return false, }; - if Some(head.clone().def_id()) != self.tcx.lang_items().sized_trait() { + if Some(parent.clone().def_id()) != self.tcx.lang_items().sized_trait() { return false; } - let head_ty = head.self_ty(); - let tail_ty = tail.self_ty(); - debug!("report_overflow_error_cycle: head_ty = {:?}, tail_ty = {:?}", head_ty, tail_ty); + let parent_ty = parent.self_ty(); + let child_ty = child.self_ty(); + debug!( + "report_overflow_error_cycle: parent_ty = {:?}, child_ty = {:?}", + parent_ty, child_ty + ); - let head_kind = head_ty.kind(); - let tail_kind = tail_ty.kind(); + let parent_kind = parent_ty.kind(); + let child_kind = child_ty.kind(); debug!( - "report_overflow_error_cycle: head_kind = {:?}, tail_kind = {:?}", - head_kind, tail_kind + "report_overflow_error_cycle: parent_kind = {:?}, child_kind = {:?}", + parent_kind, child_kind ); - let (substs, item_def_id) = match head_kind { + let (substs, item_def_id) = match parent_kind { ty::Projection(ty::ProjectionTy { substs, item_def_id }) => (substs, item_def_id), _ => return false, }; @@ -278,7 +283,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { .map(|gens| { gens.iter() .enumerate() - .find_map(|(i, param)| if param == &tail_ty.into() { Some(i) } else { None }) + .find_map(|(i, param)| if param == &child_ty.into() { Some(i) } else { None }) }) .flatten() { @@ -290,16 +295,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { let param = &assoc_generics.params[index]; debug!("report_overflow_error_cycle: param = {:?}", param); - let mut err = struct_span_err!( - self.tcx.sess, - begin.cause.span, - E0275, - "overflow evaluating the requirement `{}`", - head - ); - - err.note("detected recursive derive for `Sized` in a generic associated type"); - let (span, separator) = match param.bounds { [] => (param.span.shrink_to_hi(), ":"), [.., bound] => (bound.span().shrink_to_hi(), " +"), @@ -311,8 +306,6 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { Applicability::MachineApplicable, ); - err.emit(); - self.tcx.sess.abort_if_errors(); true } @@ -328,12 +321,24 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> { debug!("report_overflow_error_cycle: cycle={:?}", cycle); let begin = &cycle[0]; - let end = &cycle[cycle.len() - 1]; + let mut err = struct_span_err!( + self.tcx.sess, + begin.cause.span, + E0275, + "overflow evaluating the requirement `{}`", + begin.predicate + ); + err.note("detected recursive derive for `Sized` in a generic associated type"); - if !self.maybe_report_recursive_gat(begin, end) { + if cycle.windows(2).fold(false, |valid_error, preds| { + valid_error || self.maybe_report_recursive_gat(&mut err, &preds[0], &preds[1]) + }) { + err.emit(); + self.tcx.sess.abort_if_errors(); + bug!() + } else { self.report_overflow_error(begin, false); } - bug!() } fn report_selection_error(