Skip to content

Commit d9deced

Browse files
committed
instance: polymorphize upvar closures/generators
This commit modifies how instances are polymorphized so that closures and generators have any closures or generators captured within their upvars also polymorphized - this avoids symbol clashes with the new symbol mangling scheme. Signed-off-by: David Wood <[email protected]>
1 parent 3cfc7fe commit d9deced

File tree

5 files changed

+222
-20
lines changed

5 files changed

+222
-20
lines changed

src/librustc_middle/ty/instance.rs

+87-20
Original file line numberDiff line numberDiff line change
@@ -474,26 +474,7 @@ impl<'tcx> Instance<'tcx> {
474474
}
475475

476476
if let InstanceDef::Item(def) = self.def {
477-
let unused = tcx.unused_generic_params(def.did);
478-
479-
if unused.is_empty() {
480-
// Exit early if every parameter was used.
481-
return self;
482-
}
483-
484-
debug!("polymorphize: unused={:?}", unused);
485-
let polymorphized_substs =
486-
InternalSubsts::for_item(tcx, def.did, |param, _| match param.kind {
487-
// If parameter is a const or type parameter..
488-
ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
489-
// ..and is within range and unused..
490-
unused.contains(param.index).unwrap_or(false) =>
491-
// ..then use the identity for this parameter.
492-
tcx.mk_param_from_def(param),
493-
// Otherwise, use the parameter as before.
494-
_ => self.substs[param.index as usize],
495-
});
496-
477+
let polymorphized_substs = polymorphize(tcx, def.did, self.substs);
497478
debug!("polymorphize: self={:?} polymorphized_substs={:?}", self, polymorphized_substs);
498479
Self { def: self.def, substs: polymorphized_substs }
499480
} else {
@@ -502,6 +483,92 @@ impl<'tcx> Instance<'tcx> {
502483
}
503484
}
504485

486+
fn polymorphize<'tcx>(
487+
tcx: TyCtxt<'tcx>,
488+
def_id: DefId,
489+
substs: SubstsRef<'tcx>,
490+
) -> SubstsRef<'tcx> {
491+
debug!("polymorphize({:?}, {:?})", def_id, substs);
492+
let unused = tcx.unused_generic_params(def_id);
493+
debug!("polymorphize: unused={:?}", unused);
494+
495+
if unused.is_empty() {
496+
// Exit early if every parameter was used.
497+
return substs;
498+
}
499+
500+
// If this is a closure or generator then we need to handle the case where another closure
501+
// from the function is captured as an upvar and hasn't been polymorphized. In this case,
502+
// the unpolymorphized upvar closure would result in a polymorphized closure producing
503+
// multiple mono items (and eventually symbol clashes).
504+
let upvars_ty = if tcx.is_closure(def_id) {
505+
Some(substs.as_closure().tupled_upvars_ty())
506+
} else if tcx.type_of(def_id).is_generator() {
507+
Some(substs.as_generator().tupled_upvars_ty())
508+
} else {
509+
None
510+
};
511+
let has_upvars = upvars_ty.map(|ty| ty.tuple_fields().count() > 0).unwrap_or(false);
512+
debug!("polymorphize: upvars_ty={:?} has_upvars={:?}", upvars_ty, has_upvars);
513+
514+
struct PolymorphizationFolder<'tcx> {
515+
tcx: TyCtxt<'tcx>,
516+
};
517+
518+
impl ty::TypeFolder<'tcx> for PolymorphizationFolder<'tcx> {
519+
fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
520+
self.tcx
521+
}
522+
523+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
524+
debug!("fold_ty: ty={:?}", ty);
525+
match ty.kind {
526+
ty::Closure(def_id, substs) => {
527+
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
528+
self.tcx.mk_closure(def_id, polymorphized_substs)
529+
}
530+
ty::Generator(def_id, substs, movability) => {
531+
let polymorphized_substs = polymorphize(self.tcx, def_id, substs);
532+
self.tcx.mk_generator(def_id, polymorphized_substs, movability)
533+
}
534+
_ => ty.super_fold_with(self),
535+
}
536+
}
537+
}
538+
539+
InternalSubsts::for_item(tcx, def_id, |param, _| {
540+
let is_unused = unused.contains(param.index).unwrap_or(false);
541+
debug!("polymorphize: param={:?} is_unused={:?}", param, is_unused);
542+
match param.kind {
543+
// Upvar case: If parameter is a type parameter..
544+
ty::GenericParamDefKind::Type { .. } if
545+
// ..and has upvars..
546+
has_upvars &&
547+
// ..and this param has the same type as the tupled upvars..
548+
upvars_ty == Some(substs[param.index as usize].expect_ty()) => {
549+
// ..then double-check that polymorphization marked it used..
550+
debug_assert!(!is_unused);
551+
// ..and polymorphize any closures/generators captured as upvars.
552+
let upvars_ty = upvars_ty.unwrap();
553+
let polymorphized_upvars_ty = upvars_ty.fold_with(
554+
&mut PolymorphizationFolder { tcx });
555+
debug!("polymorphize: polymorphized_upvars_ty={:?}", polymorphized_upvars_ty);
556+
ty::GenericArg::from(polymorphized_upvars_ty)
557+
},
558+
559+
// Simple case: If parameter is a const or type parameter..
560+
ty::GenericParamDefKind::Const | ty::GenericParamDefKind::Type { .. } if
561+
// ..and is within range and unused..
562+
unused.contains(param.index).unwrap_or(false) =>
563+
// ..then use the identity for this parameter.
564+
tcx.mk_param_from_def(param),
565+
566+
// Otherwise, use the parameter as before.
567+
_ => substs[param.index as usize],
568+
}
569+
})
570+
}
571+
505572
fn needs_fn_once_adapter_shim(
506573
actual_closure_kind: ty::ClosureKind,
507574
trait_closure_kind: ty::ClosureKind,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
// build-pass
2+
// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
3+
4+
fn foo(f: impl Fn()) {
5+
let x = |_: ()| ();
6+
7+
// Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
8+
// `x` that will differ for each instantiation despite polymorphisation of the varying
9+
// argument.
10+
let y = || x(());
11+
12+
// Consider `f` used in `foo`.
13+
f();
14+
// Use `y` so that it is visited in monomorphisation collection.
15+
y();
16+
}
17+
18+
fn entry_a() {
19+
foo(|| ());
20+
}
21+
22+
fn entry_b() {
23+
foo(|| ());
24+
}
25+
26+
fn main() {
27+
entry_a();
28+
entry_b();
29+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// build-pass
2+
// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
3+
4+
fn foo(f: impl Fn()) {
5+
// Mutate an upvar from `x` so that it implements `FnMut`.
6+
let mut outer = 3;
7+
let mut x = |_: ()| {
8+
outer = 4;
9+
()
10+
};
11+
12+
// Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
13+
// `x` that will differ for each instantiation despite polymorphisation of the varying
14+
// argument.
15+
let mut y = || x(());
16+
17+
// Consider `f` used in `foo`.
18+
f();
19+
// Use `y` so that it is visited in monomorphisation collection.
20+
y();
21+
}
22+
23+
fn entry_a() {
24+
foo(|| ());
25+
}
26+
27+
fn entry_b() {
28+
foo(|| ());
29+
}
30+
31+
fn main() {
32+
entry_a();
33+
entry_b();
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
// build-pass
2+
// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
3+
4+
fn foo(f: impl Fn()) {
5+
// Move a non-copy type into `x` so that it implements `FnOnce`.
6+
let outer = Vec::<u32>::new();
7+
let x = move |_: ()| {
8+
let inner = outer;
9+
()
10+
};
11+
12+
// Don't use `f` in `y`, but refer to `x` so that the closure substs contain a reference to
13+
// `x` that will differ for each instantiation despite polymorphisation of the varying
14+
// argument.
15+
let y = || x(());
16+
17+
// Consider `f` used in `foo`.
18+
f();
19+
// Use `y` so that it is visited in monomorphisation collection.
20+
y();
21+
}
22+
23+
fn entry_a() {
24+
foo(|| ());
25+
}
26+
27+
fn entry_b() {
28+
foo(|| ());
29+
}
30+
31+
fn main() {
32+
entry_a();
33+
entry_b();
34+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// build-pass
2+
// compile-flags:-Zpolymorphize=on -Zsymbol-mangling-version=v0
3+
4+
fn y_uses_f(f: impl Fn()) {
5+
let x = |_: ()| ();
6+
7+
let y = || {
8+
f();
9+
x(());
10+
};
11+
12+
f();
13+
y();
14+
}
15+
16+
fn x_uses_f(f: impl Fn()) {
17+
let x = |_: ()| { f(); };
18+
19+
let y = || x(());
20+
21+
f();
22+
y();
23+
}
24+
25+
fn entry_a() {
26+
x_uses_f(|| ());
27+
y_uses_f(|| ());
28+
}
29+
30+
fn entry_b() {
31+
x_uses_f(|| ());
32+
y_uses_f(|| ());
33+
}
34+
35+
fn main() {
36+
entry_a();
37+
entry_b();
38+
}

0 commit comments

Comments
 (0)