Skip to content

Commit dbf31f1

Browse files
committed
Auto merge of rust-lang#113000 - Mark-Simulacrum:beta-backport, r=Mark-Simulacrum
[beta] backport This PR backports: - rust-lang#112684: Disable alignment checks on i686-pc-windows-msvc - rust-lang#112581: [rustdoc] Fix URL encoding of % sign - rust-lang#112312: Update to LLVM 16.0.5 - rust-lang#112266: Fix type-inference regression in rust-lang#112225 - rust-lang#112062: Make struct layout not depend on unsizeable tail r? `@Mark-Simulacrum`
2 parents 78a6ac0 + f8052fc commit dbf31f1

File tree

13 files changed

+202
-49
lines changed

13 files changed

+202
-49
lines changed

.gitmodules

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
[submodule "src/llvm-project"]
2626
path = src/llvm-project
2727
url = https://github.com/rust-lang/llvm-project.git
28-
branch = rustc/16.0-2023-04-05
28+
branch = rustc/16.0-2023-06-05
2929
[submodule "src/doc/embedded-book"]
3030
path = src/doc/embedded-book
3131
url = https://github.com/rust-embedded/book.git

compiler/rustc_abi/src/layout.rs

+54-44
Original file line numberDiff line numberDiff line change
@@ -57,48 +57,54 @@ pub trait LayoutCalculator {
5757
// run and bias niches to the right and then check which one is closer to one of the struct's
5858
// edges.
5959
if let Some(layout) = &layout {
60-
if let Some(niche) = layout.largest_niche {
61-
let head_space = niche.offset.bytes();
62-
let niche_length = niche.value.size(dl).bytes();
63-
let tail_space = layout.size.bytes() - head_space - niche_length;
64-
65-
// This may end up doing redundant work if the niche is already in the last field
66-
// (e.g. a trailing bool) and there is tail padding. But it's non-trivial to get
67-
// the unpadded size so we try anyway.
68-
if fields.len() > 1 && head_space != 0 && tail_space > 0 {
69-
let alt_layout = univariant(self, dl, fields, repr, kind, NicheBias::End)
70-
.expect("alt layout should always work");
71-
let niche = alt_layout
72-
.largest_niche
73-
.expect("alt layout should have a niche like the regular one");
74-
let alt_head_space = niche.offset.bytes();
75-
let alt_niche_len = niche.value.size(dl).bytes();
76-
let alt_tail_space = alt_layout.size.bytes() - alt_head_space - alt_niche_len;
77-
78-
debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes());
79-
80-
let prefer_alt_layout =
81-
alt_head_space > head_space && alt_head_space > tail_space;
82-
83-
debug!(
84-
"sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\
85-
layout: {}\n\
86-
alt_layout: {}\n",
87-
layout.size.bytes(),
88-
head_space,
89-
niche_length,
90-
tail_space,
91-
alt_head_space,
92-
alt_niche_len,
93-
alt_tail_space,
94-
layout.fields.count(),
95-
prefer_alt_layout,
96-
format_field_niches(&layout, &fields, &dl),
97-
format_field_niches(&alt_layout, &fields, &dl),
98-
);
99-
100-
if prefer_alt_layout {
101-
return Some(alt_layout);
60+
// Don't try to calculate an end-biased layout for unsizable structs,
61+
// otherwise we could end up with different layouts for
62+
// Foo<Type> and Foo<dyn Trait> which would break unsizing
63+
if !matches!(kind, StructKind::MaybeUnsized) {
64+
if let Some(niche) = layout.largest_niche {
65+
let head_space = niche.offset.bytes();
66+
let niche_length = niche.value.size(dl).bytes();
67+
let tail_space = layout.size.bytes() - head_space - niche_length;
68+
69+
// This may end up doing redundant work if the niche is already in the last field
70+
// (e.g. a trailing bool) and there is tail padding. But it's non-trivial to get
71+
// the unpadded size so we try anyway.
72+
if fields.len() > 1 && head_space != 0 && tail_space > 0 {
73+
let alt_layout = univariant(self, dl, fields, repr, kind, NicheBias::End)
74+
.expect("alt layout should always work");
75+
let niche = alt_layout
76+
.largest_niche
77+
.expect("alt layout should have a niche like the regular one");
78+
let alt_head_space = niche.offset.bytes();
79+
let alt_niche_len = niche.value.size(dl).bytes();
80+
let alt_tail_space =
81+
alt_layout.size.bytes() - alt_head_space - alt_niche_len;
82+
83+
debug_assert_eq!(layout.size.bytes(), alt_layout.size.bytes());
84+
85+
let prefer_alt_layout =
86+
alt_head_space > head_space && alt_head_space > tail_space;
87+
88+
debug!(
89+
"sz: {}, default_niche_at: {}+{}, default_tail_space: {}, alt_niche_at/head_space: {}+{}, alt_tail: {}, num_fields: {}, better: {}\n\
90+
layout: {}\n\
91+
alt_layout: {}\n",
92+
layout.size.bytes(),
93+
head_space,
94+
niche_length,
95+
tail_space,
96+
alt_head_space,
97+
alt_niche_len,
98+
alt_tail_space,
99+
layout.fields.count(),
100+
prefer_alt_layout,
101+
format_field_niches(&layout, &fields, &dl),
102+
format_field_niches(&alt_layout, &fields, &dl),
103+
);
104+
105+
if prefer_alt_layout {
106+
return Some(alt_layout);
107+
}
102108
}
103109
}
104110
}
@@ -828,6 +834,7 @@ fn univariant(
828834
if optimize && fields.len() > 1 {
829835
let end = if let StructKind::MaybeUnsized = kind { fields.len() - 1 } else { fields.len() };
830836
let optimizing = &mut inverse_memory_index.raw[..end];
837+
let fields_excluding_tail = &fields.raw[..end];
831838

832839
// If `-Z randomize-layout` was enabled for the type definition we can shuffle
833840
// the field ordering to try and catch some code making assumptions about layouts
@@ -844,8 +851,11 @@ fn univariant(
844851
}
845852
// Otherwise we just leave things alone and actually optimize the type's fields
846853
} else {
847-
let max_field_align = fields.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
848-
let largest_niche_size = fields
854+
// To allow unsizing `&Foo<Type>` -> `&Foo<dyn Trait>`, the layout of the struct must
855+
// not depend on the layout of the tail.
856+
let max_field_align =
857+
fields_excluding_tail.iter().map(|f| f.align().abi.bytes()).max().unwrap_or(1);
858+
let largest_niche_size = fields_excluding_tail
849859
.iter()
850860
.filter_map(|f| f.largest_niche())
851861
.map(|n| n.available(dl))

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+10-1
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
362362
continue;
363363
}
364364

365-
let is_closure = matches!(arg.kind, ExprKind::Closure { .. });
365+
// For this check, we do *not* want to treat async generator closures (async blocks)
366+
// as proper closures. Doing so would regress type inference when feeding
367+
// the return value of an argument-position async block to an argument-position
368+
// closure wrapped in a block.
369+
// See <https://github.com/rust-lang/rust/issues/112225>.
370+
let is_closure = if let ExprKind::Closure(closure) = arg.kind {
371+
!tcx.generator_is_async(closure.def_id.to_def_id())
372+
} else {
373+
false
374+
};
366375
if is_closure != check_closures {
367376
continue;
368377
}

compiler/rustc_mir_transform/src/check_alignment.rs

+4
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ pub struct CheckAlignment;
1414

1515
impl<'tcx> MirPass<'tcx> for CheckAlignment {
1616
fn is_enabled(&self, sess: &Session) -> bool {
17+
// FIXME(#112480) MSVC and rustc disagree on minimum stack alignment on x86 Windows
18+
if sess.target.llvm_target == "i686-pc-windows-msvc" {
19+
return false;
20+
}
1721
sess.opts.debug_assertions
1822
}
1923

src/librustdoc/html/render/mod.rs

-2
Original file line numberDiff line numberDiff line change
@@ -1933,8 +1933,6 @@ pub(crate) fn small_url_encode(s: String) -> String {
19331933
// While the same is not true for hashes, rustdoc only needs to be
19341934
// consistent with itself when encoding them.
19351935
st += "+";
1936-
} else if b == b'%' {
1937-
st += "%%";
19381936
} else {
19391937
write!(st, "%{:02X}", b).unwrap();
19401938
}

src/llvm-project

Submodule llvm-project updated 69 files
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// check-pass
2+
// edition:2021
3+
4+
use core::future::Future;
5+
6+
fn main() {
7+
do_async(async { (0,) }, {
8+
// closure must be inside block
9+
|info| println!("{:?}", info.0)
10+
});
11+
}
12+
13+
fn do_async<R, Fut, F>(_tokio_fut: Fut, _glib_closure: F)
14+
where
15+
Fut: Future<Output = R>,
16+
F: FnOnce(R),
17+
{
18+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
// edition:2021
2+
3+
// With the current compiler logic, we cannot have both the `112225-1` case,
4+
// and this `112225-2` case working, as the type inference depends on the evaluation
5+
// order, and there is some explicit ordering going on.
6+
// See the `check_closures` part in `FnCtxt::check_argument_types`.
7+
// The `112225-1` case was a regression in real world code, whereas the `112225-2`
8+
// case never used to work prior to 1.70.
9+
10+
use core::future::Future;
11+
12+
fn main() {
13+
let x = Default::default();
14+
//~^ ERROR: type annotations needed
15+
do_async(
16+
async { x.0; },
17+
{ || { let _: &(i32,) = &x; } },
18+
);
19+
}
20+
fn do_async<Fut, T>(_fut: Fut, _val: T, ) {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
error[E0282]: type annotations needed
2+
--> $DIR/issue-112225-2.rs:13:9
3+
|
4+
LL | let x = Default::default();
5+
| ^
6+
...
7+
LL | async { x.0; },
8+
| - type must be known at this point
9+
|
10+
help: consider giving `x` an explicit type
11+
|
12+
LL | let x: /* Type */ = Default::default();
13+
| ++++++++++++
14+
15+
error: aborting due to previous error
16+
17+
For more information about this error, try `rustc --explain E0282`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// run-pass
2+
3+
// Check that unsizing doesn't reorder fields.
4+
5+
#![allow(dead_code)]
6+
7+
use std::fmt::Debug;
8+
9+
#[derive(Debug)]
10+
struct GcNode<T: ?Sized> {
11+
gets_swapped_with_next: usize,
12+
next: Option<&'static GcNode<dyn Debug>>,
13+
tail: T,
14+
}
15+
16+
fn main() {
17+
let node: Box<GcNode<dyn Debug>> = Box::new(GcNode {
18+
gets_swapped_with_next: 42,
19+
next: None,
20+
tail: Box::new(1),
21+
});
22+
23+
assert_eq!(node.gets_swapped_with_next, 42);
24+
assert!(node.next.is_none());
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// run-pass
2+
3+
// Check that unsizing does not change which field is considered for niche layout.
4+
5+
#![feature(offset_of)]
6+
#![allow(dead_code)]
7+
8+
#[derive(Clone)]
9+
struct WideptrField<T: ?Sized> {
10+
first: usize,
11+
second: usize,
12+
niche: NicheAtEnd,
13+
tail: T,
14+
}
15+
16+
#[derive(Clone)]
17+
#[repr(C)]
18+
struct NicheAtEnd {
19+
arr: [u8; 7],
20+
b: bool,
21+
}
22+
23+
type Tail = [bool; 8];
24+
25+
fn main() {
26+
assert_eq!(
27+
core::mem::offset_of!(WideptrField<Tail>, niche),
28+
core::mem::offset_of!(WideptrField<dyn Send>, niche)
29+
);
30+
}

tests/ui/mir/mir_alignment_check.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// run-fail
22
// ignore-wasm32-bare: No panic messages
3+
// ignore-i686-pc-windows-msvc: #112480
34
// compile-flags: -C debug-assertions
45
// error-pattern: misaligned pointer dereference: address must be a multiple of 0x4 but is
56

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// run-pass
2+
// only-i686-pc-windows-msvc
3+
// compile-flags: -Copt-level=0 -Cdebug-assertions=yes
4+
5+
// MSVC isn't sure if on 32-bit Windows its u64 type is 8-byte-aligned or 4-byte-aligned.
6+
// So this test ensures that on i686-pc-windows-msvc, we do not insert a runtime check
7+
// that will fail on dereferencing of a pointer to u64 which is not 8-byte-aligned but is
8+
// 4-byte-aligned.
9+
10+
#![feature(strict_provenance)]
11+
12+
fn main() {
13+
let mut x = [0u64; 2];
14+
let ptr: *mut u8 = x.as_mut_ptr().cast::<u8>();
15+
unsafe {
16+
let misaligned = ptr.add(4).cast::<u64>();
17+
assert!(misaligned.addr() % 8 != 0);
18+
assert!(misaligned.addr() % 4 == 0);
19+
*misaligned = 42;
20+
}
21+
}

0 commit comments

Comments
 (0)