Skip to content

Commit 1e58871

Browse files
committed
Auto merge of rust-lang#75518 - davidtwco:issue-75326-polymorphization-symbol-mangling-v0-predicates, r=lcnr
polymorphize: `I` used if `T` used in `I: Foo<T>` Fixes rust-lang#75326. This PR 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. With this PR, rustc will now fully bootstrap with polymorphization and the new symbol mangling scheme enabled - not all tests pass, but I'm not sure how much of that is the interaction of the two features, I'll be looking into that soon. All tests pass with only polymorphization enabled. r? @lcnr (this isn't sufficiently complex that I need to add to eddy's review queue) cc @eddyb
2 parents 5205b97 + bf3ef26 commit 1e58871

File tree

3 files changed

+189
-31
lines changed

3 files changed

+189
-31
lines changed

Diff for: 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+
}

Diff for: 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
}

Diff for: src/test/ui/polymorphization/symbol-ambiguity.rs

+22
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)