Skip to content

Commit 0446a73

Browse files
committed
Erase all regions before constructing an LLVM type
Fixes rust-lang#55976 Previously, we only erased early-bound regions. However, two types that differ only in their regions (including late-bound regions) are indistinguishable to LLVM, so it's safe to erase all regions.
1 parent 521d8d8 commit 0446a73

File tree

4 files changed

+58
-2
lines changed

4 files changed

+58
-2
lines changed

compiler/rustc_codegen_llvm/src/type_of.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,8 @@ impl<'tcx> LayoutLlvmExt<'tcx> for TyAndLayout<'tcx> {
252252

253253
// Make sure lifetimes are erased, to avoid generating distinct LLVM
254254
// types for Rust types that only differ in the choice of lifetimes.
255-
let normal_ty = cx.tcx.erase_regions(&self.ty);
255+
// Note that we erase *all* regions, include late-bound regions.
256+
let normal_ty = cx.tcx.erase_early_and_late_regions(&self.ty);
256257

257258
let mut defer = None;
258259
let llty = if self.ty != normal_ty {

compiler/rustc_middle/src/query/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,11 @@ rustc_queries! {
347347
anon
348348
desc { "erasing regions from `{:?}`", ty }
349349
}
350+
351+
query erase_early_and_late_regions_ty(ty: Ty<'tcx>) -> Ty<'tcx> {
352+
anon
353+
desc { "erasing early and late regions for `{:?}`", ty }
354+
}
350355
}
351356

352357
Linking {

compiler/rustc_middle/src/ty/erase_regions.rs

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ use crate::ty::fold::{TypeFoldable, TypeFolder};
22
use crate::ty::{self, Ty, TyCtxt, TypeFlags};
33

44
pub(super) fn provide(providers: &mut ty::query::Providers) {
5-
*providers = ty::query::Providers { erase_regions_ty, ..*providers };
5+
*providers =
6+
ty::query::Providers { erase_regions_ty, erase_early_and_late_regions_ty, ..*providers };
67
}
78

89
fn erase_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
@@ -11,6 +12,12 @@ fn erase_regions_ty<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
1112
ty.super_fold_with(&mut RegionEraserVisitor { tcx })
1213
}
1314

15+
fn erase_early_and_late_regions_ty(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
16+
// N.B., use `super_fold_with` here. If we used `fold_with`, it
17+
// could invoke the `erase_regions_ty` query recursively.
18+
ty.super_fold_with(&mut AllRegionEraserVisitor { tcx })
19+
}
20+
1421
impl<'tcx> TyCtxt<'tcx> {
1522
/// Returns an equivalent value with all free regions removed (note
1623
/// that late-bound regions remain, because they are important for
@@ -28,6 +35,36 @@ impl<'tcx> TyCtxt<'tcx> {
2835
debug!("erase_regions({:?}) = {:?}", value, value1);
2936
value1
3037
}
38+
39+
/// Like `erase_regions`, but erases all regions, including late-bound regions.
40+
/// This is only useful during certain parts of codegen, where regions truly
41+
/// don't matter. Normally, `erase_regions` should be used instead.
42+
pub fn erase_early_and_late_regions<T>(self, value: &T) -> T
43+
where
44+
T: TypeFoldable<'tcx>,
45+
{
46+
// If there's nothing to erase avoid performing the query at all
47+
if !value.has_type_flags(TypeFlags::HAS_RE_LATE_BOUND | TypeFlags::HAS_FREE_REGIONS) {
48+
return value.clone();
49+
}
50+
value.fold_with(&mut AllRegionEraserVisitor { tcx: self })
51+
}
52+
}
53+
54+
struct AllRegionEraserVisitor<'tcx> {
55+
tcx: TyCtxt<'tcx>,
56+
}
57+
58+
impl TypeFolder<'tcx> for AllRegionEraserVisitor<'tcx> {
59+
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
60+
self.tcx
61+
}
62+
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
63+
self.tcx.erase_early_and_late_regions_ty(ty)
64+
}
65+
fn fold_region(&mut self, _: ty::Region<'tcx>) -> ty::Region<'tcx> {
66+
self.tcx.lifetimes.re_erased
67+
}
3168
}
3269

3370
struct RegionEraserVisitor<'tcx> {
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Regression test for issue #55976
2+
// Tests that we don't generate invalid LLVM IR when certain
3+
// higher-ranked trait bounds are involved.
4+
5+
// run-pass
6+
7+
pub struct Foo<T>(T, [u8; 64]);
8+
9+
pub fn abc<'a>(x: &Foo<Box<dyn for<'b> Fn(&'b u8)>>) -> &Foo<Box<dyn Fn(&'a u8)>> { x }
10+
11+
fn main() {
12+
abc(&Foo(Box::new(|_x| ()), [0; 64]));
13+
}

0 commit comments

Comments
 (0)