From bf3ef26713b8208e0127f4c51725897dbcd11bd9 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 14 Aug 2020 12:33:20 +0100 Subject: [PATCH] polymorphize: `I` used if `T` used in `I: Foo` This commit adjusts polymorphization's handling of predicates so that after ensuring that `T` is used in `I: Foo` if `I` is used, it now ensures that `I` is used if `T` is used in `I: Foo`. This is necessary to mark generic parameters that only exist in impl parameters as used - thereby avoiding symbol clashes when using the new mangling scheme. Signed-off-by: David Wood --- src/librustc_mir/monomorphize/polymorphize.rs | 150 ++++++++++++++---- src/test/ui/polymorphization/predicates.rs | 48 ++++++ .../ui/polymorphization/symbol-ambiguity.rs | 22 +++ 3 files changed, 189 insertions(+), 31 deletions(-) create mode 100644 src/test/ui/polymorphization/symbol-ambiguity.rs diff --git a/src/librustc_mir/monomorphize/polymorphize.rs b/src/librustc_mir/monomorphize/polymorphize.rs index fc9f7f1af622f..d946c1947c2df 100644 --- a/src/librustc_mir/monomorphize/polymorphize.rs +++ b/src/librustc_mir/monomorphize/polymorphize.rs @@ -69,8 +69,7 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet { // Visit MIR and accumululate used generic parameters. let body = tcx.optimized_mir(def_id); - let mut vis = - UsedGenericParametersVisitor { tcx, def_id, unused_parameters: &mut unused_parameters }; + let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters }; vis.visit_body(body); debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters); @@ -120,45 +119,101 @@ fn mark_used_by_predicates<'tcx>( def_id: DefId, unused_parameters: &mut FiniteBitSet, ) { - let def_id = tcx.closure_base_def_id(def_id); - - let is_self_ty_used = |unused_parameters: &mut FiniteBitSet, self_ty: Ty<'tcx>| { - debug!("unused_generic_params: self_ty={:?}", self_ty); - if let ty::Param(param) = self_ty.kind { - !unused_parameters.contains(param.index).unwrap_or(false) - } else { - false - } + let is_ty_used = |unused_parameters: &FiniteBitSet, ty: Ty<'tcx>| -> bool { + let mut vis = IsUsedGenericParams { unused_parameters }; + ty.visit_with(&mut vis) }; let mark_ty = |unused_parameters: &mut FiniteBitSet, ty: Ty<'tcx>| { - let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters }; + let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters }; ty.visit_with(&mut vis); }; + let def_id = tcx.closure_base_def_id(def_id); let predicates = tcx.explicit_predicates_of(def_id); - debug!("mark_parameters_used_in_predicates: predicates_of={:?}", predicates); - for (predicate, _) in predicates.predicates { - match predicate.skip_binders() { - ty::PredicateAtom::Trait(predicate, ..) => { - let trait_ref = predicate.trait_ref; - if is_self_ty_used(unused_parameters, trait_ref.self_ty()) { + debug!("mark_used_by_predicates: predicates_of={:?}", predicates); + + let mut current_unused_parameters = FiniteBitSet::new_empty(); + // Run to a fixed point to support `where T: Trait, U: Trait`, starting with an empty + // bit set so that this is skipped if all parameters are already used. + while current_unused_parameters != *unused_parameters { + debug!( + "mark_used_by_predicates: current_unused_parameters={:?} = unused_parameters={:?}", + current_unused_parameters, unused_parameters + ); + current_unused_parameters = *unused_parameters; + + for (predicate, _) in predicates.predicates { + match predicate.skip_binders() { + ty::PredicateAtom::Trait(predicate, ..) => { + let trait_ref = predicate.trait_ref; + debug!("mark_used_by_predicates: (trait) trait_ref={:?}", trait_ref); + + // Consider `T` used if `I` is used in predicates of the form + // `I: Iterator` + debug!("mark_used_by_predicates: checking self"); + if is_ty_used(unused_parameters, trait_ref.self_ty()) { + debug!("mark_used_by_predicates: used!"); + for ty in trait_ref.substs.types() { + mark_ty(unused_parameters, ty); + } + + // No need to check for a type being used in the substs if `self_ty` was + // used. + continue; + } + + // Consider `I` used if `T` is used in predicates of the form + // `I: Iterator` (see rust-lang/rust#75326) + debug!("mark_used_by_predicates: checking substs"); for ty in trait_ref.substs.types() { - debug!("unused_generic_params: (trait) ty={:?}", ty); - mark_ty(unused_parameters, ty); + if is_ty_used(unused_parameters, ty) { + debug!("mark_used_by_predicates: used!"); + mark_ty(unused_parameters, trait_ref.self_ty()); + } } } - } - ty::PredicateAtom::Projection(proj, ..) => { - let self_ty = proj.projection_ty.self_ty(); - if is_self_ty_used(unused_parameters, self_ty) { - debug!("unused_generic_params: (projection ty={:?}", proj.ty); - mark_ty(unused_parameters, proj.ty); + ty::PredicateAtom::Projection(proj, ..) => { + let self_ty = proj.projection_ty.self_ty(); + debug!( + "mark_used_by_predicates: (projection) self_ty={:?} proj.ty={:?}", + self_ty, proj.ty + ); + + // Consider `T` used if `I` is used in predicates of the form + // `::Item = T` + debug!("mark_used_by_predicates: checking self"); + if is_ty_used(unused_parameters, self_ty) { + debug!("mark_used_by_predicates: used!"); + mark_ty(unused_parameters, proj.ty); + + // No need to check for projection type being used if `self_ty` was used. + continue; + } + + // Consider `I` used if `T` is used in predicates of the form + // `::Item = &'a (T, E)` (see rust-lang/rust#75326) + debug!("mark_used_by_predicates: checking projection ty"); + if is_ty_used(unused_parameters, proj.ty) { + debug!("mark_used_by_predicates: used!"); + mark_ty(unused_parameters, self_ty); + } } + ty::PredicateAtom::RegionOutlives(..) + | ty::PredicateAtom::TypeOutlives(..) + | ty::PredicateAtom::WellFormed(..) + | ty::PredicateAtom::ObjectSafe(..) + | ty::PredicateAtom::ClosureKind(..) + | ty::PredicateAtom::Subtype(..) + | ty::PredicateAtom::ConstEvaluatable(..) + | ty::PredicateAtom::ConstEquate(..) => (), } - _ => (), } } + + if let Some(parent) = predicates.parent { + mark_used_by_predicates(tcx, parent, unused_parameters); + } } /// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic @@ -204,13 +259,13 @@ fn emit_unused_generic_params_error<'tcx>( } /// Visitor used to aggregate generic parameter uses. -struct UsedGenericParametersVisitor<'a, 'tcx> { +struct MarkUsedGenericParams<'a, 'tcx> { tcx: TyCtxt<'tcx>, def_id: DefId, unused_parameters: &'a mut FiniteBitSet, } -impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> { +impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> { /// Invoke `unused_generic_params` on a body contained within the current item (e.g. /// a closure, generator or constant). fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) { @@ -229,7 +284,7 @@ impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { +impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) { debug!("visit_local_decl: local_decl={:?}", local_decl); if local == Local::from_usize(1) { @@ -256,7 +311,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { } } -impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { +impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> { fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { debug!("visit_const: c={:?}", c); if !c.has_param_types_or_consts() { @@ -318,3 +373,36 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> { } } } + +/// Visitor used to check if a generic parameter is used. +struct IsUsedGenericParams<'a> { + unused_parameters: &'a FiniteBitSet, +} + +impl<'a, 'tcx> TypeVisitor<'tcx> for IsUsedGenericParams<'a> { + fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool { + debug!("visit_const: c={:?}", c); + if !c.has_param_types_or_consts() { + return false; + } + + match c.val { + ty::ConstKind::Param(param) => { + !self.unused_parameters.contains(param.index).unwrap_or(false) + } + _ => c.super_visit_with(self), + } + } + + fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool { + debug!("visit_ty: ty={:?}", ty); + if !ty.has_param_types_or_consts() { + return false; + } + + match ty.kind { + ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false), + _ => ty.super_visit_with(self), + } + } +} diff --git a/src/test/ui/polymorphization/predicates.rs b/src/test/ui/polymorphization/predicates.rs index 82a94933b470e..60555dc12dcac 100644 --- a/src/test/ui/polymorphization/predicates.rs +++ b/src/test/ui/polymorphization/predicates.rs @@ -18,7 +18,55 @@ where bar::() } +#[rustc_polymorphize_error] +fn baz(_: I) +where + std::iter::Repeat: Iterator, +{ + bar::() +} + +// In addition, check that `I` is considered used in `next::{{closure}}`, because `T` is used and +// `T` is really just `I::Item`. `E` is used due to the fixed-point marking of predicates. + +pub(crate) struct Foo<'a, I, E>(I, &'a E); + +impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E> +where + I: Iterator, +{ + type Item = T; + + #[rustc_polymorphize_error] + fn next(&mut self) -> Option { + self.find(|_| true) + } +} + +// Furthermore, check that `B` is considered used because `C` is used, and that `A` is considered +// used because `B` is now used. + +trait Baz {} + +impl Baz for u8 {} +impl Baz for u16 {} + +#[rustc_polymorphize_error] +fn quux() -> usize +where + A: Baz, + B: Baz, +{ + std::mem::size_of::() +} + fn main() { let x = &[2u32]; foo(x.iter()); + baz(x.iter()); + + let mut a = Foo([(1u32, 1u16)].iter(), &1u16); + let _ = a.next(); + + let _ = quux::(); } diff --git a/src/test/ui/polymorphization/symbol-ambiguity.rs b/src/test/ui/polymorphization/symbol-ambiguity.rs new file mode 100644 index 0000000000000..d97bae183d9c2 --- /dev/null +++ b/src/test/ui/polymorphization/symbol-ambiguity.rs @@ -0,0 +1,22 @@ +// build-pass +// compile-flags: -Zpolymorphize=on -Zsymbol-mangling-version=v0 + +pub(crate) struct Foo<'a, I, E>(I, &'a E); + +impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E> +where + I: Iterator, +{ + type Item = T; + + fn next(&mut self) -> Option { + self.find(|_| true) + } +} + +fn main() { + let mut a = Foo([(1u32, 1u16)].iter(), &1u16); + let mut b = Foo([(1u16, 1u32)].iter(), &1u32); + let _ = a.next(); + let _ = b.next(); +}