Skip to content

Commit bf3ef26

Browse files
committed
polymorphize: I used if T used in I: Foo<T>
This commit adjusts polymorphization's handling of predicates so that after ensuring that `T` is used in `I: Foo<T>` if `I` is used, it now ensures that `I` is used if `T` is used in `I: Foo<T>`. 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 <[email protected]>
1 parent 3fbed17 commit bf3ef26

File tree

3 files changed

+189
-31
lines changed

3 files changed

+189
-31
lines changed

src/librustc_mir/monomorphize/polymorphize.rs

+119-31
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,7 @@ fn unused_generic_params(tcx: TyCtxt<'_>, def_id: DefId) -> FiniteBitSet<u32> {
6969

7070
// Visit MIR and accumululate used generic parameters.
7171
let body = tcx.optimized_mir(def_id);
72-
let mut vis =
73-
UsedGenericParametersVisitor { tcx, def_id, unused_parameters: &mut unused_parameters };
72+
let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters: &mut unused_parameters };
7473
vis.visit_body(body);
7574
debug!("unused_generic_params: (after visitor) unused_parameters={:?}", unused_parameters);
7675

@@ -120,45 +119,101 @@ fn mark_used_by_predicates<'tcx>(
120119
def_id: DefId,
121120
unused_parameters: &mut FiniteBitSet<u32>,
122121
) {
123-
let def_id = tcx.closure_base_def_id(def_id);
124-
125-
let is_self_ty_used = |unused_parameters: &mut FiniteBitSet<u32>, self_ty: Ty<'tcx>| {
126-
debug!("unused_generic_params: self_ty={:?}", self_ty);
127-
if let ty::Param(param) = self_ty.kind {
128-
!unused_parameters.contains(param.index).unwrap_or(false)
129-
} else {
130-
false
131-
}
122+
let is_ty_used = |unused_parameters: &FiniteBitSet<u32>, ty: Ty<'tcx>| -> bool {
123+
let mut vis = IsUsedGenericParams { unused_parameters };
124+
ty.visit_with(&mut vis)
132125
};
133126

134127
let mark_ty = |unused_parameters: &mut FiniteBitSet<u32>, ty: Ty<'tcx>| {
135-
let mut vis = UsedGenericParametersVisitor { tcx, def_id, unused_parameters };
128+
let mut vis = MarkUsedGenericParams { tcx, def_id, unused_parameters };
136129
ty.visit_with(&mut vis);
137130
};
138131

132+
let def_id = tcx.closure_base_def_id(def_id);
139133
let predicates = tcx.explicit_predicates_of(def_id);
140-
debug!("mark_parameters_used_in_predicates: predicates_of={:?}", predicates);
141-
for (predicate, _) in predicates.predicates {
142-
match predicate.skip_binders() {
143-
ty::PredicateAtom::Trait(predicate, ..) => {
144-
let trait_ref = predicate.trait_ref;
145-
if is_self_ty_used(unused_parameters, trait_ref.self_ty()) {
134+
debug!("mark_used_by_predicates: predicates_of={:?}", predicates);
135+
136+
let mut current_unused_parameters = FiniteBitSet::new_empty();
137+
// Run to a fixed point to support `where T: Trait<U>, U: Trait<V>`, starting with an empty
138+
// bit set so that this is skipped if all parameters are already used.
139+
while current_unused_parameters != *unused_parameters {
140+
debug!(
141+
"mark_used_by_predicates: current_unused_parameters={:?} = unused_parameters={:?}",
142+
current_unused_parameters, unused_parameters
143+
);
144+
current_unused_parameters = *unused_parameters;
145+
146+
for (predicate, _) in predicates.predicates {
147+
match predicate.skip_binders() {
148+
ty::PredicateAtom::Trait(predicate, ..) => {
149+
let trait_ref = predicate.trait_ref;
150+
debug!("mark_used_by_predicates: (trait) trait_ref={:?}", trait_ref);
151+
152+
// Consider `T` used if `I` is used in predicates of the form
153+
// `I: Iterator<Item = T>`
154+
debug!("mark_used_by_predicates: checking self");
155+
if is_ty_used(unused_parameters, trait_ref.self_ty()) {
156+
debug!("mark_used_by_predicates: used!");
157+
for ty in trait_ref.substs.types() {
158+
mark_ty(unused_parameters, ty);
159+
}
160+
161+
// No need to check for a type being used in the substs if `self_ty` was
162+
// used.
163+
continue;
164+
}
165+
166+
// Consider `I` used if `T` is used in predicates of the form
167+
// `I: Iterator<Item = &'a (T, E)>` (see rust-lang/rust#75326)
168+
debug!("mark_used_by_predicates: checking substs");
146169
for ty in trait_ref.substs.types() {
147-
debug!("unused_generic_params: (trait) ty={:?}", ty);
148-
mark_ty(unused_parameters, ty);
170+
if is_ty_used(unused_parameters, ty) {
171+
debug!("mark_used_by_predicates: used!");
172+
mark_ty(unused_parameters, trait_ref.self_ty());
173+
}
149174
}
150175
}
151-
}
152-
ty::PredicateAtom::Projection(proj, ..) => {
153-
let self_ty = proj.projection_ty.self_ty();
154-
if is_self_ty_used(unused_parameters, self_ty) {
155-
debug!("unused_generic_params: (projection ty={:?}", proj.ty);
156-
mark_ty(unused_parameters, proj.ty);
176+
ty::PredicateAtom::Projection(proj, ..) => {
177+
let self_ty = proj.projection_ty.self_ty();
178+
debug!(
179+
"mark_used_by_predicates: (projection) self_ty={:?} proj.ty={:?}",
180+
self_ty, proj.ty
181+
);
182+
183+
// Consider `T` used if `I` is used in predicates of the form
184+
// `<I as Iterator>::Item = T`
185+
debug!("mark_used_by_predicates: checking self");
186+
if is_ty_used(unused_parameters, self_ty) {
187+
debug!("mark_used_by_predicates: used!");
188+
mark_ty(unused_parameters, proj.ty);
189+
190+
// No need to check for projection type being used if `self_ty` was used.
191+
continue;
192+
}
193+
194+
// Consider `I` used if `T` is used in predicates of the form
195+
// `<I as Iterator>::Item = &'a (T, E)` (see rust-lang/rust#75326)
196+
debug!("mark_used_by_predicates: checking projection ty");
197+
if is_ty_used(unused_parameters, proj.ty) {
198+
debug!("mark_used_by_predicates: used!");
199+
mark_ty(unused_parameters, self_ty);
200+
}
157201
}
202+
ty::PredicateAtom::RegionOutlives(..)
203+
| ty::PredicateAtom::TypeOutlives(..)
204+
| ty::PredicateAtom::WellFormed(..)
205+
| ty::PredicateAtom::ObjectSafe(..)
206+
| ty::PredicateAtom::ClosureKind(..)
207+
| ty::PredicateAtom::Subtype(..)
208+
| ty::PredicateAtom::ConstEvaluatable(..)
209+
| ty::PredicateAtom::ConstEquate(..) => (),
158210
}
159-
_ => (),
160211
}
161212
}
213+
214+
if let Some(parent) = predicates.parent {
215+
mark_used_by_predicates(tcx, parent, unused_parameters);
216+
}
162217
}
163218

164219
/// Emit errors for the function annotated by `#[rustc_polymorphize_error]`, labelling each generic
@@ -204,13 +259,13 @@ fn emit_unused_generic_params_error<'tcx>(
204259
}
205260

206261
/// Visitor used to aggregate generic parameter uses.
207-
struct UsedGenericParametersVisitor<'a, 'tcx> {
262+
struct MarkUsedGenericParams<'a, 'tcx> {
208263
tcx: TyCtxt<'tcx>,
209264
def_id: DefId,
210265
unused_parameters: &'a mut FiniteBitSet<u32>,
211266
}
212267

213-
impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> {
268+
impl<'a, 'tcx> MarkUsedGenericParams<'a, 'tcx> {
214269
/// Invoke `unused_generic_params` on a body contained within the current item (e.g.
215270
/// a closure, generator or constant).
216271
fn visit_child_body(&mut self, def_id: DefId, substs: SubstsRef<'tcx>) {
@@ -229,7 +284,7 @@ impl<'a, 'tcx> UsedGenericParametersVisitor<'a, 'tcx> {
229284
}
230285
}
231286

232-
impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
287+
impl<'a, 'tcx> Visitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
233288
fn visit_local_decl(&mut self, local: Local, local_decl: &LocalDecl<'tcx>) {
234289
debug!("visit_local_decl: local_decl={:?}", local_decl);
235290
if local == Local::from_usize(1) {
@@ -256,7 +311,7 @@ impl<'a, 'tcx> Visitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
256311
}
257312
}
258313

259-
impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
314+
impl<'a, 'tcx> TypeVisitor<'tcx> for MarkUsedGenericParams<'a, 'tcx> {
260315
fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool {
261316
debug!("visit_const: c={:?}", c);
262317
if !c.has_param_types_or_consts() {
@@ -318,3 +373,36 @@ impl<'a, 'tcx> TypeVisitor<'tcx> for UsedGenericParametersVisitor<'a, 'tcx> {
318373
}
319374
}
320375
}
376+
377+
/// Visitor used to check if a generic parameter is used.
378+
struct IsUsedGenericParams<'a> {
379+
unused_parameters: &'a FiniteBitSet<u32>,
380+
}
381+
382+
impl<'a, 'tcx> TypeVisitor<'tcx> for IsUsedGenericParams<'a> {
383+
fn visit_const(&mut self, c: &'tcx Const<'tcx>) -> bool {
384+
debug!("visit_const: c={:?}", c);
385+
if !c.has_param_types_or_consts() {
386+
return false;
387+
}
388+
389+
match c.val {
390+
ty::ConstKind::Param(param) => {
391+
!self.unused_parameters.contains(param.index).unwrap_or(false)
392+
}
393+
_ => c.super_visit_with(self),
394+
}
395+
}
396+
397+
fn visit_ty(&mut self, ty: Ty<'tcx>) -> bool {
398+
debug!("visit_ty: ty={:?}", ty);
399+
if !ty.has_param_types_or_consts() {
400+
return false;
401+
}
402+
403+
match ty.kind {
404+
ty::Param(param) => !self.unused_parameters.contains(param.index).unwrap_or(false),
405+
_ => ty.super_visit_with(self),
406+
}
407+
}
408+
}

src/test/ui/polymorphization/predicates.rs

+48
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,55 @@ where
1818
bar::<I>()
1919
}
2020

21+
#[rustc_polymorphize_error]
22+
fn baz<I, T>(_: I)
23+
where
24+
std::iter::Repeat<I>: Iterator<Item = T>,
25+
{
26+
bar::<I>()
27+
}
28+
29+
// In addition, check that `I` is considered used in `next::{{closure}}`, because `T` is used and
30+
// `T` is really just `I::Item`. `E` is used due to the fixed-point marking of predicates.
31+
32+
pub(crate) struct Foo<'a, I, E>(I, &'a E);
33+
34+
impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E>
35+
where
36+
I: Iterator<Item = &'a (T, E)>,
37+
{
38+
type Item = T;
39+
40+
#[rustc_polymorphize_error]
41+
fn next(&mut self) -> Option<Self::Item> {
42+
self.find(|_| true)
43+
}
44+
}
45+
46+
// Furthermore, check that `B` is considered used because `C` is used, and that `A` is considered
47+
// used because `B` is now used.
48+
49+
trait Baz<Z> {}
50+
51+
impl Baz<u16> for u8 {}
52+
impl Baz<u32> for u16 {}
53+
54+
#[rustc_polymorphize_error]
55+
fn quux<A, B, C: Default>() -> usize
56+
where
57+
A: Baz<B>,
58+
B: Baz<C>,
59+
{
60+
std::mem::size_of::<C>()
61+
}
62+
2163
fn main() {
2264
let x = &[2u32];
2365
foo(x.iter());
66+
baz(x.iter());
67+
68+
let mut a = Foo([(1u32, 1u16)].iter(), &1u16);
69+
let _ = a.next();
70+
71+
let _ = quux::<u8, u16, u32>();
2472
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// build-pass
2+
// compile-flags: -Zpolymorphize=on -Zsymbol-mangling-version=v0
3+
4+
pub(crate) struct Foo<'a, I, E>(I, &'a E);
5+
6+
impl<'a, I, T: 'a, E> Iterator for Foo<'a, I, E>
7+
where
8+
I: Iterator<Item = &'a (T, E)>,
9+
{
10+
type Item = T;
11+
12+
fn next(&mut self) -> Option<Self::Item> {
13+
self.find(|_| true)
14+
}
15+
}
16+
17+
fn main() {
18+
let mut a = Foo([(1u32, 1u16)].iter(), &1u16);
19+
let mut b = Foo([(1u16, 1u32)].iter(), &1u32);
20+
let _ = a.next();
21+
let _ = b.next();
22+
}

0 commit comments

Comments
 (0)