diff options
| author | Ralf Jung <post@ralfj.de> | 2025-04-29 07:18:23 +0000 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-04-29 07:18:23 +0000 |
| commit | 929117e9a688601700ceb749750a8d5b55a68437 (patch) | |
| tree | 9889b042b45c5a367ccf3928d8b053b7707251e1 | |
| parent | fe89e798fdb4902dac4624639fac464931d3f298 (diff) | |
| parent | 0917a072641fb50866fed781af4745321d9e0373 (diff) | |
| download | rust-929117e9a688601700ceb749750a8d5b55a68437.tar.gz rust-929117e9a688601700ceb749750a8d5b55a68437.zip | |
Merge pull request #4301 from rust-lang/rustup-2025-04-29
Automatic Rustup
416 files changed, 7701 insertions, 3796 deletions
diff --git a/Cargo.lock b/Cargo.lock index ee6726eae51..60daa453c60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -737,6 +737,7 @@ dependencies = [ "libc", "miow", "miropt-test-tools", + "rayon", "regex", "rustfix", "semver", @@ -3699,6 +3700,9 @@ dependencies = [ [[package]] name = "rustc_fs_util" version = "0.0.0" +dependencies = [ + "tempfile", +] [[package]] name = "rustc_graphviz" @@ -3753,7 +3757,6 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", - "rustc_target", "rustc_trait_selection", "smallvec", "tracing", @@ -3792,6 +3795,7 @@ dependencies = [ "rustc_middle", "rustc_session", "rustc_span", + "rustc_target", "rustc_trait_selection", "smallvec", "tracing", @@ -4011,7 +4015,6 @@ dependencies = [ "rustc_session", "rustc_span", "rustc_target", - "tempfile", "tracing", ] @@ -4916,15 +4919,6 @@ dependencies = [ ] [[package]] -name = "spdx" -version = "0.10.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58b69356da67e2fc1f542c71ea7e654a361a79c938e4424392ecf4fa065d2193" -dependencies = [ - "smallvec", -] - -[[package]] name = "spdx-expression" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -5743,9 +5737,9 @@ dependencies = [ [[package]] name = "wasi-preview1-component-adapter-provider" -version = "29.0.1" +version = "31.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcd9f21bbde82ba59e415a8725e6ad0d0d7e9e460b1a3ccbca5bdee952c1a324" +checksum = "86fabda09a0d89ffd1615b297b4a5d4b4d99df9598aeb24685837e63019e927b" [[package]] name = "wasm-bindgen" @@ -5807,9 +5801,9 @@ dependencies = [ [[package]] name = "wasm-component-ld" -version = "0.5.12" +version = "0.5.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "580305a8e3f1b7a79859a8db897de643533b2851c5eb080fe5800233f16dec88" +checksum = "a60a07a994a3538b57d8c5f8caba19f4793fb4c7156276e5e90e90acbb829e20" dependencies = [ "anyhow", "clap", @@ -5817,7 +5811,7 @@ dependencies = [ "libc", "tempfile", "wasi-preview1-component-adapter-provider", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wat", "windows-sys 0.59.0", "winsplit", @@ -5844,39 +5838,24 @@ dependencies = [ [[package]] name = "wasm-encoder" -version = "0.223.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0a96fdeaee8fbeb4bd917fb8157d48c0d61c3b1f4ee4c363c8e8d68b2f4fe8" -dependencies = [ - "leb128", - "wasmparser 0.223.1", -] - -[[package]] -name = "wasm-encoder" -version = "0.228.0" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d30290541f2d4242a162bbda76b8f2d8b1ac59eab3568ed6f2327d52c9b2c4" +checksum = "38ba1d491ecacb085a2552025c10a675a6fddcbd03b1fc9b36c536010ce265d2" dependencies = [ "leb128fmt", - "wasmparser 0.228.0", + "wasmparser 0.229.0", ] [[package]] name = "wasm-metadata" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2e7e37883181704d96b89dbd8f1291be13694c71cd0663aebb94b46d235a377" +checksum = "78fdb7d29a79191ab363dc90c1ddd3a1e880ffd5348d92d48482393a9e6c5f4d" dependencies = [ "anyhow", "indexmap", - "serde", - "serde_derive", - "serde_json", - "spdx", - "url", - "wasm-encoder 0.223.1", - "wasmparser 0.223.1", + "wasm-encoder 0.229.0", + "wasmparser 0.229.0", ] [[package]] @@ -5900,9 +5879,9 @@ dependencies = [ [[package]] name = "wasmparser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "664b980991ed9a8c834eb528a8979ab1109edcf52dc05dd5751e2cc3fb31035d" +checksum = "0cc3b1f053f5d41aa55640a1fa9b6d1b8a9e4418d118ce308d20e24ff3575a8c" dependencies = [ "bitflags", "hashbrown", @@ -5912,34 +5891,23 @@ dependencies = [ ] [[package]] -name = "wasmparser" -version = "0.228.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4abf1132c1fdf747d56bbc1bb52152400c70f336870f968b85e89ea422198ae3" -dependencies = [ - "bitflags", - "indexmap", - "semver", -] - -[[package]] name = "wast" -version = "228.0.0" +version = "229.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5aae124478cb51439f6587f074a3a5e835afd22751c59a87b2e2a882727c97" +checksum = "63fcaff613c12225696bb163f79ca38ffb40e9300eff0ff4b8aa8b2f7eadf0d9" dependencies = [ "bumpalo", "leb128fmt", "memchr", "unicode-width 0.2.0", - "wasm-encoder 0.228.0", + "wasm-encoder 0.229.0", ] [[package]] name = "wat" -version = "1.228.0" +version = "1.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ec29c89a8d055df988de7236483bf569988ac3d6905899f6af5ef920f9385ad" +checksum = "4189bad08b70455a9e9e67dc126d2dcf91fac143a80f1046747a5dde6d4c33e0" dependencies = [ "wast", ] @@ -6398,9 +6366,9 @@ dependencies = [ [[package]] name = "wit-component" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fc2fcc52a79f6f010a89c867e53e06d4227f86c58984add3e37f32b8e7af5f0" +checksum = "7f550067740e223bfe6c4878998e81cdbe2529dd9a793dc49248dd6613394e8b" dependencies = [ "anyhow", "bitflags", @@ -6409,17 +6377,17 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "wasm-encoder 0.223.1", + "wasm-encoder 0.229.0", "wasm-metadata", - "wasmparser 0.223.1", + "wasmparser 0.229.0", "wit-parser", ] [[package]] name = "wit-parser" -version = "0.223.1" +version = "0.229.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "263fde17f1fbe55a413f16eb59094bf751795c6da469a05c3d45ea6c77df6b40" +checksum = "459c6ba62bf511d6b5f2a845a2a736822e38059c1cfa0b644b467bbbfae4efa6" dependencies = [ "anyhow", "id-arena", @@ -6430,7 +6398,7 @@ dependencies = [ "serde_derive", "serde_json", "unicode-xid", - "wasmparser 0.223.1", + "wasmparser 0.229.0", ] [[package]] diff --git a/bootstrap.example.toml b/bootstrap.example.toml index 72c4492d465..b8f863bbed1 100644 --- a/bootstrap.example.toml +++ b/bootstrap.example.toml @@ -180,7 +180,7 @@ # Note that this will attempt to download GCC even if there are local # modifications to the `src/gcc` submodule. # Currently, this is only supported for the `x86_64-unknown-linux-gnu` target. -# download-ci-gcc = false +#download-ci-gcc = false # ============================================================================= # General build configuration options @@ -500,7 +500,7 @@ # building without optimizations takes much longer than optimizing. Further, some platforms # fail to build without this optimization (c.f. #65352). # The valid options are: -# true - Enable optimizations. +# true - Enable optimizations (same as 3). # false - Disable optimizations. # 0 - Disable optimizations. # 1 - Basic optimizations. diff --git a/compiler/rustc_ast_pretty/src/lib.rs b/compiler/rustc_ast_pretty/src/lib.rs index 84d9ce278a2..1079ccccb03 100644 --- a/compiler/rustc_ast_pretty/src/lib.rs +++ b/compiler/rustc_ast_pretty/src/lib.rs @@ -2,6 +2,7 @@ #![allow(internal_features)] #![doc(rust_logo)] #![feature(box_patterns)] +#![feature(negative_impls)] #![feature(rustdoc_internals)] // tidy-alphabetical-end diff --git a/compiler/rustc_ast_pretty/src/pp.rs b/compiler/rustc_ast_pretty/src/pp.rs index e4fd7e94fde..142c80b8e39 100644 --- a/compiler/rustc_ast_pretty/src/pp.rs +++ b/compiler/rustc_ast_pretty/src/pp.rs @@ -234,6 +234,38 @@ struct BufEntry { size: isize, } +// Boxes opened with methods like `Printer::{cbox,ibox}` must be closed with +// `Printer::end`. Failure to do so can result in bad indenting, or in extreme +// cases, cause no output to be produced at all. +// +// Box opening and closing used to be entirely implicit, which was hard to +// understand and easy to get wrong. This marker type is now returned from the +// box opening methods and forgotten by `Printer::end`. Any marker that isn't +// forgotten will trigger a panic in `drop`. (Closing a box more than once +// isn't possible because `BoxMarker` doesn't implement `Copy` or `Clone`.) +// +// FIXME(nnethercote): the panic in `drop` is currently disabled because a few +// places fail to close their boxes. It can be enabled once they are fixed. +// +// Note: it would be better to make open/close mismatching impossible and avoid +// the need for this marker type altogether by having functions like +// `with_ibox` that open a box, call a closure, and then close the box. That +// would work for simple cases, but box lifetimes sometimes interact with +// complex control flow and across function boundaries in ways that are +// difficult to handle with such a technique. +#[must_use] +pub struct BoxMarker; + +impl !Clone for BoxMarker {} +impl !Copy for BoxMarker {} + +impl Drop for BoxMarker { + fn drop(&mut self) { + // FIXME(nnethercote): enable once the bad cases are fixed + //panic!("BoxMarker not ended with `Printer::end()`"); + } +} + impl Printer { pub fn new() -> Self { Printer { @@ -270,7 +302,8 @@ impl Printer { } } - fn scan_begin(&mut self, token: BeginToken) { + // This is is where `BoxMarker`s are produced. + fn scan_begin(&mut self, token: BeginToken) -> BoxMarker { if self.scan_stack.is_empty() { self.left_total = 1; self.right_total = 1; @@ -278,15 +311,18 @@ impl Printer { } let right = self.buf.push(BufEntry { token: Token::Begin(token), size: -self.right_total }); self.scan_stack.push_back(right); + BoxMarker } - fn scan_end(&mut self) { + // This is is where `BoxMarker`s are consumed. + fn scan_end(&mut self, b: BoxMarker) { if self.scan_stack.is_empty() { self.print_end(); } else { let right = self.buf.push(BufEntry { token: Token::End, size: -1 }); self.scan_stack.push_back(right); } + std::mem::forget(b) } fn scan_break(&mut self, token: BreakToken) { diff --git a/compiler/rustc_ast_pretty/src/pp/convenience.rs b/compiler/rustc_ast_pretty/src/pp/convenience.rs index a1c07bb07e4..9b902b38122 100644 --- a/compiler/rustc_ast_pretty/src/pp/convenience.rs +++ b/compiler/rustc_ast_pretty/src/pp/convenience.rs @@ -1,25 +1,27 @@ use std::borrow::Cow; -use crate::pp::{BeginToken, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token}; +use crate::pp::{ + BeginToken, BoxMarker, BreakToken, Breaks, IndentStyle, Printer, SIZE_INFINITY, Token, +}; impl Printer { /// "raw box" - pub fn rbox(&mut self, indent: isize, breaks: Breaks) { + pub fn rbox(&mut self, indent: isize, breaks: Breaks) -> BoxMarker { self.scan_begin(BeginToken { indent: IndentStyle::Block { offset: indent }, breaks }) } /// Inconsistent breaking box - pub fn ibox(&mut self, indent: isize) { + pub fn ibox(&mut self, indent: isize) -> BoxMarker { self.rbox(indent, Breaks::Inconsistent) } /// Consistent breaking box - pub fn cbox(&mut self, indent: isize) { + pub fn cbox(&mut self, indent: isize) -> BoxMarker { self.rbox(indent, Breaks::Consistent) } - pub fn visual_align(&mut self) { - self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }); + pub fn visual_align(&mut self) -> BoxMarker { + self.scan_begin(BeginToken { indent: IndentStyle::Visual, breaks: Breaks::Consistent }) } pub fn break_offset(&mut self, n: usize, off: isize) { @@ -30,8 +32,8 @@ impl Printer { }); } - pub fn end(&mut self) { - self.scan_end() + pub fn end(&mut self, b: BoxMarker) { + self.scan_end(b) } pub fn eof(mut self) -> String { diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 62a50c73855..985359e1234 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -26,7 +26,7 @@ use rustc_span::symbol::IdentPrinter; use rustc_span::{BytePos, CharPos, DUMMY_SP, FileName, Ident, Pos, Span, Symbol, kw, sym}; use crate::pp::Breaks::{Consistent, Inconsistent}; -use crate::pp::{self, Breaks}; +use crate::pp::{self, BoxMarker, Breaks}; use crate::pprust::state::fixup::FixupContext; pub enum MacHeader<'a> { @@ -419,7 +419,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere { let mut it = elts.into_iter(); - self.rbox(0, b); + let rb = self.rbox(0, b); if let Some(first) = it.next() { op(self, first); for elt in it { @@ -430,7 +430,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere op(self, elt); } } - self.end(); + self.end(rb); } fn commasep<'x, T: 'x, F, I>(&mut self, b: Breaks, elts: I, op: F) @@ -461,7 +461,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.zerobreak(); } if let Some((last, lines)) = cmnt.lines.split_last() { - self.ibox(0); + let ib = self.ibox(0); for line in lines { self.word(line.clone()); @@ -471,7 +471,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.word(last.clone()); self.space(); - self.end(); + self.end(ib); } self.zerobreak() } @@ -494,14 +494,14 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere self.word(line.clone()); self.hardbreak() } else { - self.visual_align(); + let vb = self.visual_align(); for line in &cmnt.lines { if !line.is_empty() { self.word(line.clone()); } self.hardbreak(); } - self.end(); + self.end(vb); } } CommentStyle::BlankLine => { @@ -620,7 +620,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } fn print_attr_item(&mut self, item: &ast::AttrItem, span: Span) { - self.ibox(0); + let ib = self.ibox(0); match item.unsafety { ast::Safety::Unsafe(_) => { self.word("unsafe"); @@ -653,7 +653,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere ast::Safety::Unsafe(_) => self.pclose(), ast::Safety::Default | ast::Safety::Safe(_) => {} } - self.end(); + self.end(ib); } /// This doesn't deserve to be called "pretty" printing, but it should be @@ -739,9 +739,7 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere convert_dollar_crate: bool, span: Span, ) { - if delim == Delimiter::Brace { - self.cbox(INDENT_UNIT); - } + let cb = (delim == Delimiter::Brace).then(|| self.cbox(INDENT_UNIT)); match header { Some(MacHeader::Path(path)) => self.print_path(path, false, 0), Some(MacHeader::Keyword(kw)) => self.word(kw), @@ -763,18 +761,18 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere if !tts.is_empty() { self.space(); } - self.ibox(0); + let ib = self.ibox(0); self.print_tts(tts, convert_dollar_crate); - self.end(); + self.end(ib); let empty = tts.is_empty(); - self.bclose(span, empty); + self.bclose(span, empty, cb.unwrap()); } delim => { let token_str = self.token_kind_to_string(&delim.as_open_token_kind()); self.word(token_str); - self.ibox(0); + let ib = self.ibox(0); self.print_tts(tts, convert_dollar_crate); - self.end(); + self.end(ib); let token_str = self.token_kind_to_string(&delim.as_close_token_kind()); self.word(token_str); } @@ -828,37 +826,38 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere } } - fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) { + fn head<S: Into<Cow<'static, str>>>(&mut self, w: S) -> (BoxMarker, BoxMarker) { let w = w.into(); // Outer-box is consistent. - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); // Head-box is inconsistent. - self.ibox(0); + let ib = self.ibox(0); // Keyword that starts the head. if !w.is_empty() { self.word_nbsp(w); } + (cb, ib) } - fn bopen(&mut self) { + fn bopen(&mut self, ib: BoxMarker) { self.word("{"); - self.end(); // Close the head-box. + self.end(ib); } - fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, close_box: bool) { + fn bclose_maybe_open(&mut self, span: rustc_span::Span, empty: bool, cb: Option<BoxMarker>) { let has_comment = self.maybe_print_comment(span.hi()); if !empty || has_comment { self.break_offset_if_not_bol(1, -INDENT_UNIT); } self.word("}"); - if close_box { - self.end(); // Close the outer-box. + if let Some(cb) = cb { + self.end(cb); } } - fn bclose(&mut self, span: rustc_span::Span, empty: bool) { - let close_box = true; - self.bclose_maybe_open(span, empty, close_box) + fn bclose(&mut self, span: rustc_span::Span, empty: bool, cb: BoxMarker) { + let cb = Some(cb); + self.bclose_maybe_open(span, empty, cb) } fn break_offset_if_not_bol(&mut self, n: usize, off: isize) { @@ -1014,11 +1013,8 @@ pub trait PrintState<'a>: std::ops::Deref<Target = pp::Printer> + std::ops::Dere fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { - // Containing cbox, will be closed by `print_block` at `}`. - s.cbox(INDENT_UNIT); - // Head-ibox, will be closed by `print_block` after `{`. - s.ibox(0); - s.print_block(blk) + let (cb, ib) = s.head(""); + s.print_block(blk, cb, ib) }) } @@ -1090,7 +1086,7 @@ impl<'a> State<'a> { F: FnMut(&mut State<'_>, &T), G: FnMut(&T) -> rustc_span::Span, { - self.rbox(0, b); + let rb = self.rbox(0, b); let len = elts.len(); let mut i = 0; for elt in elts { @@ -1103,7 +1099,7 @@ impl<'a> State<'a> { self.space_if_not_bol(); } } - self.end(); + self.end(rb); } fn commasep_exprs(&mut self, b: Breaks, exprs: &[P<ast::Expr>]) { @@ -1172,7 +1168,7 @@ impl<'a> State<'a> { pub fn print_type(&mut self, ty: &ast::Ty) { self.maybe_print_comment(ty.span.lo()); - self.ibox(0); + let ib = self.ibox(0); match &ty.kind { ast::TyKind::Slice(ty) => { self.word("["); @@ -1214,12 +1210,12 @@ impl<'a> State<'a> { self.print_ty_fn(f.ext, f.safety, &f.decl, None, &f.generic_params); } ast::TyKind::UnsafeBinder(f) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("unsafe"); self.print_generic_params(&f.generic_params); self.nbsp(); self.print_type(&f.inner_ty); - self.end(); + self.end(ib); } ast::TyKind::Path(None, path) => { self.print_path(path, false, 0); @@ -1277,7 +1273,7 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } } - self.end(); + self.end(ib); } fn print_trait_ref(&mut self, t: &ast::TraitRef) { @@ -1324,15 +1320,15 @@ impl<'a> State<'a> { ast::StmtKind::Let(loc) => { self.print_outer_attributes(&loc.attrs); self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib1 = self.ibox(INDENT_UNIT); if loc.super_.is_some() { self.word_nbsp("super"); } self.word_nbsp("let"); - self.ibox(INDENT_UNIT); + let ib2 = self.ibox(INDENT_UNIT); self.print_local_decl(loc); - self.end(); + self.end(ib2); if let Some((init, els)) = loc.kind.init_else_opt() { self.nbsp(); self.word_space("="); @@ -1342,14 +1338,14 @@ impl<'a> State<'a> { FixupContext::default(), ); if let Some(els) = els { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word(" else "); - self.print_block(els); + self.print_block(els, cb, ib); } } self.word(";"); - self.end(); // `let` ibox + self.end(ib1); } ast::StmtKind::Item(item) => self.print_item(item), ast::StmtKind::Expr(expr) => { @@ -1380,23 +1376,30 @@ impl<'a> State<'a> { self.maybe_print_trailing_comment(st.span, None) } - fn print_block(&mut self, blk: &ast::Block) { - self.print_block_with_attrs(blk, &[]) + fn print_block(&mut self, blk: &ast::Block, cb: BoxMarker, ib: BoxMarker) { + self.print_block_with_attrs(blk, &[], cb, ib) } - fn print_block_unclosed_indent(&mut self, blk: &ast::Block) { - self.print_block_maybe_unclosed(blk, &[], false) + fn print_block_unclosed_indent(&mut self, blk: &ast::Block, ib: BoxMarker) { + self.print_block_maybe_unclosed(blk, &[], None, ib) } - fn print_block_with_attrs(&mut self, blk: &ast::Block, attrs: &[ast::Attribute]) { - self.print_block_maybe_unclosed(blk, attrs, true) + fn print_block_with_attrs( + &mut self, + blk: &ast::Block, + attrs: &[ast::Attribute], + cb: BoxMarker, + ib: BoxMarker, + ) { + self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib) } fn print_block_maybe_unclosed( &mut self, blk: &ast::Block, attrs: &[ast::Attribute], - close_box: bool, + cb: Option<BoxMarker>, + ib: BoxMarker, ) { match blk.rules { BlockCheckMode::Unsafe(..) => self.word_space("unsafe"), @@ -1404,7 +1407,7 @@ impl<'a> State<'a> { } self.maybe_print_comment(blk.span.lo()); self.ann.pre(self, AnnNode::Block(blk)); - self.bopen(); + self.bopen(ib); let has_attrs = self.print_inner_attributes(attrs); @@ -1421,7 +1424,7 @@ impl<'a> State<'a> { } let empty = !has_attrs && blk.stmts.is_empty(); - self.bclose_maybe_open(blk.span, empty, close_box); + self.bclose_maybe_open(blk.span, empty, cb); self.ann.post(self, AnnNode::Block(blk)) } @@ -1556,8 +1559,8 @@ impl<'a> State<'a> { } } InlineAsmOperand::Label { block } => { - s.head("label"); - s.print_block(block); + let (cb, ib) = s.head("label"); + s.print_block(block, cb, ib); } } } @@ -1671,13 +1674,13 @@ impl<'a> State<'a> { Consistent, fields, |s, f| { - s.cbox(INDENT_UNIT); + let cb = s.cbox(INDENT_UNIT); if !f.is_shorthand { s.print_ident(f.ident); s.word_nbsp(":"); } s.print_pat(&f.pat); - s.end(); + s.end(cb); }, |f| f.pat.span, ); @@ -1928,7 +1931,7 @@ impl<'a> State<'a> { } fn print_param(&mut self, input: &ast::Param, is_closure: bool) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_outer_attributes_inline(&input.attrs); @@ -1947,16 +1950,16 @@ impl<'a> State<'a> { } } } - self.end(); + self.end(ib); } fn print_fn_ret_ty(&mut self, fn_ret_ty: &ast::FnRetTy) { if let ast::FnRetTy::Ty(ty) = fn_ret_ty { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("->"); self.print_type(ty); - self.end(); + self.end(ib); self.maybe_print_comment(ty.span.lo()); } } @@ -1969,12 +1972,12 @@ impl<'a> State<'a> { name: Option<Ident>, generic_params: &[ast::GenericParam], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_formal_generic_params(generic_params); let generics = ast::Generics::default(); let header = ast::FnHeader { safety, ext, ..ast::FnHeader::default() }; self.print_fn(decl, header, name, &generics); - self.end(); + self.end(ib); } fn print_fn_header_info(&mut self, header: ast::FnHeader) { @@ -2052,7 +2055,7 @@ impl<'a> State<'a> { } fn print_meta_item(&mut self, item: &ast::MetaItem) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); match &item.kind { ast::MetaItemKind::Word => self.print_path(&item.path, false, 0), ast::MetaItemKind::NameValue(value) => { @@ -2068,7 +2071,7 @@ impl<'a> State<'a> { self.pclose(); } } - self.end(); + self.end(ib); } pub(crate) fn bounds_to_string(&self, bounds: &[ast::GenericBound]) -> String { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs index df848a26d39..38cadc77b77 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/expr.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/expr.rs @@ -13,7 +13,7 @@ use rustc_ast::{ use crate::pp::Breaks::Inconsistent; use crate::pprust::state::fixup::FixupContext; -use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State}; +use crate::pprust::state::{AnnNode, BoxMarker, INDENT_UNIT, PrintState, State}; impl<'a> State<'a> { fn print_else(&mut self, els: Option<&ast::Expr>) { @@ -21,20 +21,20 @@ impl<'a> State<'a> { match &_else.kind { // Another `else if` block. ast::ExprKind::If(i, then, e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else if "); self.print_expr_as_cond(i); self.space(); - self.print_block(then); + self.print_block(then, cb, ib); self.print_else(e.as_deref()) } // Final `else` block. - ast::ExprKind::Block(b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + ast::ExprKind::Block(b, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else "); - self.print_block(b) + self.print_block(b, cb, ib) } // Constraints would be great here! _ => { @@ -45,10 +45,12 @@ impl<'a> State<'a> { } fn print_if(&mut self, test: &ast::Expr, blk: &ast::Block, elseopt: Option<&ast::Expr>) { - self.head("if"); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.word_nbsp("if"); self.print_expr_as_cond(test); self.space(); - self.print_block(blk); + self.print_block(blk, cb, ib); self.print_else(elseopt) } @@ -110,11 +112,11 @@ impl<'a> State<'a> { } fn print_expr_vec(&mut self, exprs: &[P<ast::Expr>]) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.commasep_exprs(Inconsistent, exprs); self.word("]"); - self.end(); + self.end(ib); } pub(super) fn print_expr_anon_const( @@ -122,27 +124,27 @@ impl<'a> State<'a> { expr: &ast::AnonConst, attrs: &[ast::Attribute], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("const"); self.nbsp(); if let ast::ExprKind::Block(block, None) = &expr.value.kind { - self.cbox(0); - self.ibox(0); - self.print_block_with_attrs(block, attrs); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.print_block_with_attrs(block, attrs, cb, ib); } else { self.print_expr(&expr.value, FixupContext::default()); } - self.end(); + self.end(ib); } fn print_expr_repeat(&mut self, element: &ast::Expr, count: &ast::AnonConst) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.print_expr(element, FixupContext::default()); self.word_space(";"); self.print_expr(&count.value, FixupContext::default()); self.word("]"); - self.end(); + self.end(ib); } fn print_expr_struct( @@ -167,7 +169,7 @@ impl<'a> State<'a> { self.word("}"); return; } - self.cbox(0); + let cb = self.cbox(0); for (pos, field) in fields.iter().with_position() { let is_first = matches!(pos, Position::First | Position::Only); let is_last = matches!(pos, Position::Last | Position::Only); @@ -198,7 +200,7 @@ impl<'a> State<'a> { self.space(); } self.offset(-INDENT_UNIT); - self.end(); + self.end(cb); self.word("}"); } @@ -366,7 +368,7 @@ impl<'a> State<'a> { self.print_outer_attributes(attrs); } - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); // The Match subexpression in `match x {} - 1` must be parenthesized if // it is the leftmost subexpression in a statement: @@ -438,14 +440,14 @@ impl<'a> State<'a> { ast::ExprKind::Type(expr, ty) => { self.word("builtin # type_ascribe"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); self.word(","); self.space_if_not_bol(); self.print_type(ty); - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::Let(pat, scrutinee, _, _) => { @@ -457,20 +459,20 @@ impl<'a> State<'a> { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("while"); self.print_expr_as_cond(test); self.space(); - self.print_block_with_attrs(blk, attrs); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::ForLoop { pat, iter, body, label, kind } => { if let Some(label) = label { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("for"); if kind == &ForLoopKind::ForAwait { self.word_nbsp("await"); @@ -480,21 +482,21 @@ impl<'a> State<'a> { self.word_space("in"); self.print_expr_as_cond(iter); self.space(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } ast::ExprKind::Loop(blk, opt_label, _) => { if let Some(label) = opt_label { self.print_ident(label.ident); self.word_space(":"); } - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("loop"); - self.print_block_with_attrs(blk, attrs); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Match(expr, arms, match_kind) => { - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); match match_kind { MatchKind::Prefix => { @@ -512,13 +514,13 @@ impl<'a> State<'a> { } } - self.bopen(); + self.bopen(ib); self.print_inner_attributes_no_trailing_hardbreak(attrs); for arm in arms { self.print_arm(arm); } let empty = attrs.is_empty() && arms.is_empty(); - self.bclose(expr.span, empty); + self.bclose(expr.span, empty, cb); } ast::ExprKind::Closure(box ast::Closure { binder, @@ -540,12 +542,15 @@ impl<'a> State<'a> { self.print_fn_params_and_ret(fn_decl, true); self.space(); self.print_expr(body, FixupContext::default()); - self.end(); // need to close a box + // FIXME(nnethercote): Bogus. Reduce visibility of `ended` once it's fixed. + let fake_ib = BoxMarker; + self.end(fake_ib); - // a box will be closed by print_expr, but we didn't want an overall + // A box will be closed by print_expr, but we didn't want an overall // wrapper so we closed the corresponding opening. so create an // empty box to satisfy the close. - self.ibox(0); + // FIXME(nnethercote): Bogus. + let _ib = self.ibox(0); } ast::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { @@ -553,18 +558,18 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at } - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after { - self.ibox(0); - self.print_block_with_attrs(blk, attrs); + let ib = self.ibox(0); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Gen(capture_clause, blk, kind, _decl_span) => { self.word_nbsp(kind.modifier()); self.print_capture_clause(*capture_clause); // cbox/ibox in analogy to the `ExprKind::Block` arm above - self.cbox(0); - self.ibox(0); - self.print_block_with_attrs(blk, attrs); + let cb = self.cbox(0); + let ib = self.ibox(0); + self.print_block_with_attrs(blk, attrs, cb, ib); } ast::ExprKind::Await(expr, _) => { self.print_expr_cond_paren( @@ -726,19 +731,19 @@ impl<'a> State<'a> { // FIXME: Print `builtin # format_args` once macro `format_args` uses `builtin_syntax`. self.word("format_args!"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.word(reconstruct_format_args_template_string(&fmt.template)); for arg in fmt.arguments.all_args() { self.word_space(","); self.print_expr(&arg.expr, FixupContext::default()); } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::OffsetOf(container, fields) => { self.word("builtin # offset_of"); self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_type(container); self.word(","); self.space(); @@ -751,7 +756,7 @@ impl<'a> State<'a> { self.print_ident(field); } } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::MacCall(m) => self.print_mac(m), @@ -789,10 +794,10 @@ impl<'a> State<'a> { self.word("?") } ast::ExprKind::TryBlock(blk) => { - self.cbox(0); - self.ibox(0); + let cb = self.cbox(0); + let ib = self.ibox(0); self.word_nbsp("try"); - self.print_block_with_attrs(blk, attrs) + self.print_block_with_attrs(blk, attrs, cb, ib) } ast::ExprKind::UnsafeBinderCast(kind, expr, ty) => { self.word("builtin # "); @@ -801,7 +806,7 @@ impl<'a> State<'a> { ast::UnsafeBinderCastKind::Unwrap => self.word("unwrap_binder"), } self.popen(); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr, FixupContext::default()); if let Some(ty) = ty { @@ -810,7 +815,7 @@ impl<'a> State<'a> { self.print_type(ty); } - self.end(); + self.end(ib); self.pclose(); } ast::ExprKind::Err(_) => { @@ -831,7 +836,7 @@ impl<'a> State<'a> { self.pclose(); } - self.end(); + self.end(ib); } fn print_arm(&mut self, arm: &ast::Arm) { @@ -839,8 +844,8 @@ impl<'a> State<'a> { if arm.attrs.is_empty() { self.space(); } - self.cbox(INDENT_UNIT); - self.ibox(0); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(0); self.maybe_print_comment(arm.pat.span.lo()); self.print_outer_attributes(&arm.attrs); self.print_pat(&arm.pat); @@ -861,8 +866,7 @@ impl<'a> State<'a> { self.word_space(":"); } - // The block will close the pattern's ibox. - self.print_block_unclosed_indent(blk); + self.print_block_unclosed_indent(blk, ib); // If it is a user-provided unsafe block, print a comma after it. if let BlockCheckMode::Unsafe(ast::UserProvided) = blk.rules { @@ -870,15 +874,16 @@ impl<'a> State<'a> { } } _ => { - self.end(); // Close the ibox for the pattern. + self.end(ib); self.print_expr(body, FixupContext::new_match_arm()); self.word(","); } } } else { + self.end(ib); self.word(","); } - self.end(); // Close enclosing cbox. + self.end(cb); } fn print_closure_binder(&mut self, binder: &ast::ClosureBinder) { diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index 653bd77cc4d..1e02ac8fd5d 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -5,6 +5,7 @@ use rustc_ast::ModKind; use rustc_ast::ptr::P; use rustc_span::Ident; +use crate::pp::BoxMarker; use crate::pp::Breaks::Inconsistent; use crate::pprust::state::fixup::FixupContext; use crate::pprust::state::{AnnNode, INDENT_UNIT, PrintState, State}; @@ -96,7 +97,7 @@ impl<'a> State<'a> { define_opaque: Option<&[(ast::NodeId, ast::Path)]>, ) { self.print_define_opaques(define_opaque); - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_safety(safety); self.print_defaultness(defaultness); @@ -113,14 +114,14 @@ impl<'a> State<'a> { if body.is_some() { self.space(); } - self.end(); // end the head-ibox + self.end(ib); if let Some(body) = body { self.word_space("="); self.print_expr(body, FixupContext::default()); } self.print_where_clause(&generics.where_clause); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } fn print_associated_type( @@ -135,7 +136,7 @@ impl<'a> State<'a> { ) { let (before_predicates, after_predicates) = generics.where_clause.predicates.split_at(where_clauses.split); - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(vis); self.print_defaultness(defaultness); self.word_space("type"); @@ -153,8 +154,8 @@ impl<'a> State<'a> { } self.print_where_clause_parts(where_clauses.after.has_where_token, after_predicates); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } /// Pretty-prints an item. @@ -165,7 +166,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Item(item)); match &item.kind { ast::ItemKind::ExternCrate(orig_name, ident) => { - self.head(visibility_qualified(&item.vis, "extern crate")); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "extern crate")); if let &Some(orig_name) = orig_name { self.print_name(orig_name); self.space(); @@ -174,8 +175,8 @@ impl<'a> State<'a> { } self.print_ident(*ident); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } ast::ItemKind::Use(tree) => { self.print_visibility(&item.vis); @@ -228,7 +229,7 @@ impl<'a> State<'a> { self.print_fn_full(&item.vis, &item.attrs, &*func); } ast::ItemKind::Mod(safety, ident, mod_kind) => { - self.head(Self::to_string(|s| { + let (cb, ib) = self.head(Self::to_string(|s| { s.print_visibility(&item.vis); s.print_safety(*safety); s.word("mod"); @@ -238,23 +239,23 @@ impl<'a> State<'a> { match mod_kind { ModKind::Loaded(items, ..) => { self.nbsp(); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for item in items { self.print_item(item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ModKind::Unloaded => { self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } } } ast::ItemKind::ForeignMod(nmod) => { - self.head(Self::to_string(|s| { + let (cb, ib) = self.head(Self::to_string(|s| { s.print_safety(nmod.safety); s.word("extern"); })); @@ -262,18 +263,18 @@ impl<'a> State<'a> { self.print_token_literal(abi.as_token_lit(), abi.span); self.nbsp(); } - self.bopen(); + self.bopen(ib); self.print_foreign_mod(nmod, &item.attrs); let empty = item.attrs.is_empty() && nmod.items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ast::ItemKind::GlobalAsm(asm) => { // FIXME: Print `builtin # global_asm` once macro `global_asm` uses `builtin_syntax`. - self.head(visibility_qualified(&item.vis, "global_asm!")); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "global_asm!")); self.print_inline_asm(asm); self.word(";"); - self.end(); - self.end(); + self.end(ib); + self.end(cb); } ast::ItemKind::TyAlias(box ast::TyAlias { defaultness, @@ -297,12 +298,12 @@ impl<'a> State<'a> { self.print_enum_def(enum_definition, params, *ident, item.span, &item.vis); } ast::ItemKind::Struct(ident, struct_def, generics) => { - self.head(visibility_qualified(&item.vis, "struct")); - self.print_struct(struct_def, generics, *ident, item.span, true); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "struct")); + self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } ast::ItemKind::Union(ident, struct_def, generics) => { - self.head(visibility_qualified(&item.vis, "union")); - self.print_struct(struct_def, generics, *ident, item.span, true); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "union")); + self.print_struct(struct_def, generics, *ident, item.span, true, cb, ib); } ast::ItemKind::Impl(box ast::Impl { safety, @@ -314,7 +315,7 @@ impl<'a> State<'a> { self_ty, items, }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&item.vis); self.print_defaultness(*defaultness); self.print_safety(*safety); @@ -343,13 +344,13 @@ impl<'a> State<'a> { self.print_where_clause(&generics.where_clause); self.space(); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for impl_item in items { self.print_assoc_item(impl_item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ast::ItemKind::Trait(box ast::Trait { safety, @@ -359,7 +360,7 @@ impl<'a> State<'a> { bounds, items, }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&item.vis); self.print_safety(*safety); self.print_is_auto(*is_auto); @@ -372,16 +373,16 @@ impl<'a> State<'a> { } self.print_where_clause(&generics.where_clause); self.word(" "); - self.bopen(); + self.bopen(ib); self.print_inner_attributes(&item.attrs); for trait_item in items { self.print_assoc_item(trait_item); } let empty = item.attrs.is_empty() && items.is_empty(); - self.bclose(item.span, empty); + self.bclose(item.span, empty, cb); } ast::ItemKind::TraitAlias(ident, generics, bounds) => { - self.head(visibility_qualified(&item.vis, "trait")); + let (cb, ib) = self.head(visibility_qualified(&item.vis, "trait")); self.print_ident(*ident); self.print_generic_params(&generics.params); self.nbsp(); @@ -391,8 +392,8 @@ impl<'a> State<'a> { } self.print_where_clause(&generics.where_clause); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } ast::ItemKind::MacCall(mac) => { self.print_mac(mac); @@ -433,28 +434,24 @@ impl<'a> State<'a> { span: rustc_span::Span, visibility: &ast::Visibility, ) { - self.head(visibility_qualified(visibility, "enum")); + let (cb, ib) = self.head(visibility_qualified(visibility, "enum")); self.print_ident(ident); self.print_generic_params(&generics.params); self.print_where_clause(&generics.where_clause); self.space(); - self.print_variants(&enum_definition.variants, span) - } - - fn print_variants(&mut self, variants: &[ast::Variant], span: rustc_span::Span) { - self.bopen(); - for v in variants { + self.bopen(ib); + for v in enum_definition.variants.iter() { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); self.print_outer_attributes(&v.attrs); - self.ibox(0); + let ib = self.ibox(0); self.print_variant(v); self.word(","); - self.end(); + self.end(ib); self.maybe_print_trailing_comment(v.span, None); } - let empty = variants.is_empty(); - self.bclose(span, empty) + let empty = enum_definition.variants.is_empty(); + self.bclose(span, empty, cb) } pub(crate) fn print_visibility(&mut self, vis: &ast::Visibility) { @@ -478,33 +475,6 @@ impl<'a> State<'a> { } } - pub(crate) fn print_record_struct_body( - &mut self, - fields: &[ast::FieldDef], - span: rustc_span::Span, - ) { - self.nbsp(); - self.bopen(); - - let empty = fields.is_empty(); - if !empty { - self.hardbreak_if_not_bol(); - - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_outer_attributes(&field.attrs); - self.print_visibility(&field.vis); - self.print_ident(field.ident.unwrap()); - self.word_nbsp(":"); - self.print_type(&field.ty); - self.word(","); - } - } - - self.bclose(span, empty); - } - fn print_struct( &mut self, struct_def: &ast::VariantData, @@ -512,6 +482,8 @@ impl<'a> State<'a> { ident: Ident, span: rustc_span::Span, print_finalizer: bool, + cb: BoxMarker, + ib: BoxMarker, ) { self.print_ident(ident); self.print_generic_params(&generics.params); @@ -531,21 +503,40 @@ impl<'a> State<'a> { if print_finalizer { self.word(";"); } - self.end(); - self.end(); // Close the outer-box. + self.end(ib); + self.end(cb); } ast::VariantData::Struct { fields, .. } => { self.print_where_clause(&generics.where_clause); - self.print_record_struct_body(fields, span); + self.nbsp(); + self.bopen(ib); + + let empty = fields.is_empty(); + if !empty { + self.hardbreak_if_not_bol(); + + for field in fields { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_outer_attributes(&field.attrs); + self.print_visibility(&field.vis); + self.print_ident(field.ident.unwrap()); + self.word_nbsp(":"); + self.print_type(&field.ty); + self.word(","); + } + } + + self.bclose(span, empty, cb); } } } pub(crate) fn print_variant(&mut self, v: &ast::Variant) { - self.head(""); + let (cb, ib) = self.head(""); self.print_visibility(&v.vis); let generics = ast::Generics::default(); - self.print_struct(&v.data, &generics, v.ident, v.span, false); + self.print_struct(&v.data, &generics, v.ident, v.span, false, cb, ib); if let Some(d) = &v.disr_expr { self.space(); self.word_space("="); @@ -636,9 +627,7 @@ impl<'a> State<'a> { kind: DelegationKind<'_>, body: &Option<P<ast::Block>>, ) { - if body.is_some() { - self.head(""); - } + let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); self.print_visibility(vis); self.word_nbsp("reuse"); @@ -670,9 +659,9 @@ impl<'a> State<'a> { self.word("*"); } } - if let Some(body) = body { + if let Some((body, (cb, ib))) = body_cb_ib { self.nbsp(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } else { self.word(";"); } @@ -683,9 +672,8 @@ impl<'a> State<'a> { self.print_define_opaques(define_opaque.as_deref()); - if body.is_some() { - self.head(""); - } + let body_cb_ib = body.as_ref().map(|body| (body, self.head(""))); + self.print_visibility(vis); self.print_defaultness(*defaultness); self.print_fn(&sig.decl, sig.header, Some(*ident), generics); @@ -693,9 +681,9 @@ impl<'a> State<'a> { self.nbsp(); self.print_contract(contract); } - if let Some(body) = body { + if let Some((body, (cb, ib))) = body_cb_ib { self.nbsp(); - self.print_block_with_attrs(body, attrs); + self.print_block_with_attrs(body, attrs, cb, ib); } else { self.word(";"); } @@ -851,10 +839,10 @@ impl<'a> State<'a> { } else if let [(item, _)] = items.as_slice() { self.print_use_tree(item); } else { - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.word("{"); self.zerobreak(); - self.ibox(0); + let ib = self.ibox(0); for (pos, use_tree) in items.iter().with_position() { let is_last = matches!(pos, Position::Last | Position::Only); self.print_use_tree(&use_tree.0); @@ -867,11 +855,11 @@ impl<'a> State<'a> { } } } - self.end(); + self.end(ib); self.trailing_comma(); self.offset(-INDENT_UNIT); self.word("}"); - self.end(); + self.end(cb); } } } diff --git a/compiler/rustc_ast_pretty/src/pprust/tests.rs b/compiler/rustc_ast_pretty/src/pprust/tests.rs index bc7f22766a5..786de529c5b 100644 --- a/compiler/rustc_ast_pretty/src/pprust/tests.rs +++ b/compiler/rustc_ast_pretty/src/pprust/tests.rs @@ -11,10 +11,10 @@ fn fun_to_string( generics: &ast::Generics, ) -> String { to_string(|s| { - s.head(""); + let (cb, ib) = s.head(""); s.print_fn(decl, header, Some(ident), generics); - s.end(); // Close the head box. - s.end(); // Close the outer box. + s.end(ib); + s.end(cb); }) } diff --git a/compiler/rustc_borrowck/src/lib.rs b/compiler/rustc_borrowck/src/lib.rs index 51d37353520..44af1b76539 100644 --- a/compiler/rustc_borrowck/src/lib.rs +++ b/compiler/rustc_borrowck/src/lib.rs @@ -795,7 +795,14 @@ impl<'a, 'tcx> ResultsVisitor<'tcx, Borrowck<'a, 'tcx>> for MirBorrowckCtxt<'a, TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(loc, (discr, span), state); } - TerminatorKind::Drop { place, target: _, unwind: _, replace } => { + TerminatorKind::Drop { + place, + target: _, + unwind: _, + replace, + drop: _, + async_fut: _, + } => { debug!( "visit_terminator_drop \ loc: {:?} term: {:?} place: {:?} span: {:?}", diff --git a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs index 0d1d8642bca..99dd0b2dd46 100644 --- a/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs +++ b/compiler/rustc_borrowck/src/polonius/legacy/loan_invalidations.rs @@ -101,7 +101,14 @@ impl<'a, 'tcx> Visitor<'tcx> for LoanInvalidationsGenerator<'a, 'tcx> { TerminatorKind::SwitchInt { discr, targets: _ } => { self.consume_operand(location, discr); } - TerminatorKind::Drop { place: drop_place, target: _, unwind: _, replace } => { + TerminatorKind::Drop { + place: drop_place, + target: _, + unwind: _, + replace, + drop: _, + async_fut: _, + } => { let write_kind = if *replace { WriteKind::Replace } else { WriteKind::StorageDeadOrDrop }; self.access_place( diff --git a/compiler/rustc_borrowck/src/type_check/mod.rs b/compiler/rustc_borrowck/src/type_check/mod.rs index 3c00b819a96..99a544279b9 100644 --- a/compiler/rustc_borrowck/src/type_check/mod.rs +++ b/compiler/rustc_borrowck/src/type_check/mod.rs @@ -2079,8 +2079,14 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> { } } TerminatorKind::Unreachable => {} - TerminatorKind::Drop { target, unwind, .. } - | TerminatorKind::Assert { target, unwind, .. } => { + TerminatorKind::Drop { target, unwind, drop, .. } => { + self.assert_iscleanup(block_data, target, is_cleanup); + self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); + if let Some(drop) = drop { + self.assert_iscleanup(block_data, drop, is_cleanup); + } + } + TerminatorKind::Assert { target, unwind, .. } => { self.assert_iscleanup(block_data, target, is_cleanup); self.assert_iscleanup_unwind(block_data, unwind, is_cleanup); } diff --git a/compiler/rustc_codegen_cranelift/src/abi/mod.rs b/compiler/rustc_codegen_cranelift/src/abi/mod.rs index ddd119e0c61..5f631405a9a 100644 --- a/compiler/rustc_codegen_cranelift/src/abi/mod.rs +++ b/compiler/rustc_codegen_cranelift/src/abi/mod.rs @@ -441,7 +441,9 @@ pub(crate) fn codegen_terminator_call<'tcx>( Err(instance) => Some(instance), } } - InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + InstanceKind::DropGlue(_, None) => { // empty drop glue - a nop. let dest = target.expect("Non terminating drop_in_place_real???"); let ret_block = fx.get_block(dest); @@ -707,9 +709,8 @@ pub(crate) fn codegen_drop<'tcx>( let ty = drop_place.layout().ty; let drop_instance = Instance::resolve_drop_in_place(fx.tcx, ty); - if let ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) = - drop_instance.def - { + // AsyncDropGlueCtorShim can't be here + if let ty::InstanceKind::DropGlue(_, None) = drop_instance.def { // we don't actually need to drop anything } else { match ty.kind() { diff --git a/compiler/rustc_codegen_cranelift/src/base.rs b/compiler/rustc_codegen_cranelift/src/base.rs index adaa754491e..e9c7186b03c 100644 --- a/compiler/rustc_codegen_cranelift/src/base.rs +++ b/compiler/rustc_codegen_cranelift/src/base.rs @@ -565,7 +565,11 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) { | TerminatorKind::CoroutineDrop => { bug!("shouldn't exist at codegen {:?}", bb_data.terminator()); } - TerminatorKind::Drop { place, target, unwind: _, replace: _ } => { + TerminatorKind::Drop { place, target, unwind: _, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync before codegen" + ); let drop_place = codegen_place(fx, *place); crate::abi::codegen_drop(fx, source_info, drop_place, *target); } diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs index 07075be55fa..e9574108696 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs @@ -721,8 +721,7 @@ fn build_union_fields_for_direct_tag_coroutine<'ll, 'tcx>( _ => unreachable!(), }; - let coroutine_layout = - cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.kind_ty()).unwrap(); + let coroutine_layout = cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args.args).unwrap(); let common_upvar_names = cx.tcx.closure_saved_names_of_captured_variables(coroutine_def_id); let variant_range = coroutine_args.variant_range(coroutine_def_id, cx.tcx); diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs index bfd131cfd3d..20a841f2287 100644 --- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs +++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/native.rs @@ -174,10 +174,8 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>( DIFlags::FlagZero, ), |cx, coroutine_type_di_node| { - let coroutine_layout = cx - .tcx - .coroutine_layout(coroutine_def_id, coroutine_args.as_coroutine().kind_ty()) - .unwrap(); + let coroutine_layout = + cx.tcx.coroutine_layout(coroutine_def_id, coroutine_args).unwrap(); let Variants::Multiple { tag_encoding: TagEncoding::Direct, ref variants, .. } = coroutine_type_and_layout.variants diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs index 34c84c64070..1e1bdfb5977 100644 --- a/compiler/rustc_codegen_ssa/src/back/archive.rs +++ b/compiler/rustc_codegen_ssa/src/back/archive.rs @@ -13,9 +13,9 @@ use object::read::archive::ArchiveFile; use object::read::macho::FatArch; use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; +use rustc_fs_util::TempDirBuilder; use rustc_session::Session; use rustc_span::Symbol; -use tempfile::Builder as TempFileBuilder; use tracing::trace; use super::metadata::search_for_section; @@ -501,7 +501,7 @@ impl<'a> ArArchiveBuilder<'a> { // it creates. We need it to be the default mode for back compat reasons however. (See // #107495) To handle this we are telling tempfile to create a temporary directory instead // and then inside this directory create a file using File::create. - let archive_tmpdir = TempFileBuilder::new() + let archive_tmpdir = TempDirBuilder::new() .suffix(".temp-archive") .tempdir_in(output.parent().unwrap_or_else(|| Path::new(""))) .map_err(|err| { diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs index f0c47ac41e8..323538969d7 100644 --- a/compiler/rustc_codegen_ssa/src/back/link.rs +++ b/compiler/rustc_codegen_ssa/src/back/link.rs @@ -18,7 +18,7 @@ use rustc_data_structures::fx::FxIndexSet; use rustc_data_structures::memmap::Mmap; use rustc_data_structures::temp_dir::MaybeTempDir; use rustc_errors::{DiagCtxtHandle, LintDiagnostic}; -use rustc_fs_util::{fix_windows_verbatim_for_gcc, try_canonicalize}; +use rustc_fs_util::{TempDirBuilder, fix_windows_verbatim_for_gcc, try_canonicalize}; use rustc_hir::def_id::{CrateNum, LOCAL_CRATE}; use rustc_macros::LintDiagnostic; use rustc_metadata::fs::{METADATA_FILENAME, copy_to_stdout, emit_wrapper_file}; @@ -48,7 +48,6 @@ use rustc_target::spec::{ LinkerFeatures, LinkerFlavor, LinkerFlavorCli, Lld, PanicStrategy, RelocModel, RelroLevel, SanitizerSet, SplitDebuginfo, }; -use tempfile::Builder as TempFileBuilder; use tracing::{debug, info, warn}; use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder}; @@ -100,7 +99,7 @@ pub fn link_binary( }); if outputs.outputs.should_link() { - let tmpdir = TempFileBuilder::new() + let tmpdir = TempDirBuilder::new() .prefix("rustc") .tempdir() .unwrap_or_else(|error| sess.dcx().emit_fatal(errors::CreateTempDir { error })); diff --git a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs index fd06c50eb81..2bfab1f43bc 100644 --- a/compiler/rustc_codegen_ssa/src/back/symbol_export.rs +++ b/compiler/rustc_codegen_ssa/src/back/symbol_export.rs @@ -374,7 +374,7 @@ fn exported_symbols_provider_local( )); } MonoItem::Fn(Instance { - def: InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)), + def: InstanceKind::AsyncDropGlueCtorShim(_, ty), args, }) => { // A little sanity-check @@ -388,6 +388,16 @@ fn exported_symbols_provider_local( }, )); } + MonoItem::Fn(Instance { def: InstanceKind::AsyncDropGlue(def, ty), args: _ }) => { + symbols.push(( + ExportedSymbol::AsyncDropGlue(def, ty), + SymbolExportInfo { + level: SymbolExportLevel::Rust, + kind: SymbolExportKind::Text, + used: false, + }, + )); + } _ => { // Any other symbols don't qualify for sharing } @@ -429,11 +439,10 @@ fn upstream_monomorphizations_provider( if let Some(async_drop_in_place_fn_def_id) = async_drop_in_place_fn_def_id { (async_drop_in_place_fn_def_id, tcx.mk_args(&[ty.into()])) } else { - // `drop_in_place` in place does not exist, don't try - // to use it. continue; } } + ExportedSymbol::AsyncDropGlue(def_id, ty) => (def_id, tcx.mk_args(&[ty.into()])), ExportedSymbol::NonGeneric(..) | ExportedSymbol::ThreadLocalShim(..) | ExportedSymbol::NoDefId(..) => { @@ -582,6 +591,13 @@ pub(crate) fn symbol_name_for_instance_in_crate<'tcx>( instantiating_crate, ) } + ExportedSymbol::AsyncDropGlue(def_id, ty) => { + rustc_symbol_mangling::symbol_name_for_instance_in_crate( + tcx, + Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty), + instantiating_crate, + ) + } ExportedSymbol::NoDefId(symbol_name) => symbol_name.to_string(), } } @@ -604,6 +620,7 @@ fn calling_convention_for_symbol<'tcx>( // AsyncDropGlueCtorShim always use the Rust calling convention and thus follow the // target's default symbol decoration scheme. ExportedSymbol::AsyncDropGlueCtorShim(..) => None, + ExportedSymbol::AsyncDropGlue(..) => None, // NoDefId always follow the target's default symbol decoration scheme. ExportedSymbol::NoDefId(..) => None, // ThreadLocalShim always follow the target's default symbol decoration scheme. diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index d184ce3d61d..950f19a6f0f 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -926,10 +926,9 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let def = instance.map(|i| i.def); - if let Some( - ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None), - ) = def - { + // We don't need AsyncDropGlueCtorShim here because it is not `noop func`, + // it is `func returning noop future` + if let Some(ty::InstanceKind::DropGlue(_, None)) = def { // Empty drop glue; a no-op. let target = target.unwrap(); return helper.funclet_br(self, bx, target, mergeable_succ); @@ -1386,8 +1385,12 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MergingSucc::False } - mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => self - .codegen_drop_terminator( + mir::TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync before codegen" + ); + self.codegen_drop_terminator( helper, bx, &terminator.source_info, @@ -1395,7 +1398,8 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { target, unwind, mergeable_succ(), - ), + ) + } mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, unwind } => self .codegen_assert_terminator( diff --git a/compiler/rustc_const_eval/src/const_eval/machine.rs b/compiler/rustc_const_eval/src/const_eval/machine.rs index 61a7ec13511..7c7daed525b 100644 --- a/compiler/rustc_const_eval/src/const_eval/machine.rs +++ b/compiler/rustc_const_eval/src/const_eval/machine.rs @@ -502,6 +502,7 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> { RemainderByZero(op) => RemainderByZero(eval_to_int(op)?), ResumedAfterReturn(coroutine_kind) => ResumedAfterReturn(*coroutine_kind), ResumedAfterPanic(coroutine_kind) => ResumedAfterPanic(*coroutine_kind), + ResumedAfterDrop(coroutine_kind) => ResumedAfterDrop(*coroutine_kind), MisalignedPointerDereference { required, found } => MisalignedPointerDereference { required: eval_to_int(required)?, found: eval_to_int(found)?, diff --git a/compiler/rustc_const_eval/src/interpret/call.rs b/compiler/rustc_const_eval/src/interpret/call.rs index 29f819cca1f..216800717fd 100644 --- a/compiler/rustc_const_eval/src/interpret/call.rs +++ b/compiler/rustc_const_eval/src/interpret/call.rs @@ -570,6 +570,8 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { | ty::InstanceKind::FnPtrAddrShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) + | ty::InstanceKind::AsyncDropGlue(..) + | ty::InstanceKind::FutureDropPollShim(..) | ty::InstanceKind::Item(_) => { // We need MIR for this fn. // Note that this can be an intrinsic, if we are executing its fallback body. diff --git a/compiler/rustc_const_eval/src/interpret/step.rs b/compiler/rustc_const_eval/src/interpret/step.rs index ddf2d65914f..363ceee1970 100644 --- a/compiler/rustc_const_eval/src/interpret/step.rs +++ b/compiler/rustc_const_eval/src/interpret/step.rs @@ -539,7 +539,11 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> { } } - Drop { place, target, unwind, replace: _ } => { + Drop { place, target, unwind, replace: _, drop, async_fut } => { + assert!( + async_fut.is_none() && drop.is_none(), + "Async Drop must be expanded or reset to sync in runtime MIR" + ); let place = self.eval_place(place)?; let instance = Instance::resolve_drop_in_place(*self.tcx, place.layout.ty); if let ty::InstanceKind::DropGlue(_, None) = instance.def { diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 12e254ab549..75e09cacb1f 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -383,6 +383,8 @@ declare_features! ( (unstable, associated_const_equality, "1.58.0", Some(92827)), /// Allows associated type defaults. (unstable, associated_type_defaults, "1.2.0", Some(29661)), + /// Allows implementing `AsyncDrop`. + (incomplete, async_drop, "CURRENT_RUSTC_VERSION", Some(126482)), /// Allows async functions to be called from `dyn Trait`. (incomplete, async_fn_in_dyn_trait, "1.85.0", Some(133119)), /// Allows `#[track_caller]` on async functions. diff --git a/compiler/rustc_fs_util/Cargo.toml b/compiler/rustc_fs_util/Cargo.toml index baca3bc7d49..90a6acade8b 100644 --- a/compiler/rustc_fs_util/Cargo.toml +++ b/compiler/rustc_fs_util/Cargo.toml @@ -5,4 +5,5 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +tempfile = "3.7.1" # tidy-alphabetical-end diff --git a/compiler/rustc_fs_util/src/lib.rs b/compiler/rustc_fs_util/src/lib.rs index 0df1b243d69..7a883a13b72 100644 --- a/compiler/rustc_fs_util/src/lib.rs +++ b/compiler/rustc_fs_util/src/lib.rs @@ -1,6 +1,8 @@ -use std::ffi::CString; +use std::ffi::{CString, OsStr}; use std::path::{Path, PathBuf, absolute}; -use std::{fs, io}; +use std::{env, fs, io}; + +use tempfile::TempDir; // Unfortunately, on windows, it looks like msvcrt.dll is silently translating // verbatim paths under the hood to non-verbatim paths! This manifests itself as @@ -102,3 +104,43 @@ pub fn path_to_c_string(p: &Path) -> CString { pub fn try_canonicalize<P: AsRef<Path>>(path: P) -> io::Result<PathBuf> { fs::canonicalize(&path).or_else(|_| absolute(&path)) } + +pub struct TempDirBuilder<'a, 'b> { + builder: tempfile::Builder<'a, 'b>, +} + +impl<'a, 'b> TempDirBuilder<'a, 'b> { + pub fn new() -> Self { + Self { builder: tempfile::Builder::new() } + } + + pub fn prefix<S: AsRef<OsStr> + ?Sized>(&mut self, prefix: &'a S) -> &mut Self { + self.builder.prefix(prefix); + self + } + + pub fn suffix<S: AsRef<OsStr> + ?Sized>(&mut self, suffix: &'b S) -> &mut Self { + self.builder.suffix(suffix); + self + } + + pub fn tempdir_in<P: AsRef<Path>>(&self, dir: P) -> io::Result<TempDir> { + let dir = dir.as_ref(); + // On Windows in CI, we had been getting fairly frequent "Access is denied" + // errors when creating temporary directories. + // So this implements a simple retry with backoff loop. + #[cfg(windows)] + for wait in 1..11 { + match self.builder.tempdir_in(dir) { + Err(e) if e.kind() == io::ErrorKind::PermissionDenied => {} + t => return t, + } + std::thread::sleep(std::time::Duration::from_millis(1 << wait)); + } + self.builder.tempdir_in(dir) + } + + pub fn tempdir(&self) -> io::Result<TempDir> { + self.tempdir_in(env::temp_dir()) + } +} diff --git a/compiler/rustc_hir/src/lang_items.rs b/compiler/rustc_hir/src/lang_items.rs index b9f4a8cd165..21d36ed54cd 100644 --- a/compiler/rustc_hir/src/lang_items.rs +++ b/compiler/rustc_hir/src/lang_items.rs @@ -189,19 +189,8 @@ language_item_table! { Drop, sym::drop, drop_trait, Target::Trait, GenericRequirement::None; Destruct, sym::destruct, destruct_trait, Target::Trait, GenericRequirement::None; - - AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::Exact(0); - AsyncDestruct, sym::async_destruct, async_destruct_trait, Target::Trait, GenericRequirement::Exact(0); + AsyncDrop, sym::async_drop, async_drop_trait, Target::Trait, GenericRequirement::None; AsyncDropInPlace, sym::async_drop_in_place, async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - SurfaceAsyncDropInPlace, sym::surface_async_drop_in_place, surface_async_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropSurfaceDropInPlace, sym::async_drop_surface_drop_in_place, async_drop_surface_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropSlice, sym::async_drop_slice, async_drop_slice_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropChain, sym::async_drop_chain, async_drop_chain_fn, Target::Fn, GenericRequirement::Exact(2); - AsyncDropNoop, sym::async_drop_noop, async_drop_noop_fn, Target::Fn, GenericRequirement::Exact(0); - AsyncDropDeferredDropInPlace, sym::async_drop_deferred_drop_in_place, async_drop_deferred_drop_in_place_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropFuse, sym::async_drop_fuse, async_drop_fuse_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropDefer, sym::async_drop_defer, async_drop_defer_fn, Target::Fn, GenericRequirement::Exact(1); - AsyncDropEither, sym::async_drop_either, async_drop_either_fn, Target::Fn, GenericRequirement::Exact(3); CoerceUnsized, sym::coerce_unsized, coerce_unsized_trait, Target::Trait, GenericRequirement::Minimum(1); DispatchFromDyn, sym::dispatch_from_dyn, dispatch_from_dyn_trait, Target::Trait, GenericRequirement::Minimum(1); @@ -321,6 +310,10 @@ language_item_table! { PanicAsyncGenFnResumedPanic, sym::panic_const_async_gen_fn_resumed_panic, panic_const_async_gen_fn_resumed_panic, Target::Fn, GenericRequirement::None; PanicGenFnNonePanic, sym::panic_const_gen_fn_none_panic, panic_const_gen_fn_none_panic, Target::Fn, GenericRequirement::None; PanicNullPointerDereference, sym::panic_null_pointer_dereference, panic_null_pointer_dereference, Target::Fn, GenericRequirement::None; + PanicCoroutineResumedDrop, sym::panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_drop, Target::Fn, GenericRequirement::None; + PanicAsyncFnResumedDrop, sym::panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_drop, Target::Fn, GenericRequirement::None; + PanicAsyncGenFnResumedDrop, sym::panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_drop, Target::Fn, GenericRequirement::None; + PanicGenFnNoneDrop, sym::panic_const_gen_fn_none_drop, panic_const_gen_fn_none_drop, Target::Fn, GenericRequirement::None; /// libstd panic entry point. Necessary for const eval to be able to catch it BeginPanic, sym::begin_panic, begin_panic_fn, Target::Fn, GenericRequirement::None; @@ -333,7 +326,6 @@ language_item_table! { ExchangeMalloc, sym::exchange_malloc, exchange_malloc_fn, Target::Fn, GenericRequirement::None; DropInPlace, sym::drop_in_place, drop_in_place_fn, Target::Fn, GenericRequirement::Minimum(1); - FallbackSurfaceDrop, sym::fallback_surface_drop, fallback_surface_drop_fn, Target::Fn, GenericRequirement::None; AllocLayout, sym::alloc_layout, alloc_layout, Target::Struct, GenericRequirement::None; /// For all binary crates without `#![no_main]`, Rust will generate a "main" function. diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml index e5017794d8f..58213c4f4e4 100644 --- a/compiler/rustc_hir_analysis/Cargo.toml +++ b/compiler/rustc_hir_analysis/Cargo.toml @@ -26,7 +26,6 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } -rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" diff --git a/compiler/rustc_hir_analysis/messages.ftl b/compiler/rustc_hir_analysis/messages.ftl index 92701e3328e..277bb7bd3e1 100644 --- a/compiler/rustc_hir_analysis/messages.ftl +++ b/compiler/rustc_hir_analysis/messages.ftl @@ -450,9 +450,6 @@ hir_analysis_recursive_generic_parameter = {$param_def_kind} `{$param_name}` is hir_analysis_redundant_lifetime_args = unnecessary lifetime parameter `{$victim}` .note = you can use the `{$candidate}` lifetime directly, in place of `{$victim}` -hir_analysis_register_type_unstable = - type `{$ty}` cannot be used with this register class in stable - hir_analysis_requires_note = the `{$trait_name}` impl for `{$ty}` requires that `{$error_predicate}` hir_analysis_return_type_notation_equality_bound = diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs index 4f338c6a19e..f92b2aea160 100644 --- a/compiler/rustc_hir_analysis/src/check/check.rs +++ b/compiler/rustc_hir_analysis/src/check/check.rs @@ -730,7 +730,6 @@ pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) { .is_ok() { check_impl_items_against_trait(tcx, def_id, impl_trait_header); - check_on_unimplemented(tcx, def_id); } } } diff --git a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs index 06a7b1f6c29..bbf36fef1dd 100644 --- a/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs +++ b/compiler/rustc_hir_analysis/src/check/compare_impl_item.rs @@ -379,13 +379,13 @@ fn compare_method_predicate_entailment<'tcx>( // Annoyingly, asking for the WF predicates of an array (with an unevaluated const (only?)) // will give back the well-formed predicate of the same array. let mut wf_args_seen: FxHashSet<_> = wf_args.iter().copied().collect(); - while let Some(arg) = wf_args.pop() { + while let Some(term) = wf_args.pop() { let Some(obligations) = rustc_trait_selection::traits::wf::obligations( infcx, param_env, impl_m_def_id, 0, - arg, + term, impl_m_span, ) else { continue; @@ -402,9 +402,9 @@ fn compare_method_predicate_entailment<'tcx>( | ty::ClauseKind::TypeOutlives(..) | ty::ClauseKind::Projection(..), ) => ocx.register_obligation(obligation), - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - if wf_args_seen.insert(arg) { - wf_args.push(arg) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + if wf_args_seen.insert(term) { + wf_args.push(term) } } _ => {} diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs index 5fbd771976b..fad8abf5fae 100644 --- a/compiler/rustc_hir_analysis/src/check/mod.rs +++ b/compiler/rustc_hir_analysis/src/check/mod.rs @@ -67,7 +67,6 @@ mod check; mod compare_impl_item; mod entry; pub mod intrinsic; -pub mod intrinsicck; mod region; pub mod wfcheck; diff --git a/compiler/rustc_hir_analysis/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs index 425edc0e773..fa36fe79716 100644 --- a/compiler/rustc_hir_analysis/src/check/wfcheck.rs +++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs @@ -76,12 +76,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { ) } - fn register_wf_obligation( - &self, - span: Span, - loc: Option<WellFormedLoc>, - arg: ty::GenericArg<'tcx>, - ) { + fn register_wf_obligation(&self, span: Span, loc: Option<WellFormedLoc>, term: ty::Term<'tcx>) { let cause = traits::ObligationCause::new( span, self.body_def_id, @@ -91,7 +86,7 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> { self.tcx(), cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } } @@ -1486,8 +1481,41 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id tcx.def_span(param.def_id), matches!(param.kind, GenericParamDefKind::Type { .. }) .then(|| WellFormedLoc::Ty(param.def_id.expect_local())), - default, + default.as_term().unwrap(), ); + } else { + // If we've got a generic const parameter we still want to check its + // type is correct in case both it and the param type are fully concrete. + let GenericArgKind::Const(ct) = default.unpack() else { + continue; + }; + + let ct_ty = match ct.kind() { + ty::ConstKind::Infer(_) + | ty::ConstKind::Placeholder(_) + | ty::ConstKind::Bound(_, _) => unreachable!(), + ty::ConstKind::Error(_) | ty::ConstKind::Expr(_) => continue, + ty::ConstKind::Value(cv) => cv.ty, + ty::ConstKind::Unevaluated(uv) => { + infcx.tcx.type_of(uv.def).instantiate(infcx.tcx, uv.args) + } + ty::ConstKind::Param(param_ct) => param_ct.find_ty_from_env(wfcx.param_env), + }; + + let param_ty = tcx.type_of(param.def_id).instantiate_identity(); + if !ct_ty.has_param() && !param_ty.has_param() { + let cause = traits::ObligationCause::new( + tcx.def_span(param.def_id), + wfcx.body_def_id, + ObligationCauseCode::WellFormed(None), + ); + wfcx.register_obligation(Obligation::new( + tcx, + cause, + wfcx.param_env, + ty::ClauseKind::ConstArgHasType(ct, param_ty), + )); + } } } } diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs index 508970cf255..2b1661aaac8 100644 --- a/compiler/rustc_hir_analysis/src/errors.rs +++ b/compiler/rustc_hir_analysis/src/errors.rs @@ -1675,14 +1675,6 @@ pub(crate) struct CmseEntryGeneric { pub span: Span, } -#[derive(Diagnostic)] -#[diag(hir_analysis_register_type_unstable)] -pub(crate) struct RegisterTypeUnstable<'a> { - #[primary_span] - pub span: Span, - pub ty: Ty<'a>, -} - #[derive(LintDiagnostic)] #[diag(hir_analysis_supertrait_item_shadowing)] pub(crate) struct SupertraitItemShadowing { diff --git a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs index af1107b499f..309221f9a12 100644 --- a/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs +++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs @@ -374,9 +374,12 @@ fn check_predicates<'tcx>( // Include the well-formed predicates of the type parameters of the impl. for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().instantiate_identity().args { + let Some(term) = arg.as_term() else { + continue; + }; let infcx = &tcx.infer_ctxt().build(TypingMode::non_body_analysis()); let obligations = - wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, arg, span) + wf::obligations(infcx, tcx.param_env(impl1_def_id), impl1_def_id, 0, term, span) .unwrap(); assert!(!obligations.has_infer()); diff --git a/compiler/rustc_hir_pretty/src/lib.rs b/compiler/rustc_hir_pretty/src/lib.rs index 779fae80f19..b878147522d 100644 --- a/compiler/rustc_hir_pretty/src/lib.rs +++ b/compiler/rustc_hir_pretty/src/lib.rs @@ -13,7 +13,7 @@ use rustc_abi::ExternAbi; use rustc_ast::util::parser::{self, ExprPrecedence, Fixity}; use rustc_ast::{AttrStyle, DUMMY_NODE_ID, DelimArgs}; use rustc_ast_pretty::pp::Breaks::{Consistent, Inconsistent}; -use rustc_ast_pretty::pp::{self, Breaks}; +use rustc_ast_pretty::pp::{self, BoxMarker, Breaks}; use rustc_ast_pretty::pprust::state::MacHeader; use rustc_ast_pretty::pprust::{Comments, PrintState}; use rustc_attr_data_structures::{AttributeKind, PrintAttribute}; @@ -127,7 +127,7 @@ impl<'a> State<'a> { } fn print_attr_item(&mut self, item: &hir::AttrItem, span: Span) { - self.ibox(0); + let ib = self.ibox(0); let path = ast::Path { span, segments: item @@ -161,7 +161,7 @@ impl<'a> State<'a> { self.word(token_str); } } - self.end(); + self.end(ib); } fn print_node(&mut self, node: Node<'_>) { @@ -192,10 +192,10 @@ impl<'a> State<'a> { Node::PreciseCapturingNonLifetimeArg(param) => self.print_ident(param.ident), Node::Block(a) => { // Containing cbox, will be closed by print-block at `}`. - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); // Head-ibox, will be closed by print-block after `{`. - self.ibox(0); - self.print_block(a); + let ib = self.ibox(0); + self.print_block(a, cb, ib); } Node::Lifetime(a) => self.print_lifetime(a), Node::GenericParam(_) => panic!("cannot print Node::GenericParam"), @@ -315,17 +315,17 @@ pub fn item_to_string(ann: &dyn PpAnn, pat: &hir::Item<'_>) -> String { } impl<'a> State<'a> { - fn bclose_maybe_open(&mut self, span: rustc_span::Span, close_box: bool) { + fn bclose_maybe_open(&mut self, span: rustc_span::Span, cb: Option<BoxMarker>) { self.maybe_print_comment(span.hi()); self.break_offset_if_not_bol(1, -INDENT_UNIT); self.word("}"); - if close_box { - self.end(); // close the outer-box + if let Some(cb) = cb { + self.end(cb); } } - fn bclose(&mut self, span: rustc_span::Span) { - self.bclose_maybe_open(span, true) + fn bclose(&mut self, span: rustc_span::Span, cb: BoxMarker) { + self.bclose_maybe_open(span, Some(cb)) } fn commasep_cmnt<T, F, G>(&mut self, b: Breaks, elts: &[T], mut op: F, mut get_span: G) @@ -333,7 +333,7 @@ impl<'a> State<'a> { F: FnMut(&mut State<'_>, &T), G: FnMut(&T) -> rustc_span::Span, { - self.rbox(0, b); + let rb = self.rbox(0, b); let len = elts.len(); let mut i = 0; for elt in elts { @@ -346,7 +346,7 @@ impl<'a> State<'a> { self.space_if_not_bol(); } } - self.end(); + self.end(rb); } fn commasep_exprs(&mut self, b: Breaks, exprs: &[hir::Expr<'_>]) { @@ -369,7 +369,7 @@ impl<'a> State<'a> { fn print_type(&mut self, ty: &hir::Ty<'_>) { self.maybe_print_comment(ty.span.lo()); - self.ibox(0); + let ib = self.ibox(0); match ty.kind { hir::TyKind::Slice(ty) => { self.word("["); @@ -456,16 +456,16 @@ impl<'a> State<'a> { self.print_ty_pat(pat); } } - self.end() + self.end(ib) } fn print_unsafe_binder(&mut self, unsafe_binder: &hir::UnsafeBinderTy<'_>) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("unsafe"); self.print_generic_params(unsafe_binder.generic_params); self.nbsp(); self.print_type(unsafe_binder.inner_ty); - self.end(); + self.end(ib); } fn print_foreign_item(&mut self, item: &hir::ForeignItem<'_>) { @@ -474,7 +474,7 @@ impl<'a> State<'a> { self.print_attrs_as_outer(self.attrs(item.hir_id())); match item.kind { hir::ForeignItemKind::Fn(sig, arg_idents, generics) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_fn( sig.decl, sig.header, @@ -483,13 +483,13 @@ impl<'a> State<'a> { arg_idents, None, ); - self.end(); // end head-ibox + self.end(ib); self.word(";"); - self.end() // end the outer fn box + self.end(cb) } hir::ForeignItemKind::Static(t, m, safety) => { self.print_safety(safety); - self.head("static"); + let (cb, ib) = self.head("static"); if m.is_mut() { self.word_space("mut"); } @@ -497,15 +497,15 @@ impl<'a> State<'a> { self.word_space(":"); self.print_type(t); self.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox + self.end(ib); + self.end(cb) } hir::ForeignItemKind::Type => { - self.head("type"); + let (cb, ib) = self.head("type"); self.print_ident(item.ident); self.word(";"); - self.end(); // end the head-ibox - self.end() // end the outer cbox + self.end(ib); + self.end(cb) } } } @@ -561,7 +561,7 @@ impl<'a> State<'a> { self.ann.pre(self, AnnNode::Item(item)); match item.kind { hir::ItemKind::ExternCrate(orig_name, ident) => { - self.head("extern crate"); + let (cb, ib) = self.head("extern crate"); if let Some(orig_name) = orig_name { self.print_name(orig_name); self.space(); @@ -570,11 +570,11 @@ impl<'a> State<'a> { } self.print_ident(ident); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } hir::ItemKind::Use(path, kind) => { - self.head("use"); + let (cb, ib) = self.head("use"); self.print_path(path, false); match kind { @@ -589,11 +589,11 @@ impl<'a> State<'a> { hir::UseKind::Glob => self.word("::*;"), hir::UseKind::ListStem => self.word("::{};"), } - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } hir::ItemKind::Static(ident, ty, m, expr) => { - self.head("static"); + let (cb, ib) = self.head("static"); if m.is_mut() { self.word_space("mut"); } @@ -601,85 +601,86 @@ impl<'a> State<'a> { self.word_space(":"); self.print_type(ty); self.space(); - self.end(); // end the head-ibox + self.end(ib); self.word_space("="); self.ann.nested(self, Nested::Body(expr)); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } hir::ItemKind::Const(ident, ty, generics, expr) => { - self.head("const"); + let (cb, ib) = self.head("const"); self.print_ident(ident); self.print_generic_params(generics.params); self.word_space(":"); self.print_type(ty); self.space(); - self.end(); // end the head-ibox + self.end(ib); self.word_space("="); self.ann.nested(self, Nested::Body(expr)); self.print_where_clause(generics); self.word(";"); - self.end(); // end the outer cbox + self.end(cb); } hir::ItemKind::Fn { ident, sig, generics, body, .. } => { - self.head(""); + let (cb, ib) = self.head(""); self.print_fn(sig.decl, sig.header, Some(ident.name), generics, &[], Some(body)); self.word(" "); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } hir::ItemKind::Macro(ident, macro_def, _) => { self.print_mac_def(macro_def, &ident, item.span, |_| {}); } hir::ItemKind::Mod(ident, mod_) => { - self.head("mod"); + let (cb, ib) = self.head("mod"); self.print_ident(ident); self.nbsp(); - self.bopen(); + self.bopen(ib); self.print_mod(mod_, attrs); - self.bclose(item.span); + self.bclose(item.span, cb); } hir::ItemKind::ForeignMod { abi, items } => { - self.head("extern"); + let (cb, ib) = self.head("extern"); self.word_nbsp(abi.to_string()); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(self.attrs(item.hir_id())); for item in items { self.ann.nested(self, Nested::ForeignItem(item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } hir::ItemKind::GlobalAsm { asm, .. } => { - self.head("global_asm!"); + // FIXME(nnethercote): `ib` is unclosed + let (cb, _ib) = self.head("global_asm!"); self.print_inline_asm(asm); - self.end() + self.end(cb) } hir::ItemKind::TyAlias(ident, ty, generics) => { - self.head("type"); + let (cb, ib) = self.head("type"); self.print_ident(ident); self.print_generic_params(generics.params); - self.end(); // end the inner ibox + self.end(ib); self.print_where_clause(generics); self.space(); self.word_space("="); self.print_type(ty); self.word(";"); - self.end(); // end the outer ibox + self.end(cb); } hir::ItemKind::Enum(ident, ref enum_definition, params) => { self.print_enum_def(enum_definition, params, ident.name, item.span); } hir::ItemKind::Struct(ident, ref struct_def, generics) => { - self.head("struct"); - self.print_struct(struct_def, generics, ident.name, item.span, true); + let (cb, ib) = self.head("struct"); + self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); } hir::ItemKind::Union(ident, ref struct_def, generics) => { - self.head("union"); - self.print_struct(struct_def, generics, ident.name, item.span, true); + let (cb, ib) = self.head("union"); + self.print_struct(struct_def, generics, ident.name, item.span, true, cb, ib); } hir::ItemKind::Impl(&hir::Impl { constness, @@ -692,7 +693,7 @@ impl<'a> State<'a> { self_ty, items, }) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_defaultness(defaultness); self.print_safety(safety); self.word_nbsp("impl"); @@ -720,15 +721,15 @@ impl<'a> State<'a> { self.print_where_clause(generics); self.space(); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(attrs); for impl_item in items { self.ann.nested(self, Nested::ImplItem(impl_item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } hir::ItemKind::Trait(is_auto, safety, ident, generics, bounds, trait_items) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_is_auto(is_auto); self.print_safety(safety); self.word_nbsp("trait"); @@ -737,22 +738,22 @@ impl<'a> State<'a> { self.print_bounds(":", bounds); self.print_where_clause(generics); self.word(" "); - self.bopen(); + self.bopen(ib); for trait_item in trait_items { self.ann.nested(self, Nested::TraitItem(trait_item.id)); } - self.bclose(item.span); + self.bclose(item.span, cb); } hir::ItemKind::TraitAlias(ident, generics, bounds) => { - self.head("trait"); + let (cb, ib) = self.head("trait"); self.print_ident(ident); self.print_generic_params(generics.params); self.nbsp(); self.print_bounds("=", bounds); self.print_where_clause(generics); self.word(";"); - self.end(); // end inner head-block - self.end(); // end outer head-block + self.end(ib); + self.end(cb); } } self.ann.post(self, AnnNode::Item(item)) @@ -763,7 +764,8 @@ impl<'a> State<'a> { } fn print_opaque_ty(&mut self, o: &hir::OpaqueTy<'_>) { - self.head("opaque"); + // FIXME(nnethercote): `cb` and `ib` are unclosed + let (_cb, _ib) = self.head("opaque"); self.word("{"); self.print_bounds("impl", o.bounds); self.word("}"); @@ -800,27 +802,33 @@ impl<'a> State<'a> { name: Symbol, span: rustc_span::Span, ) { - self.head("enum"); + let (cb, ib) = self.head("enum"); self.print_name(name); self.print_generic_params(generics.params); self.print_where_clause(generics); self.space(); - self.print_variants(enum_definition.variants, span); + self.print_variants(enum_definition.variants, span, cb, ib); } - fn print_variants(&mut self, variants: &[hir::Variant<'_>], span: rustc_span::Span) { - self.bopen(); + fn print_variants( + &mut self, + variants: &[hir::Variant<'_>], + span: rustc_span::Span, + cb: BoxMarker, + ib: BoxMarker, + ) { + self.bopen(ib); for v in variants { self.space_if_not_bol(); self.maybe_print_comment(v.span.lo()); self.print_attrs_as_outer(self.attrs(v.hir_id)); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_variant(v); self.word(","); - self.end(); + self.end(ib); self.maybe_print_trailing_comment(v.span, None); } - self.bclose(span) + self.bclose(span, cb) } fn print_defaultness(&mut self, defaultness: hir::Defaultness) { @@ -837,6 +845,8 @@ impl<'a> State<'a> { name: Symbol, span: rustc_span::Span, print_finalizer: bool, + cb: BoxMarker, + ib: BoxMarker, ) { self.print_name(name); self.print_generic_params(generics.params); @@ -855,38 +865,34 @@ impl<'a> State<'a> { if print_finalizer { self.word(";"); } - self.end(); - self.end() // close the outer-box + self.end(ib); + self.end(cb); } hir::VariantData::Struct { .. } => { self.print_where_clause(generics); - self.print_variant_struct(span, struct_def.fields()) - } - } - } - - fn print_variant_struct(&mut self, span: rustc_span::Span, fields: &[hir::FieldDef<'_>]) { - self.nbsp(); - self.bopen(); - self.hardbreak_if_not_bol(); + self.nbsp(); + self.bopen(ib); + self.hardbreak_if_not_bol(); + + for field in struct_def.fields() { + self.hardbreak_if_not_bol(); + self.maybe_print_comment(field.span.lo()); + self.print_attrs_as_outer(self.attrs(field.hir_id)); + self.print_ident(field.ident); + self.word_nbsp(":"); + self.print_type(field.ty); + self.word(","); + } - for field in fields { - self.hardbreak_if_not_bol(); - self.maybe_print_comment(field.span.lo()); - self.print_attrs_as_outer(self.attrs(field.hir_id)); - self.print_ident(field.ident); - self.word_nbsp(":"); - self.print_type(field.ty); - self.word(","); + self.bclose(span, cb) + } } - - self.bclose(span) } pub fn print_variant(&mut self, v: &hir::Variant<'_>) { - self.head(""); + let (cb, ib) = self.head(""); let generics = hir::Generics::empty(); - self.print_struct(&v.data, generics, v.ident.name, v.span, false); + self.print_struct(&v.data, generics, v.ident.name, v.span, false, cb, ib); if let Some(ref d) = v.disr_expr { self.space(); self.word_space("="); @@ -919,11 +925,11 @@ impl<'a> State<'a> { self.word(";"); } hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_method_sig(ti.ident, sig, ti.generics, &[], Some(body)); self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } hir::TraitItemKind::Type(bounds, default) => { @@ -944,11 +950,11 @@ impl<'a> State<'a> { self.print_associated_const(ii.ident, ii.generics, ty, Some(expr)); } hir::ImplItemKind::Fn(ref sig, body) => { - self.head(""); + let (cb, ib) = self.head(""); self.print_method_sig(ii.ident, sig, ii.generics, &[], Some(body)); self.nbsp(); - self.end(); // need to close a box - self.end(); // need to close a box + self.end(ib); + self.end(cb); self.ann.nested(self, Nested::Body(body)); } hir::ImplItemKind::Type(ty) => { @@ -966,15 +972,15 @@ impl<'a> State<'a> { decl: impl Fn(&mut Self), ) { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ibm1 = self.ibox(INDENT_UNIT); if super_ { self.word_nbsp("super"); } self.word_nbsp("let"); - self.ibox(INDENT_UNIT); + let ibm2 = self.ibox(INDENT_UNIT); decl(self); - self.end(); + self.end(ibm2); if let Some(init) = init { self.nbsp(); @@ -986,13 +992,13 @@ impl<'a> State<'a> { self.nbsp(); self.word_space("else"); // containing cbox, will be closed by print-block at `}` - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after `{` - self.ibox(0); - self.print_block(els); + let ib = self.ibox(0); + self.print_block(els, cb, ib); } - self.end() + self.end(ibm1) } fn print_stmt(&mut self, st: &hir::Stmt<'_>) { @@ -1020,23 +1026,30 @@ impl<'a> State<'a> { self.maybe_print_trailing_comment(st.span, None) } - fn print_block(&mut self, blk: &hir::Block<'_>) { - self.print_block_with_attrs(blk, &[]) + fn print_block(&mut self, blk: &hir::Block<'_>, cb: BoxMarker, ib: BoxMarker) { + self.print_block_with_attrs(blk, &[], cb, ib) } - fn print_block_unclosed(&mut self, blk: &hir::Block<'_>) { - self.print_block_maybe_unclosed(blk, &[], false) + fn print_block_unclosed(&mut self, blk: &hir::Block<'_>, ib: BoxMarker) { + self.print_block_maybe_unclosed(blk, &[], None, ib) } - fn print_block_with_attrs(&mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute]) { - self.print_block_maybe_unclosed(blk, attrs, true) + fn print_block_with_attrs( + &mut self, + blk: &hir::Block<'_>, + attrs: &[hir::Attribute], + cb: BoxMarker, + ib: BoxMarker, + ) { + self.print_block_maybe_unclosed(blk, attrs, Some(cb), ib) } fn print_block_maybe_unclosed( &mut self, blk: &hir::Block<'_>, attrs: &[hir::Attribute], - close_box: bool, + cb: Option<BoxMarker>, + ib: BoxMarker, ) { match blk.rules { hir::BlockCheckMode::UnsafeBlock(..) => self.word_space("unsafe"), @@ -1044,7 +1057,7 @@ impl<'a> State<'a> { } self.maybe_print_comment(blk.span.lo()); self.ann.pre(self, AnnNode::Block(blk)); - self.bopen(); + self.bopen(ib); self.print_attrs_as_inner(attrs); @@ -1056,7 +1069,7 @@ impl<'a> State<'a> { self.print_expr(expr); self.maybe_print_trailing_comment(expr.span, Some(blk.span.hi())); } - self.bclose_maybe_open(blk.span, close_box); + self.bclose_maybe_open(blk.span, cb); self.ann.post(self, AnnNode::Block(blk)) } @@ -1064,21 +1077,21 @@ impl<'a> State<'a> { if let Some(els_inner) = els { match els_inner.kind { // Another `else if` block. - hir::ExprKind::If(i, then, e) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + hir::ExprKind::If(i, hir::Expr { kind: hir::ExprKind::Block(t, None), .. }, e) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else if "); self.print_expr_as_cond(i); self.space(); - self.print_expr(then); + self.print_block(t, cb, ib); self.print_else(e); } // Final `else` block. - hir::ExprKind::Block(b, _) => { - self.cbox(INDENT_UNIT - 1); - self.ibox(0); + hir::ExprKind::Block(b, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); self.word(" else "); - self.print_block(b); + self.print_block(b, cb, ib); } // Constraints would be great here! _ => { @@ -1094,11 +1107,18 @@ impl<'a> State<'a> { blk: &hir::Expr<'_>, elseopt: Option<&hir::Expr<'_>>, ) { - self.head("if"); - self.print_expr_as_cond(test); - self.space(); - self.print_expr(blk); - self.print_else(elseopt) + match blk.kind { + hir::ExprKind::Block(blk, None) => { + let cb = self.cbox(0); + let ib = self.ibox(0); + self.word_nbsp("if"); + self.print_expr_as_cond(test); + self.space(); + self.print_block(blk, cb, ib); + self.print_else(elseopt) + } + _ => panic!("non-block then expr"), + } } fn print_anon_const(&mut self, constant: &hir::AnonConst) { @@ -1168,28 +1188,28 @@ impl<'a> State<'a> { } fn print_expr_vec(&mut self, exprs: &[hir::Expr<'_>]) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.commasep_exprs(Inconsistent, exprs); self.word("]"); - self.end() + self.end(ib) } fn print_inline_const(&mut self, constant: &hir::ConstBlock) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("const"); self.ann.nested(self, Nested::Body(constant.body)); - self.end() + self.end(ib) } fn print_expr_repeat(&mut self, element: &hir::Expr<'_>, count: &hir::ConstArg<'_>) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word("["); self.print_expr(element); self.word_space(";"); self.print_const_arg(count); self.word("]"); - self.end() + self.end(ib) } fn print_expr_struct( @@ -1204,23 +1224,23 @@ impl<'a> State<'a> { self.commasep_cmnt(Consistent, fields, |s, field| s.print_expr_field(field), |f| f.span); match wth { hir::StructTailExpr::Base(expr) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); if !fields.is_empty() { self.word(","); self.space(); } self.word(".."); self.print_expr(expr); - self.end(); + self.end(ib); } hir::StructTailExpr::DefaultFields(_) => { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); if !fields.is_empty() { self.word(","); self.space(); } self.word(".."); - self.end(); + self.end(ib); } hir::StructTailExpr::None => {} } @@ -1229,14 +1249,14 @@ impl<'a> State<'a> { } fn print_expr_field(&mut self, field: &hir::ExprField<'_>) { - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_space(":"); } self.print_expr(field.expr); - self.end() + self.end(cb) } fn print_expr_tup(&mut self, exprs: &[hir::Expr<'_>]) { @@ -1409,8 +1429,8 @@ impl<'a> State<'a> { s.print_qpath(path, true); } hir::InlineAsmOperand::Label { block } => { - s.head("label"); - s.print_block(block); + let (cb, ib) = s.head("label"); + s.print_block(block, cb, ib); } }, AsmArg::Options(opts) => { @@ -1428,7 +1448,7 @@ impl<'a> State<'a> { fn print_expr(&mut self, expr: &hir::Expr<'_>) { self.maybe_print_comment(expr.span.lo()); self.print_attrs_as_outer(self.attrs(expr.hir_id)); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.ann.pre(self, AnnNode::Expr(expr)); match expr.kind { hir::ExprKind::Array(exprs) => { @@ -1476,21 +1496,21 @@ impl<'a> State<'a> { } hir::ExprKind::Type(expr, ty) => { self.word("type_ascribe!("); - self.ibox(0); + let ib = self.ibox(0); self.print_expr(expr); self.word(","); self.space_if_not_bol(); self.print_type(ty); - self.end(); + self.end(ib); self.word(")"); } hir::ExprKind::DropTemps(init) => { // Print `{`: - self.cbox(INDENT_UNIT); - self.ibox(0); - self.bopen(); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(0); + self.bopen(ib); // Print `let _t = $init;`: let temp = Ident::from_str("_t"); @@ -1502,7 +1522,7 @@ impl<'a> State<'a> { self.print_ident(temp); // Print `}`: - self.bclose_maybe_open(expr.span, true); + self.bclose_maybe_open(expr.span, Some(cb)); } hir::ExprKind::Let(&hir::LetExpr { pat, ty, init, .. }) => { self.print_let(pat, ty, init); @@ -1515,20 +1535,20 @@ impl<'a> State<'a> { self.print_ident(label.ident); self.word_space(":"); } - self.head("loop"); - self.print_block(blk); + let (cb, ib) = self.head("loop"); + self.print_block(blk, cb, ib); } hir::ExprKind::Match(expr, arms, _) => { - self.cbox(INDENT_UNIT); - self.ibox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_nbsp("match"); self.print_expr_as_cond(expr); self.space(); - self.bopen(); + self.bopen(ib); for arm in arms { self.print_arm(arm); } - self.bclose(expr.span); + self.bclose(expr.span, cb); } hir::ExprKind::Closure(&hir::Closure { binder, @@ -1551,12 +1571,15 @@ impl<'a> State<'a> { // This is a bare expression. self.ann.nested(self, Nested::Body(body)); - self.end(); // need to close a box + // FIXME(nnethercote): this is bogus + let fake_ib = BoxMarker; + self.end(fake_ib); // A box will be closed by `print_expr`, but we didn't want an overall // wrapper so we closed the corresponding opening. so create an // empty box to satisfy the close. - self.ibox(0); + // FIXME(nnethercote): this is bogus, and `print_expr` is missing + let _ib = self.ibox(0); } hir::ExprKind::Block(blk, opt_label) => { if let Some(label) = opt_label { @@ -1564,10 +1587,10 @@ impl<'a> State<'a> { self.word_space(":"); } // containing cbox, will be closed by print-block at `}` - self.cbox(0); + let cb = self.cbox(0); // head-box, will be closed by print-block after `{` - self.ibox(0); - self.print_block(blk); + let ib = self.ibox(0); + self.print_block(blk, cb, ib); } hir::ExprKind::Assign(lhs, rhs, _) => { self.print_expr_cond_paren(lhs, lhs.precedence() <= ExprPrecedence::Assign); @@ -1668,7 +1691,7 @@ impl<'a> State<'a> { } } self.ann.post(self, AnnNode::Expr(expr)); - self.end() + self.end(ib) } fn print_local_decl(&mut self, loc: &hir::LetStmt<'_>) { @@ -2036,14 +2059,14 @@ impl<'a> State<'a> { if self.attrs(field.hir_id).is_empty() { self.space(); } - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.print_attrs_as_outer(self.attrs(field.hir_id)); if !field.is_shorthand { self.print_ident(field.ident); self.word_nbsp(":"); } self.print_pat(field.pat); - self.end(); + self.end(cb); } fn print_param(&mut self, arg: &hir::Param<'_>) { @@ -2079,9 +2102,9 @@ impl<'a> State<'a> { if self.attrs(arm.hir_id).is_empty() { self.space(); } - self.cbox(INDENT_UNIT); + let cb = self.cbox(INDENT_UNIT); self.ann.pre(self, AnnNode::Arm(arm)); - self.ibox(0); + let ib = self.ibox(0); self.print_attrs_as_outer(self.attrs(arm.hir_id)); self.print_pat(arm.pat); self.space(); @@ -2098,8 +2121,7 @@ impl<'a> State<'a> { self.print_ident(label.ident); self.word_space(":"); } - // the block will close the pattern's ibox - self.print_block_unclosed(blk); + self.print_block_unclosed(blk, ib); // If it is a user-provided unsafe block, print a comma after it if let hir::BlockCheckMode::UnsafeBlock(hir::UnsafeSource::UserProvided) = blk.rules @@ -2108,13 +2130,13 @@ impl<'a> State<'a> { } } _ => { - self.end(); // close the ibox for the pattern + self.end(ib); self.print_expr(arm.body); self.word(","); } } self.ann.post(self, AnnNode::Arm(arm)); - self.end() // close enclosing cbox + self.end(cb) } fn print_fn( @@ -2160,9 +2182,9 @@ impl<'a> State<'a> { i += 1; }; self.commasep(Inconsistent, decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); + let ib = s.ibox(INDENT_UNIT); print_arg(s, Some(ty)); - s.end(); + s.end(ib); }); if decl.c_variadic { if !decl.inputs.is_empty() { @@ -2181,7 +2203,7 @@ impl<'a> State<'a> { self.word("|"); let mut i = 0; self.commasep(Inconsistent, decl.inputs, |s, ty| { - s.ibox(INDENT_UNIT); + let ib = s.ibox(INDENT_UNIT); s.ann.nested(s, Nested::BodyParamPat(body_id, i)); i += 1; @@ -2193,7 +2215,7 @@ impl<'a> State<'a> { s.space(); s.print_type(ty); } - s.end(); + s.end(ib); }); self.word("|"); @@ -2435,16 +2457,16 @@ impl<'a> State<'a> { match decl.output { hir::FnRetTy::Return(ty) => { self.space_if_not_bol(); - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.word_space("->"); self.print_type(ty); - } - hir::FnRetTy::DefaultReturn(..) => return, - } - self.end(); + self.end(ib); - if let hir::FnRetTy::Return(output) = decl.output { - self.maybe_print_comment(output.span.lo()); + if let hir::FnRetTy::Return(output) = decl.output { + self.maybe_print_comment(output.span.lo()); + } + } + hir::FnRetTy::DefaultReturn(..) => {} } } @@ -2457,7 +2479,7 @@ impl<'a> State<'a> { generic_params: &[hir::GenericParam<'_>], arg_idents: &[Option<Ident>], ) { - self.ibox(INDENT_UNIT); + let ib = self.ibox(INDENT_UNIT); self.print_formal_generic_params(generic_params); let generics = hir::Generics::empty(); self.print_fn( @@ -2473,7 +2495,7 @@ impl<'a> State<'a> { arg_idents, None, ); - self.end(); + self.end(ib); } fn print_fn_header_info(&mut self, header: hir::FnHeader) { diff --git a/compiler/rustc_hir_typeck/Cargo.toml b/compiler/rustc_hir_typeck/Cargo.toml index b2b90cb29e3..f00125c3e09 100644 --- a/compiler/rustc_hir_typeck/Cargo.toml +++ b/compiler/rustc_hir_typeck/Cargo.toml @@ -22,6 +22,7 @@ rustc_macros = { path = "../rustc_macros" } rustc_middle = { path = "../rustc_middle" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } +rustc_target = { path = "../rustc_target" } rustc_trait_selection = { path = "../rustc_trait_selection" } smallvec = { version = "1.8.1", features = ["union", "may_dangle"] } tracing = "0.1" diff --git a/compiler/rustc_hir_typeck/messages.ftl b/compiler/rustc_hir_typeck/messages.ftl index 9e1b70f5767..23309102c4d 100644 --- a/compiler/rustc_hir_typeck/messages.ftl +++ b/compiler/rustc_hir_typeck/messages.ftl @@ -179,6 +179,9 @@ hir_typeck_ptr_cast_add_auto_to_object = cannot add {$traits_len -> .help = use `transmute` if you're sure this is sound .label = unsupported cast +hir_typeck_register_type_unstable = + type `{$ty}` cannot be used with this register class in stable + hir_typeck_remove_semi_for_coerce = you might have meant to return the `match` expression hir_typeck_remove_semi_for_coerce_expr = this could be implicitly returned but it is a statement, not a tail expression hir_typeck_remove_semi_for_coerce_ret = the `match` arms can conform to this return type diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index d770937bd66..5f5e9e45612 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -34,11 +34,9 @@ pub(crate) fn check_legal_trait_for_method_call( receiver: Option<Span>, expr_span: Span, trait_id: DefId, - body_id: DefId, + _body_id: DefId, ) -> Result<(), ErrorGuaranteed> { - if tcx.is_lang_item(trait_id, LangItem::Drop) - && !tcx.is_lang_item(body_id, LangItem::FallbackSurfaceDrop) - { + if tcx.is_lang_item(trait_id, LangItem::Drop) { let sugg = if let Some(receiver) = receiver.filter(|s| !s.is_empty()) { errors::ExplicitDestructorCallSugg::Snippet { lo: expr_span.shrink_to_lo(), diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 9e7305430e5..73279553508 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -963,3 +963,11 @@ pub(crate) enum SupertraitItemShadowee { traits: DiagSymbolList, }, } + +#[derive(Diagnostic)] +#[diag(hir_typeck_register_type_unstable)] +pub(crate) struct RegisterTypeUnstable<'a> { + #[primary_span] + pub span: Span, + pub ty: Ty<'a>, +} diff --git a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs index f5e0f01e4c5..17e13ec0a37 100644 --- a/compiler/rustc_hir_typeck/src/expr_use_visitor.rs +++ b/compiler/rustc_hir_typeck/src/expr_use_visitor.rs @@ -1000,13 +1000,15 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx // determines whether to borrow *at the level of the deref pattern* rather than // borrowing the bound place (since that inner place is inside the temporary that // stores the result of calling `deref()`/`deref_mut()` so can't be captured). + // Deref patterns on boxes don't borrow, so we ignore them here. // HACK: this could be a fake pattern corresponding to a deref inserted by match // ergonomics, in which case `pat.hir_id` will be the id of the subpattern. - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(subpattern); - let mutability = - if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let bk = ty::BorrowKind::from_mutbl(mutability); - self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + if let hir::ByRef::Yes(mutability) = + self.cx.typeck_results().deref_pat_borrow_mode(place.place.ty(), subpattern) + { + let bk = ty::BorrowKind::from_mutbl(mutability); + self.delegate.borrow_mut().borrow(place, discr_place.hir_id, bk); + } } PatKind::Never => { // A `!` pattern always counts as an immutable read of the discriminant, @@ -1691,18 +1693,19 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx place_with_id = match adjust.kind { adjustment::PatAdjust::BuiltinDeref => self.cat_deref(pat.hir_id, place_with_id)?, adjustment::PatAdjust::OverloadedDeref => { - // This adjustment corresponds to an overloaded deref; it borrows the scrutinee to - // call `Deref::deref` or `DerefMut::deref_mut`. Invoke the callback before setting - // `place_with_id` to the temporary storing the result of the deref. + // This adjustment corresponds to an overloaded deref; unless it's on a box, it + // borrows the scrutinee to call `Deref::deref` or `DerefMut::deref_mut`. Invoke + // the callback before setting `place_with_id` to the temporary storing the + // result of the deref. // HACK(dianne): giving the callback a fake deref pattern makes sure it behaves the - // same as it would if this were an explicit deref pattern. + // same as it would if this were an explicit deref pattern (including for boxes). op(&place_with_id, &hir::Pat { kind: PatKind::Deref(pat), ..*pat })?; let target_ty = match adjusts.peek() { Some(&&next_adjust) => next_adjust.source, // At the end of the deref chain, we get `pat`'s scrutinee. None => self.pat_ty_unadjusted(pat)?, }; - self.pat_deref_temp(pat.hir_id, pat, target_ty)? + self.pat_deref_place(pat.hir_id, place_with_id, pat, target_ty)? } }; } @@ -1810,7 +1813,7 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx } PatKind::Deref(subpat) => { let ty = self.pat_ty_adjusted(subpat)?; - let place = self.pat_deref_temp(pat.hir_id, subpat, ty)?; + let place = self.pat_deref_place(pat.hir_id, place_with_id, subpat, ty)?; self.cat_pattern(place, subpat, op)?; } @@ -1863,21 +1866,27 @@ impl<'tcx, Cx: TypeInformationCtxt<'tcx>, D: Delegate<'tcx>> ExprUseVisitor<'tcx Ok(()) } - /// Represents the place of the temp that stores the scrutinee of a deref pattern's interior. - fn pat_deref_temp( + /// Represents the place matched on by a deref pattern's interior. + fn pat_deref_place( &self, hir_id: HirId, + base_place: PlaceWithHirId<'tcx>, inner: &hir::Pat<'_>, target_ty: Ty<'tcx>, ) -> Result<PlaceWithHirId<'tcx>, Cx::Error> { - let mutable = self.cx.typeck_results().pat_has_ref_mut_binding(inner); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - let re_erased = self.cx.tcx().lifetimes.re_erased; - let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); - // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... - let base = self.cat_rvalue(hir_id, ty); - // ... and the inner pattern matches on the place behind that reference. - self.cat_deref(hir_id, base) + match self.cx.typeck_results().deref_pat_borrow_mode(base_place.place.ty(), inner) { + // Deref patterns on boxes are lowered using a built-in deref. + hir::ByRef::No => self.cat_deref(hir_id, base_place), + // For other types, we create a temporary to match on. + hir::ByRef::Yes(mutability) => { + let re_erased = self.cx.tcx().lifetimes.re_erased; + let ty = Ty::new_ref(self.cx.tcx(), re_erased, target_ty, mutability); + // A deref pattern stores the result of `Deref::deref` or `DerefMut::deref_mut` ... + let base = self.cat_rvalue(hir_id, ty); + // ... and the inner pattern matches on the place behind that reference. + self.cat_deref(hir_id, base) + } + } } fn is_multivariant_adt(&self, ty: Ty<'tcx>, span: Span) -> bool { diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs index cac8237f8f6..362c7d8efac 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs @@ -22,8 +22,8 @@ use rustc_infer::infer::{DefineOpaqueTypes, InferResult}; use rustc_lint::builtin::SELF_CONSTRUCTOR_FROM_OUTER_ITEM; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, GenericArgKind, GenericArgsRef, GenericParamDefKind, - IsIdentity, Ty, TyCtxt, TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, + self, AdtKind, CanonicalUserType, GenericArgsRef, GenericParamDefKind, IsIdentity, Ty, TyCtxt, + TypeFoldable, TypeVisitable, TypeVisitableExt, UserArgs, UserSelfTy, }; use rustc_middle::{bug, span_bug}; use rustc_session::lint; @@ -573,7 +573,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. pub(crate) fn register_wf_obligation( &self, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, span: Span, code: traits::ObligationCauseCode<'tcx>, ) { @@ -583,16 +583,14 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { self.tcx, cause, self.param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg))), + ty::ClauseKind::WellFormed(term), )); } /// Registers obligations that all `args` are well-formed. pub(crate) fn add_wf_bounds(&self, args: GenericArgsRef<'tcx>, span: Span) { - for arg in args.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, span, ObligationCauseCode::WellFormed(None)); + for term in args.iter().filter_map(ty::GenericArg::as_term) { + self.register_wf_obligation(term, span, ObligationCauseCode::WellFormed(None)); } } @@ -1320,27 +1318,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { infer_args: bool, ) -> ty::GenericArg<'tcx> { let tcx = self.fcx.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => self - .fcx - .re_infer( - self.span, - rustc_hir_analysis::hir_ty_lowering::RegionInferReason::Param(param), - ) - .into(), - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - if !infer_args && let Some(default) = param.default_value(tcx) { - // If we have a default, then it doesn't matter that we're not inferring - // the type/const arguments: We provide the default where any is missing. - return default.instantiate(tcx, preceding_args); - } - // If no type/const arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g., - // a lifetime argument being given instead of a type/const parameter. - // Using inference instead of `Error` gives better error messages. - self.fcx.var_for_def(self.span, param) - } + if !infer_args && let Some(default) = param.default_value(tcx) { + // If we have a default, then it doesn't matter that we're not inferring + // the type/const arguments: We provide the default where any is missing. + return default.instantiate(tcx, preceding_args); } + // If no type/const arguments were provided, we have to infer them. + // This case also occurs as a result of some malformed input, e.g., + // a lifetime argument being given instead of a type/const parameter. + // Using inference instead of `Error` gives better error messages. + self.fcx.var_for_def(self.span, param) } } diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 8b2d9ab2979..e70aa1a7064 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -8,7 +8,6 @@ use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{ExprKind, HirId, Node, QPath}; -use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; use rustc_index::IndexVec; @@ -33,6 +32,7 @@ use crate::errors::SuggestPtrNullMut; use crate::fn_ctxt::arg_matrix::{ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx}; use crate::fn_ctxt::infer::FnCall; use crate::gather_locals::Declaration; +use crate::inline_asm::InlineAsmCtxt; use crate::method::MethodCallee; use crate::method::probe::IsSuggestion; use crate::method::probe::Mode::MethodCall; @@ -98,13 +98,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); for (asm, hir_id) in deferred_asm_checks.drain(..) { let enclosing_id = self.tcx.hir_enclosing_body_owner(hir_id); - InlineAsmCtxt::new( - enclosing_id, - &self.infcx, - self.typing_env(self.param_env), - &*self.typeck_results.borrow(), - ) - .check_asm(asm); + InlineAsmCtxt::new(self, enclosing_id).check_asm(asm); } } diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_typeck/src/inline_asm.rs index 32a582aadc1..6399f0a78ae 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs +++ b/compiler/rustc_hir_typeck/src/inline_asm.rs @@ -3,25 +3,22 @@ use rustc_ast::InlineAsmTemplatePiece; use rustc_data_structures::fx::FxIndexSet; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, LangItem}; -use rustc_infer::infer::InferCtxt; use rustc_middle::bug; -use rustc_middle::ty::{ - self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, TypeckResults, UintTy, -}; +use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitableExt, UintTy}; use rustc_session::lint; use rustc_span::def_id::LocalDefId; -use rustc_span::{Symbol, sym}; +use rustc_span::{Span, Symbol, sym}; use rustc_target::asm::{ InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType, ModifierInfo, }; +use rustc_trait_selection::infer::InferCtxtExt; +use crate::FnCtxt; use crate::errors::RegisterTypeUnstable; -pub struct InlineAsmCtxt<'a, 'tcx> { - typing_env: ty::TypingEnv<'tcx>, +pub(crate) struct InlineAsmCtxt<'a, 'tcx> { target_features: &'tcx FxIndexSet<Symbol>, - infcx: &'a InferCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, + fcx: &'a FnCtxt<'a, 'tcx>, } enum NonAsmTypeReason<'tcx> { @@ -33,27 +30,17 @@ enum NonAsmTypeReason<'tcx> { } impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { - pub fn new( - def_id: LocalDefId, - infcx: &'a InferCtxt<'tcx>, - typing_env: ty::TypingEnv<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - ) -> Self { - InlineAsmCtxt { - typing_env, - target_features: infcx.tcx.asm_target_features(def_id), - infcx, - typeck_results, - } + pub(crate) fn new(fcx: &'a FnCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { + InlineAsmCtxt { target_features: fcx.tcx.asm_target_features(def_id), fcx } } fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx + self.fcx.tcx } fn expr_ty(&self, expr: &hir::Expr<'tcx>) -> Ty<'tcx> { - let ty = self.typeck_results.expr_ty_adjusted(expr); - let ty = self.infcx.resolve_vars_if_possible(ty); + let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted(expr); + let ty = self.fcx.try_structurally_resolve_type(expr.span, ty); if ty.has_non_region_infer() { Ty::new_misc_error(self.tcx()) } else { @@ -62,19 +49,23 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()` - fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool { + fn is_thin_ptr_ty(&self, span: Span, ty: Ty<'tcx>) -> bool { // Type still may have region variables, but `Sized` does not depend // on those, so just erase them before querying. - if ty.is_sized(self.tcx(), self.typing_env) { + if self.fcx.type_is_sized_modulo_regions(self.fcx.param_env, ty) { return true; } - if let ty::Foreign(..) = ty.kind() { + if let ty::Foreign(..) = self.fcx.try_structurally_resolve_type(span, ty).kind() { return true; } false } - fn get_asm_ty(&self, ty: Ty<'tcx>) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> { + fn get_asm_ty( + &self, + span: Span, + ty: Ty<'tcx>, + ) -> Result<InlineAsmType, NonAsmTypeReason<'tcx>> { let asm_ty_isize = match self.tcx().sess.target.pointer_width { 16 => InlineAsmType::I16, 32 => InlineAsmType::I32, @@ -95,7 +86,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Float(FloatTy::F128) => Ok(InlineAsmType::F128), ty::FnPtr(..) => Ok(asm_ty_isize), ty::RawPtr(elem_ty, _) => { - if self.is_thin_ptr_ty(elem_ty) { + if self.is_thin_ptr_ty(span, elem_ty) { Ok(asm_ty_isize) } else { Err(NonAsmTypeReason::NotSizedPtr(ty)) @@ -109,11 +100,20 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let field = &fields[FieldIdx::ZERO]; let elem_ty = field.ty(self.tcx(), args); - let (size, ty) = match elem_ty.kind() { + let (size, ty) = match *elem_ty.kind() { ty::Array(ty, len) => { - let len = self.tcx().normalize_erasing_regions(self.typing_env, *len); + // FIXME: `try_structurally_resolve_const` doesn't eval consts + // in the old solver. + let len = if self.fcx.next_trait_solver() { + self.fcx.try_structurally_resolve_const(span, len) + } else { + self.fcx.tcx.normalize_erasing_regions( + self.fcx.typing_env(self.fcx.param_env), + len, + ) + }; if let Some(len) = len.try_to_target_usize(self.tcx()) { - (len, *ty) + (len, ty) } else { return Err(NonAsmTypeReason::UnevaluatedSIMDArrayLength( field.did, len, @@ -183,9 +183,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ); let fields = &ty.non_enum_variant().fields; let ty = fields[FieldIdx::ZERO].ty(self.tcx(), args); - self.get_asm_ty(ty) + self.get_asm_ty(expr.span, ty) } - _ => self.get_asm_ty(ty), + _ => self.get_asm_ty(expr.span, ty), }; let asm_ty = match asm_ty { Ok(asm_ty) => asm_ty, @@ -193,7 +193,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { match reason { NonAsmTypeReason::UnevaluatedSIMDArrayLength(did, len) => { let msg = format!("cannot evaluate SIMD vector length `{len}`"); - self.infcx + self.fcx .dcx() .struct_span_err(self.tcx().def_span(did), msg) .with_span_note( @@ -204,7 +204,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } NonAsmTypeReason::Invalid(ty) => { let msg = format!("cannot use value of type `{ty}` for inline assembly"); - self.infcx.dcx().struct_span_err(expr.span, msg).with_note( + self.fcx.dcx().struct_span_err(expr.span, msg).with_note( "only integers, floats, SIMD vectors, pointers and function pointers \ can be used as arguments for inline assembly", ).emit(); @@ -213,7 +213,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = format!( "cannot use value of unsized pointer type `{ty}` for inline assembly" ); - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note("only sized pointers can be used in inline assembly") @@ -223,7 +223,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { let msg = format!( "cannot use SIMD vector with element type `{ty}` for inline assembly" ); - self.infcx.dcx() + self.fcx.dcx() .struct_span_err(self.tcx().def_span(did), msg).with_span_note( expr.span, "only integers, floats, SIMD vectors, pointers and function pointers \ @@ -232,7 +232,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { } NonAsmTypeReason::EmptySIMDArray(ty) => { let msg = format!("use of empty SIMD vector `{ty}`"); - self.infcx.dcx().struct_span_err(expr.span, msg).emit(); + self.fcx.dcx().struct_span_err(expr.span, msg).emit(); } } return None; @@ -241,9 +241,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { // Check that the type implements Copy. The only case where this can // possibly fail is for SIMD types which don't #[derive(Copy)]. - if !self.tcx().type_is_copy_modulo_regions(self.typing_env, ty) { + if !self.fcx.type_is_copy_modulo_regions(self.fcx.param_env, ty) { let msg = "arguments for inline assembly must be copyable"; - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note(format!("`{ty}` does not implement the Copy trait")) @@ -263,7 +263,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if in_asm_ty != asm_ty { let msg = "incompatible types for asm inout argument"; let in_expr_ty = self.expr_ty(in_expr); - self.infcx + self.fcx .dcx() .struct_span_err(vec![in_expr.span, expr.span], msg) .with_span_label(in_expr.span, format!("type `{in_expr_ty}`")) @@ -296,7 +296,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ) } else { let msg = format!("type `{ty}` cannot be used with this register class"); - let mut err = self.infcx.dcx().struct_span_err(expr.span, msg); + let mut err = self.fcx.dcx().struct_span_err(expr.span, msg); let supported_tys: Vec<_> = supported_tys.iter().map(|(t, _)| t.to_string()).collect(); err.note(format!( @@ -326,7 +326,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { if let Some(feature) = feature { if !self.target_features.contains(feature) { let msg = format!("`{feature}` target feature is not enabled"); - self.infcx + self.fcx .dcx() .struct_span_err(expr.span, msg) .with_note(format!( @@ -384,9 +384,9 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { Some(asm_ty) } - pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { + pub(crate) fn check_asm(&self, asm: &hir::InlineAsm<'tcx>) { let Some(asm_arch) = self.tcx().sess.asm_arch else { - self.infcx.dcx().delayed_bug("target architecture does not support asm"); + self.fcx.dcx().delayed_bug("target architecture does not support asm"); return; }; let allow_experimental_reg = self.tcx().features().asm_experimental_reg(); @@ -418,7 +418,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { op.is_clobber(), ) { let msg = format!("cannot use register `{}`: {}", reg.name(), msg); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); continue; } } @@ -458,7 +458,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { reg_class.name(), feature ); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); // register isn't enabled, don't do more checks continue; } @@ -472,7 +472,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { .intersperse(", ") .collect::<String>(), ); - self.infcx.dcx().span_err(op_sp, msg); + self.fcx.dcx().span_err(op_sp, msg); // register isn't enabled, don't do more checks continue; } @@ -512,7 +512,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::Error(_) => {} _ if ty.is_integral() => {} _ => { - self.infcx + self.fcx .dcx() .struct_span_err(op_sp, "invalid type for `const` operand") .with_span_label( @@ -531,7 +531,7 @@ impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> { ty::FnDef(..) => {} ty::Error(_) => {} _ => { - self.infcx + self.fcx .dcx() .struct_span_err(op_sp, "invalid `sym` operand") .with_span_label( diff --git a/compiler/rustc_hir_typeck/src/lib.rs b/compiler/rustc_hir_typeck/src/lib.rs index c3717b4efa4..d861a4f81b0 100644 --- a/compiler/rustc_hir_typeck/src/lib.rs +++ b/compiler/rustc_hir_typeck/src/lib.rs @@ -23,6 +23,7 @@ mod diverges; mod errors; mod expectation; mod expr; +mod inline_asm; // Used by clippy; pub mod expr_use_visitor; mod fallback; diff --git a/compiler/rustc_hir_typeck/src/writeback.rs b/compiler/rustc_hir_typeck/src/writeback.rs index 3caaa7f9a9d..c5000171ad7 100644 --- a/compiler/rustc_hir_typeck/src/writeback.rs +++ b/compiler/rustc_hir_typeck/src/writeback.rs @@ -863,7 +863,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { Resolver { fcx, span, body, nested_goals, should_normalize } } - fn report_error(&self, p: impl Into<ty::GenericArg<'tcx>>) -> ErrorGuaranteed { + fn report_error(&self, p: impl Into<ty::Term<'tcx>>) -> ErrorGuaranteed { if let Some(guar) = self.fcx.tainted_by_errors() { guar } else { @@ -887,7 +887,7 @@ impl<'cx, 'tcx> Resolver<'cx, 'tcx> { new_err: impl Fn(TyCtxt<'tcx>, ErrorGuaranteed) -> T, ) -> T where - T: Into<ty::GenericArg<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy, + T: Into<ty::Term<'tcx>> + TypeSuperFoldable<TyCtxt<'tcx>> + Copy, { let tcx = self.fcx.tcx; // We must deeply normalize in the new solver, since later lints expect diff --git a/compiler/rustc_infer/src/infer/mod.rs b/compiler/rustc_infer/src/infer/mod.rs index c4698e5cbb4..d25542dadd5 100644 --- a/compiler/rustc_infer/src/infer/mod.rs +++ b/compiler/rustc_infer/src/infer/mod.rs @@ -31,9 +31,9 @@ use rustc_middle::traits::solve::Goal; use rustc_middle::ty::error::{ExpectedFound, TypeError}; use rustc_middle::ty::{ self, BoundVarReplacerDelegate, ConstVid, FloatVid, GenericArg, GenericArgKind, GenericArgs, - GenericArgsRef, GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Ty, TyCtxt, - TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, TypeVisitableExt, TypingEnv, - TypingMode, fold_regions, + GenericArgsRef, GenericParamDefKind, InferConst, IntVid, PseudoCanonicalInput, Term, TermKind, + Ty, TyCtxt, TyVid, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, + TypeVisitableExt, TypingEnv, TypingMode, fold_regions, }; use rustc_span::{Span, Symbol}; use snapshot::undo_log::InferCtxtUndoLogs; @@ -1401,6 +1401,16 @@ impl<'tcx> TyOrConstInferVar { } } + /// Tries to extract an inference variable from a type or a constant, returns `None` + /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`) and + /// for constants other than `ty::ConstKind::Infer(_)` (or `InferConst::Fresh`). + pub fn maybe_from_term(term: Term<'tcx>) -> Option<Self> { + match term.unpack() { + TermKind::Ty(ty) => Self::maybe_from_ty(ty), + TermKind::Const(ct) => Self::maybe_from_const(ct), + } + } + /// Tries to extract an inference variable from a type, returns `None` /// for types other than `ty::Infer(_)` (or `InferTy::Fresh*`). fn maybe_from_ty(ty: Ty<'tcx>) -> Option<Self> { diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index fde1872fb39..66d2a79b93a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -982,11 +982,13 @@ fn run_required_analyses(tcx: TyCtxt<'_>) { let _ = tcx.ensure_ok().check_coroutine_obligations( tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(), ); - // Eagerly check the unsubstituted layout for cycles. - tcx.ensure_ok().layout_of( - ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) - .as_query_input(tcx.type_of(def_id).instantiate_identity()), - ); + if !tcx.is_async_drop_in_place_coroutine(def_id.to_def_id()) { + // Eagerly check the unsubstituted layout for cycles. + tcx.ensure_ok().layout_of( + ty::TypingEnv::post_analysis(tcx, def_id.to_def_id()) + .as_query_input(tcx.type_of(def_id).instantiate_identity()), + ); + } } }); }); diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs index 5c8c51c8bbc..0ceda220134 100644 --- a/compiler/rustc_interface/src/tests.rs +++ b/compiler/rustc_interface/src/tests.rs @@ -1,7 +1,7 @@ #![allow(rustc::bad_opt_access)] -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeMap; use std::num::NonZero; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::AtomicBool; use rustc_abi::Align; @@ -89,8 +89,8 @@ where S: Into<String>, I: IntoIterator<Item = S>, { - let locations: BTreeSet<CanonicalizedPath> = - locations.into_iter().map(|s| CanonicalizedPath::new(Path::new(&s.into()))).collect(); + let locations = + locations.into_iter().map(|s| CanonicalizedPath::new(PathBuf::from(s.into()))).collect(); ExternEntry { location: ExternLocation::ExactPaths(locations), diff --git a/compiler/rustc_lint/messages.ftl b/compiler/rustc_lint/messages.ftl index a6499b07fe2..99b42ee5480 100644 --- a/compiler/rustc_lint/messages.ftl +++ b/compiler/rustc_lint/messages.ftl @@ -761,7 +761,7 @@ lint_single_use_lifetime = lifetime parameter `{$ident}` only used once lint_span_use_eq_ctxt = use `.eq_ctxt()` instead of `.ctxt() == .ctxt()` -lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static is discouraged +lint_static_mut_refs_lint = creating a {$shared_label}reference to mutable static .label = {$shared_label}reference to mutable static .suggestion = use `&raw const` instead to create a raw pointer .suggestion_mut = use `&raw mut` instead to create a raw pointer diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index e65f4beab24..41b43f64798 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -948,7 +948,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #[no_mangle] /// const FOO: i32 = 5; /// ``` diff --git a/compiler/rustc_lint/src/context.rs b/compiler/rustc_lint/src/context.rs index 3660bb3f780..5679d4566dc 100644 --- a/compiler/rustc_lint/src/context.rs +++ b/compiler/rustc_lint/src/context.rs @@ -812,7 +812,10 @@ impl<'tcx> LateContext<'tcx> { return Ok(()); } - self.path.push(Symbol::intern(&disambiguated_data.data.to_string())); + self.path.push(match disambiguated_data.data.get_opt_name() { + Some(sym) => sym, + None => Symbol::intern(&disambiguated_data.data.to_string()), + }); Ok(()) } diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index f1dc420aa3c..7f4789ad0d9 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -41,7 +41,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// # #![deny(impl_trait_overcaptures)] /// # use std::fmt::Display; /// let mut x = vec![]; diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs index 50021157dda..4dda3c7951b 100644 --- a/compiler/rustc_lint/src/static_mut_refs.rs +++ b/compiler/rustc_lint/src/static_mut_refs.rs @@ -51,7 +51,7 @@ declare_lint! { /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny". pub STATIC_MUT_REFS, Warn, - "shared references or mutable references of mutable static is discouraged", + "creating a shared reference to mutable static", @future_incompatible = FutureIncompatibleInfo { reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>", diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 17d501c5730..03b8112938c 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -1424,7 +1424,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// macro_rules! foo { /// () => {}; /// ($name) => { }; @@ -4128,7 +4128,7 @@ declare_lint! { /// /// ### Example /// - /// ```rust,compile_fail + /// ```rust,compile_fail,edition2021 /// #![deny(dependency_on_unit_never_type_fallback)] /// fn main() { /// if true { diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs index 68058250a26..ed5edeef161 100644 --- a/compiler/rustc_llvm/src/lib.rs +++ b/compiler/rustc_llvm/src/lib.rs @@ -229,6 +229,7 @@ pub fn initialize_available_targets() { LLVMInitializeXtensaTargetInfo, LLVMInitializeXtensaTarget, LLVMInitializeXtensaTargetMC, + LLVMInitializeXtensaAsmPrinter, LLVMInitializeXtensaAsmParser ); init_target!( diff --git a/compiler/rustc_metadata/Cargo.toml b/compiler/rustc_metadata/Cargo.toml index 08dcc3d519a..b11f9260be7 100644 --- a/compiler/rustc_metadata/Cargo.toml +++ b/compiler/rustc_metadata/Cargo.toml @@ -26,7 +26,6 @@ rustc_serialize = { path = "../rustc_serialize" } rustc_session = { path = "../rustc_session" } rustc_span = { path = "../rustc_span" } rustc_target = { path = "../rustc_target" } -tempfile = "3.2" tracing = "0.1" # tidy-alphabetical-end diff --git a/compiler/rustc_metadata/src/fs.rs b/compiler/rustc_metadata/src/fs.rs index c4e1e0f1d1a..e57534b847e 100644 --- a/compiler/rustc_metadata/src/fs.rs +++ b/compiler/rustc_metadata/src/fs.rs @@ -2,11 +2,11 @@ use std::path::{Path, PathBuf}; use std::{fs, io}; use rustc_data_structures::temp_dir::MaybeTempDir; +use rustc_fs_util::TempDirBuilder; use rustc_middle::ty::TyCtxt; use rustc_session::config::{CrateType, OutFileName, OutputType}; use rustc_session::output::filename_for_metadata; use rustc_session::{MetadataKind, Session}; -use tempfile::Builder as TempFileBuilder; use crate::errors::{ BinaryOutputToTty, FailedCopyToStdout, FailedCreateEncodedMetadata, FailedCreateFile, @@ -45,7 +45,7 @@ pub fn encode_and_write_metadata(tcx: TyCtxt<'_>) -> (EncodedMetadata, bool) { // final destination, with an `fs::rename` call. In order for the rename to // always succeed, the temporary file needs to be on the same filesystem, // which is why we create it inside the output directory specifically. - let metadata_tmpdir = TempFileBuilder::new() + let metadata_tmpdir = TempDirBuilder::new() .prefix("rmeta") .tempdir_in(out_filename.parent().unwrap_or_else(|| Path::new(""))) .unwrap_or_else(|err| tcx.dcx().emit_fatal(FailedCreateTempdir { err })); diff --git a/compiler/rustc_middle/messages.ftl b/compiler/rustc_middle/messages.ftl index 0b3c0be1a4e..3d27e587b6c 100644 --- a/compiler/rustc_middle/messages.ftl +++ b/compiler/rustc_middle/messages.ftl @@ -1,7 +1,11 @@ +middle_assert_async_resume_after_drop = `async fn` resumed after async drop + middle_assert_async_resume_after_panic = `async fn` resumed after panicking middle_assert_async_resume_after_return = `async fn` resumed after completion +middle_assert_coroutine_resume_after_drop = coroutine resumed after async drop + middle_assert_coroutine_resume_after_panic = coroutine resumed after panicking middle_assert_coroutine_resume_after_return = coroutine resumed after completion @@ -9,6 +13,8 @@ middle_assert_coroutine_resume_after_return = coroutine resumed after completion middle_assert_divide_by_zero = attempt to divide `{$val}` by zero +middle_assert_gen_resume_after_drop = `gen` fn or block cannot be further iterated on after it async dropped + middle_assert_gen_resume_after_panic = `gen` fn or block cannot be further iterated on after it panicked middle_assert_misaligned_ptr_deref = diff --git a/compiler/rustc_middle/src/arena.rs b/compiler/rustc_middle/src/arena.rs index d1bbb0598fe..2dcb0de92b7 100644 --- a/compiler/rustc_middle/src/arena.rs +++ b/compiler/rustc_middle/src/arena.rs @@ -9,6 +9,7 @@ macro_rules! arena_types { ($macro:path) => ( $macro!([ [] layout: rustc_abi::LayoutData<rustc_abi::FieldIdx, rustc_abi::VariantIdx>, + [] proxy_coroutine_layout: rustc_middle::mir::CoroutineLayout<'tcx>, [] fn_abi: rustc_target::callconv::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>, // AdtDef are interned and compared by address [decode] adt_def: rustc_middle::ty::AdtDefData, diff --git a/compiler/rustc_middle/src/middle/exported_symbols.rs b/compiler/rustc_middle/src/middle/exported_symbols.rs index 0bfbd398797..3c62017072e 100644 --- a/compiler/rustc_middle/src/middle/exported_symbols.rs +++ b/compiler/rustc_middle/src/middle/exported_symbols.rs @@ -44,6 +44,7 @@ pub enum ExportedSymbol<'tcx> { Generic(DefId, GenericArgsRef<'tcx>), DropGlue(Ty<'tcx>), AsyncDropGlueCtorShim(Ty<'tcx>), + AsyncDropGlue(DefId, Ty<'tcx>), ThreadLocalShim(DefId), NoDefId(ty::SymbolName<'tcx>), } @@ -63,6 +64,9 @@ impl<'tcx> ExportedSymbol<'tcx> { ExportedSymbol::AsyncDropGlueCtorShim(ty) => { tcx.symbol_name(ty::Instance::resolve_async_drop_in_place(tcx, ty)) } + ExportedSymbol::AsyncDropGlue(def_id, ty) => { + tcx.symbol_name(ty::Instance::resolve_async_drop_in_place_poll(tcx, def_id, ty)) + } ExportedSymbol::ThreadLocalShim(def_id) => tcx.symbol_name(ty::Instance { def: ty::InstanceKind::ThreadLocalShim(def_id), args: ty::GenericArgs::empty(), diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index db19c858e7c..adc100941a3 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -200,7 +200,13 @@ pub struct CoroutineInfo<'tcx> { /// Coroutine drop glue. This field is populated after the state transform pass. pub coroutine_drop: Option<Body<'tcx>>, - /// The layout of a coroutine. This field is populated after the state transform pass. + /// Coroutine async drop glue. + pub coroutine_drop_async: Option<Body<'tcx>>, + + /// When coroutine has sync drop, this is async proxy calling `coroutine_drop` sync impl. + pub coroutine_drop_proxy_async: Option<Body<'tcx>>, + + /// The layout of a coroutine. Produced by the state transformation. pub coroutine_layout: Option<CoroutineLayout<'tcx>>, /// If this is a coroutine then record the type of source expression that caused this coroutine @@ -220,6 +226,8 @@ impl<'tcx> CoroutineInfo<'tcx> { yield_ty: Some(yield_ty), resume_ty: Some(resume_ty), coroutine_drop: None, + coroutine_drop_async: None, + coroutine_drop_proxy_async: None, coroutine_layout: None, } } @@ -588,6 +596,26 @@ impl<'tcx> Body<'tcx> { } #[inline] + pub fn coroutine_drop_async(&self) -> Option<&Body<'tcx>> { + self.coroutine.as_ref().and_then(|coroutine| coroutine.coroutine_drop_async.as_ref()) + } + + #[inline] + pub fn coroutine_requires_async_drop(&self) -> bool { + self.coroutine_drop_async().is_some() + } + + #[inline] + pub fn future_drop_poll(&self) -> Option<&Body<'tcx>> { + self.coroutine.as_ref().and_then(|coroutine| { + coroutine + .coroutine_drop_async + .as_ref() + .or(coroutine.coroutine_drop_proxy_async.as_ref()) + }) + } + + #[inline] pub fn coroutine_kind(&self) -> Option<CoroutineKind> { self.coroutine.as_ref().map(|coroutine| coroutine.coroutine_kind) } diff --git a/compiler/rustc_middle/src/mir/mono.rs b/compiler/rustc_middle/src/mir/mono.rs index 1ffe958dbdd..6b413a7383a 100644 --- a/compiler/rustc_middle/src/mir/mono.rs +++ b/compiler/rustc_middle/src/mir/mono.rs @@ -530,6 +530,8 @@ impl<'tcx> CodegenUnit<'tcx> { | InstanceKind::CloneShim(..) | InstanceKind::ThreadLocalShim(..) | InstanceKind::FnPtrAddrShim(..) + | InstanceKind::AsyncDropGlue(..) + | InstanceKind::FutureDropPollShim(..) | InstanceKind::AsyncDropGlueCtorShim(..) => None, } } diff --git a/compiler/rustc_middle/src/mir/pretty.rs b/compiler/rustc_middle/src/mir/pretty.rs index d57019fee0f..57ae7dc55c5 100644 --- a/compiler/rustc_middle/src/mir/pretty.rs +++ b/compiler/rustc_middle/src/mir/pretty.rs @@ -253,9 +253,7 @@ fn dump_path<'tcx>( })); s } - ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => { - // Unfortunately, pretty-printed typed are not very filename-friendly. - // We dome some filtering. + ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => { let mut s = ".".to_owned(); s.extend(ty.to_string().chars().filter_map(|c| match c { ' ' => None, @@ -264,6 +262,34 @@ fn dump_path<'tcx>( })); s } + ty::InstanceKind::AsyncDropGlue(_, ty) => { + let ty::Coroutine(_, args) = ty.kind() else { + bug!(); + }; + let ty = args.first().unwrap().expect_ty(); + let mut s = ".".to_owned(); + s.extend(ty.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } + ty::InstanceKind::FutureDropPollShim(_, proxy_cor, impl_cor) => { + let mut s = ".".to_owned(); + s.extend(proxy_cor.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s.push('.'); + s.extend(impl_cor.to_string().chars().filter_map(|c| match c { + ' ' => None, + ':' | '<' | '>' => Some('_'), + c => Some(c), + })); + s + } _ => String::new(), }; @@ -1050,7 +1076,13 @@ impl<'tcx> TerminatorKind<'tcx> { Call { target: None, unwind: _, .. } => vec![], Yield { drop: Some(_), .. } => vec!["resume".into(), "drop".into()], Yield { drop: None, .. } => vec!["resume".into()], - Drop { unwind: UnwindAction::Cleanup(_), .. } => vec!["return".into(), "unwind".into()], + Drop { unwind: UnwindAction::Cleanup(_), drop: Some(_), .. } => { + vec!["return".into(), "unwind".into(), "drop".into()] + } + Drop { unwind: UnwindAction::Cleanup(_), drop: None, .. } => { + vec!["return".into(), "unwind".into()] + } + Drop { unwind: _, drop: Some(_), .. } => vec!["return".into(), "drop".into()], Drop { unwind: _, .. } => vec!["return".into()], Assert { unwind: UnwindAction::Cleanup(_), .. } => { vec!["success".into(), "unwind".into()] diff --git a/compiler/rustc_middle/src/mir/syntax.rs b/compiler/rustc_middle/src/mir/syntax.rs index 304b3caa6e1..bb068f3821d 100644 --- a/compiler/rustc_middle/src/mir/syntax.rs +++ b/compiler/rustc_middle/src/mir/syntax.rs @@ -77,6 +77,8 @@ pub enum MirPhase { /// exception is fields of packed structs. In analysis MIR, `Drop(P)` for a `P` that might be /// misaligned for this reason implicitly moves `P` to a temporary before dropping. Runtime /// MIR has no such rules, and dropping a misaligned place is simply UB. + /// - Async drops: after drop elaboration some drops may become async (`drop`, `async_fut` fields). + /// StateTransform pass will expand those async drops or reset to sync. /// - Unwinding: in analysis MIR, unwinding from a function which may not unwind aborts. In /// runtime MIR, this is UB. /// - Retags: If `-Zmir-emit-retag` is enabled, analysis MIR has "implicit" retags in the same @@ -768,7 +770,34 @@ pub enum TerminatorKind<'tcx> { /// The `replace` flag indicates whether this terminator was created as part of an assignment. /// This should only be used for diagnostic purposes, and does not have any operational /// meaning. - Drop { place: Place<'tcx>, target: BasicBlock, unwind: UnwindAction, replace: bool }, + /// + /// Async drop processing: + /// In compiler/rustc_mir_build/src/build/scope.rs we detect possible async drop: + /// drop of object with `needs_async_drop`. + /// Async drop later, in StateTransform pass, may be expanded into additional yield-point + /// for poll-loop of async drop future. + /// So we need prepared 'drop' target block in the similar way as for `Yield` terminator + /// (see `drops.build_mir::<CoroutineDrop>` in scopes.rs). + /// In compiler/rustc_mir_transform/src/elaborate_drops.rs for object implementing `AsyncDrop` trait + /// we need to prepare async drop feature - resolve `AsyncDrop::drop` and codegen call. + /// `async_fut` is set to the corresponding local. + /// For coroutine drop we don't need this logic because coroutine drop works with the same + /// layout object as coroutine itself. So `async_fut` will be `None` for coroutine drop. + /// Both `drop` and `async_fut` fields are only used in compiler/rustc_mir_transform/src/coroutine.rs, + /// StateTransform pass. In `expand_async_drops` async drops are expanded + /// into one or two yield points with poll ready/pending switch. + /// When a coroutine has any internal async drop, the coroutine drop function will be async + /// (generated by `create_coroutine_drop_shim_async`, not `create_coroutine_drop_shim`). + Drop { + place: Place<'tcx>, + target: BasicBlock, + unwind: UnwindAction, + replace: bool, + /// Cleanup to be done if the coroutine is dropped at this suspend point (for async drop). + drop: Option<BasicBlock>, + /// Prepared async future local (for async drop) + async_fut: Option<Local>, + }, /// Roughly speaking, evaluates the `func` operand and the arguments, and starts execution of /// the referred to function. The operand types must match the argument types of the function. @@ -1043,6 +1072,7 @@ pub enum AssertKind<O> { RemainderByZero(O), ResumedAfterReturn(CoroutineKind), ResumedAfterPanic(CoroutineKind), + ResumedAfterDrop(CoroutineKind), MisalignedPointerDereference { required: O, found: O }, NullPointerDereference, } diff --git a/compiler/rustc_middle/src/mir/terminator.rs b/compiler/rustc_middle/src/mir/terminator.rs index 82e8422c52d..8a1ead7d19d 100644 --- a/compiler/rustc_middle/src/mir/terminator.rs +++ b/compiler/rustc_middle/src/mir/terminator.rs @@ -208,6 +208,16 @@ impl<O> AssertKind<O> { LangItem::PanicGenFnNonePanic } NullPointerDereference => LangItem::PanicNullPointerDereference, + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => LangItem::PanicCoroutineResumedDrop, + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + LangItem::PanicAsyncFnResumedDrop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + LangItem::PanicAsyncGenFnResumedDrop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + LangItem::PanicGenFnNoneDrop + } BoundsCheck { .. } | MisalignedPointerDereference { .. } => { bug!("Unexpected AssertKind") @@ -298,6 +308,18 @@ impl<O> AssertKind<O> { ResumedAfterPanic(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { write!(f, "\"`gen fn` should just keep returning `None` after panicking\"") } + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + write!(f, "\"coroutine resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + write!(f, "\"`async fn` resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + write!(f, "\"`async gen fn` resumed after async drop\"") + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + write!(f, "\"`gen fn` resumed after drop\"") + } } } @@ -345,6 +367,19 @@ impl<O> AssertKind<O> { middle_assert_coroutine_resume_after_panic } NullPointerDereference => middle_assert_null_ptr_deref, + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Async, _)) => { + middle_assert_async_resume_after_drop + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _)) => { + todo!() + } + ResumedAfterDrop(CoroutineKind::Desugared(CoroutineDesugaring::Gen, _)) => { + middle_assert_gen_resume_after_drop + } + ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + middle_assert_coroutine_resume_after_drop + } + MisalignedPointerDereference { .. } => middle_assert_misaligned_ptr_deref, } } @@ -377,7 +412,10 @@ impl<O> AssertKind<O> { add!("left", format!("{left:#?}")); add!("right", format!("{right:#?}")); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => {} + ResumedAfterReturn(_) + | ResumedAfterPanic(_) + | NullPointerDereference + | ResumedAfterDrop(_) => {} MisalignedPointerDereference { required, found } => { add!("required", format!("{required:#?}")); add!("found", format!("{found:#?}")); @@ -457,7 +495,7 @@ mod helper { #[define_opaque(Successors)] pub fn successors_for_value(&self, value: u128) -> Successors<'_> { let target = self.target_for_value(value); - (&[]).into_iter().copied().chain(Some(target)) + (&[]).into_iter().copied().chain(Some(target).into_iter().chain(None)) } } @@ -467,13 +505,23 @@ mod helper { pub fn successors(&self) -> Successors<'_> { use self::TerminatorKind::*; match *self { + // 3-successors for async drop: target, unwind, dropline (parent coroutine drop) + Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: Some(d), .. } => { + slice::from_ref(t) + .into_iter() + .copied() + .chain(Some(u).into_iter().chain(Some(d))) + } + // 2-successors Call { target: Some(ref t), unwind: UnwindAction::Cleanup(u), .. } | Yield { resume: ref t, drop: Some(u), .. } - | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), .. } + | Drop { target: ref t, unwind: UnwindAction::Cleanup(u), drop: None, .. } + | Drop { target: ref t, unwind: _, drop: Some(u), .. } | Assert { target: ref t, unwind: UnwindAction::Cleanup(u), .. } | FalseUnwind { real_target: ref t, unwind: UnwindAction::Cleanup(u) } => { - slice::from_ref(t).into_iter().copied().chain(Some(u)) + slice::from_ref(t).into_iter().copied().chain(Some(u).into_iter().chain(None)) } + // single successor Goto { target: ref t } | Call { target: None, unwind: UnwindAction::Cleanup(ref t), .. } | Call { target: Some(ref t), unwind: _, .. } @@ -481,23 +529,33 @@ mod helper { | Drop { target: ref t, unwind: _, .. } | Assert { target: ref t, unwind: _, .. } | FalseUnwind { real_target: ref t, unwind: _ } => { - slice::from_ref(t).into_iter().copied().chain(None) + slice::from_ref(t).into_iter().copied().chain(None.into_iter().chain(None)) } + // No successors UnwindResume | UnwindTerminate(_) | CoroutineDrop | Return | Unreachable | TailCall { .. } - | Call { target: None, unwind: _, .. } => (&[]).into_iter().copied().chain(None), + | Call { target: None, unwind: _, .. } => { + (&[]).into_iter().copied().chain(None.into_iter().chain(None)) + } + // Multiple successors InlineAsm { ref targets, unwind: UnwindAction::Cleanup(u), .. } => { - targets.iter().copied().chain(Some(u)) + targets.iter().copied().chain(Some(u).into_iter().chain(None)) + } + InlineAsm { ref targets, unwind: _, .. } => { + targets.iter().copied().chain(None.into_iter().chain(None)) } - InlineAsm { ref targets, unwind: _, .. } => targets.iter().copied().chain(None), - SwitchInt { ref targets, .. } => targets.targets.iter().copied().chain(None), - FalseEdge { ref real_target, imaginary_target } => { - slice::from_ref(real_target).into_iter().copied().chain(Some(imaginary_target)) + SwitchInt { ref targets, .. } => { + targets.targets.iter().copied().chain(None.into_iter().chain(None)) } + // FalseEdge + FalseEdge { ref real_target, imaginary_target } => slice::from_ref(real_target) + .into_iter() + .copied() + .chain(Some(imaginary_target).into_iter().chain(None)), } } @@ -506,16 +564,31 @@ mod helper { pub fn successors_mut(&mut self) -> SuccessorsMut<'_> { use self::TerminatorKind::*; match *self { + // 3-successors for async drop: target, unwind, dropline (parent coroutine drop) + Drop { + target: ref mut t, + unwind: UnwindAction::Cleanup(ref mut u), + drop: Some(ref mut d), + .. + } => slice::from_mut(t).into_iter().chain(Some(u).into_iter().chain(Some(d))), + // 2-successors Call { target: Some(ref mut t), unwind: UnwindAction::Cleanup(ref mut u), .. } | Yield { resume: ref mut t, drop: Some(ref mut u), .. } - | Drop { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } + | Drop { + target: ref mut t, + unwind: UnwindAction::Cleanup(ref mut u), + drop: None, + .. + } + | Drop { target: ref mut t, unwind: _, drop: Some(ref mut u), .. } | Assert { target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), .. } | FalseUnwind { real_target: ref mut t, unwind: UnwindAction::Cleanup(ref mut u), - } => slice::from_mut(t).into_iter().chain(Some(u)), + } => slice::from_mut(t).into_iter().chain(Some(u).into_iter().chain(None)), + // single successor Goto { target: ref mut t } | Call { target: None, unwind: UnwindAction::Cleanup(ref mut t), .. } | Call { target: Some(ref mut t), unwind: _, .. } @@ -523,22 +596,33 @@ mod helper { | Drop { target: ref mut t, unwind: _, .. } | Assert { target: ref mut t, unwind: _, .. } | FalseUnwind { real_target: ref mut t, unwind: _ } => { - slice::from_mut(t).into_iter().chain(None) + slice::from_mut(t).into_iter().chain(None.into_iter().chain(None)) } + // No successors UnwindResume | UnwindTerminate(_) | CoroutineDrop | Return | Unreachable | TailCall { .. } - | Call { target: None, unwind: _, .. } => (&mut []).into_iter().chain(None), + | Call { target: None, unwind: _, .. } => { + (&mut []).into_iter().chain(None.into_iter().chain(None)) + } + // Multiple successors InlineAsm { ref mut targets, unwind: UnwindAction::Cleanup(ref mut u), .. } => { - targets.iter_mut().chain(Some(u)) + targets.iter_mut().chain(Some(u).into_iter().chain(None)) + } + InlineAsm { ref mut targets, unwind: _, .. } => { + targets.iter_mut().chain(None.into_iter().chain(None)) + } + SwitchInt { ref mut targets, .. } => { + targets.targets.iter_mut().chain(None.into_iter().chain(None)) } - InlineAsm { ref mut targets, unwind: _, .. } => targets.iter_mut().chain(None), - SwitchInt { ref mut targets, .. } => targets.targets.iter_mut().chain(None), + // FalseEdge FalseEdge { ref mut real_target, ref mut imaginary_target } => { - slice::from_mut(real_target).into_iter().chain(Some(imaginary_target)) + slice::from_mut(real_target) + .into_iter() + .chain(Some(imaginary_target).into_iter().chain(None)) } } } @@ -671,8 +755,10 @@ impl<'tcx> TerminatorKind<'tcx> { Goto { target } => TerminatorEdges::Single(target), + // FIXME: Maybe we need also TerminatorEdges::Trio for async drop + // (target + unwind + dropline) Assert { target, unwind, expected: _, msg: _, cond: _ } - | Drop { target, unwind, place: _, replace: _ } + | Drop { target, unwind, place: _, replace: _, drop: _, async_fut: _ } | FalseUnwind { real_target: target, unwind } => match unwind { UnwindAction::Cleanup(unwind) => TerminatorEdges::Double(target, unwind), UnwindAction::Continue | UnwindAction::Terminate(_) | UnwindAction::Unreachable => { diff --git a/compiler/rustc_middle/src/mir/visit.rs b/compiler/rustc_middle/src/mir/visit.rs index de4d5140e85..1777756174b 100644 --- a/compiler/rustc_middle/src/mir/visit.rs +++ b/compiler/rustc_middle/src/mir/visit.rs @@ -353,17 +353,21 @@ macro_rules! make_mir_visitor { coroutine_closure_def_id: _def_id, receiver_by_ref: _, } - | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, None) | ty::InstanceKind::DropGlue(_def_id, None) => {} ty::InstanceKind::FnPtrShim(_def_id, ty) | ty::InstanceKind::DropGlue(_def_id, Some(ty)) | ty::InstanceKind::CloneShim(_def_id, ty) | ty::InstanceKind::FnPtrAddrShim(_def_id, ty) - | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, Some(ty)) => { + | ty::InstanceKind::AsyncDropGlue(_def_id, ty) + | ty::InstanceKind::AsyncDropGlueCtorShim(_def_id, ty) => { // FIXME(eddyb) use a better `TyContext` here. self.visit_ty($(& $mutability)? *ty, TyContext::Location(location)); } + ty::InstanceKind::FutureDropPollShim(_def_id, proxy_ty, impl_ty) => { + self.visit_ty($(& $mutability)? *proxy_ty, TyContext::Location(location)); + self.visit_ty($(& $mutability)? *impl_ty, TyContext::Location(location)); + } } self.visit_args(callee_args, location); } @@ -521,7 +525,14 @@ macro_rules! make_mir_visitor { self.visit_operand(discr, location); } - TerminatorKind::Drop { place, target: _, unwind: _, replace: _ } => { + TerminatorKind::Drop { + place, + target: _, + unwind: _, + replace: _, + drop: _, + async_fut: _, + } => { self.visit_place( place, PlaceContext::MutatingUse(MutatingUseContext::Drop), @@ -634,7 +645,7 @@ macro_rules! make_mir_visitor { OverflowNeg(op) | DivisionByZero(op) | RemainderByZero(op) => { self.visit_operand(op, location); } - ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference => { + ResumedAfterReturn(_) | ResumedAfterPanic(_) | NullPointerDereference | ResumedAfterDrop(_) => { // Nothing to visit } MisalignedPointerDereference { required, found } => { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 19a15041b41..b55e4d7d831 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1359,7 +1359,11 @@ rustc_queries! { /// Generates a MIR body for the shim. query mir_shims(key: ty::InstanceKind<'tcx>) -> &'tcx mir::Body<'tcx> { arena_cache - desc { |tcx| "generating MIR shim for `{}`", tcx.def_path_str(key.def_id()) } + desc { + |tcx| "generating MIR shim for `{}`, instance={:?}", + tcx.def_path_str(key.def_id()), + key + } } /// The `symbol_name` query provides the symbol name for calling a @@ -1590,6 +1594,10 @@ rustc_queries! { query is_unpin_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` is `Unpin`", env.value } } + /// Query backing `Ty::is_async_drop`. + query is_async_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { + desc { "computing whether `{}` is `AsyncDrop`", env.value } + } /// Query backing `Ty::needs_drop`. query needs_drop_raw(env: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>) -> bool { desc { "computing whether `{}` needs drop", env.value } @@ -1622,6 +1630,14 @@ rustc_queries! { cache_on_disk_if { true } } + /// A list of types where the ADT requires async drop if and only if any of + /// those types require async drop. If the ADT is known to always need async drop + /// then `Err(AlwaysRequiresDrop)` is returned. + query adt_async_drop_tys(def_id: DefId) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> { + desc { |tcx| "computing when `{}` needs async drop", tcx.def_path_str(def_id) } + cache_on_disk_if { true } + } + /// A list of types where the ADT requires drop if and only if any of those types /// has significant drop. A type marked with the attribute `rustc_insignificant_dtor` /// is considered to not be significant. A drop is significant if it is implemented diff --git a/compiler/rustc_middle/src/thir.rs b/compiler/rustc_middle/src/thir.rs index c168142fb1e..086ec529f33 100644 --- a/compiler/rustc_middle/src/thir.rs +++ b/compiler/rustc_middle/src/thir.rs @@ -799,7 +799,12 @@ pub enum PatKind<'tcx> { /// Deref pattern, written `box P` for now. DerefPattern { subpattern: Box<Pat<'tcx>>, - mutability: hir::Mutability, + /// Whether the pattern scrutinee needs to be borrowed in order to call `Deref::deref` or + /// `DerefMut::deref_mut`, and if so, which. This is `ByRef::No` for deref patterns on + /// boxes; they are lowered using a built-in deref rather than a method call, thus they + /// don't borrow the scrutinee. + #[type_visitable(ignore)] + borrow: ByRef, }, /// One of the following: diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 26096de669f..1efd0d1d14b 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -590,6 +590,10 @@ impl<'tcx> Interner for TyCtxt<'tcx> { self.defaultness(def_id).has_value() } + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool { + self.specializes((impl_def_id, victim_def_id)) + } + fn impl_is_default(self, impl_def_id: DefId) -> bool { self.defaultness(impl_def_id).is_default() } @@ -713,7 +717,6 @@ macro_rules! bidirectional_lang_item_map { bidirectional_lang_item_map! { // tidy-alphabetical-start - AsyncDestruct, AsyncFn, AsyncFnKindHelper, AsyncFnKindUpvars, @@ -1711,6 +1714,10 @@ impl<'tcx> TyCtxt<'tcx> { self.coroutine_kind(def_id).is_some() } + pub fn is_async_drop_in_place_coroutine(self, def_id: DefId) -> bool { + self.is_lang_item(self.parent(def_id), LangItem::AsyncDropInPlace) + } + /// Returns the movability of the coroutine of `def_id`, or panics /// if given a `def_id` that is not a coroutine. pub fn coroutine_movability(self, def_id: DefId) -> hir::Movability { diff --git a/compiler/rustc_middle/src/ty/generic_args.rs b/compiler/rustc_middle/src/ty/generic_args.rs index 8de64b3bfac..542c0b3e6eb 100644 --- a/compiler/rustc_middle/src/ty/generic_args.rs +++ b/compiler/rustc_middle/src/ty/generic_args.rs @@ -250,17 +250,17 @@ impl<'tcx> GenericArg<'tcx> { } #[inline] - pub fn as_type(self) -> Option<Ty<'tcx>> { + pub fn as_region(self) -> Option<ty::Region<'tcx>> { match self.unpack() { - GenericArgKind::Type(ty) => Some(ty), + GenericArgKind::Lifetime(re) => Some(re), _ => None, } } #[inline] - pub fn as_region(self) -> Option<ty::Region<'tcx>> { + pub fn as_type(self) -> Option<Ty<'tcx>> { match self.unpack() { - GenericArgKind::Lifetime(re) => Some(re), + GenericArgKind::Type(ty) => Some(ty), _ => None, } } @@ -273,6 +273,15 @@ impl<'tcx> GenericArg<'tcx> { } } + #[inline] + pub fn as_term(self) -> Option<ty::Term<'tcx>> { + match self.unpack() { + GenericArgKind::Lifetime(_) => None, + GenericArgKind::Type(ty) => Some(ty.into()), + GenericArgKind::Const(ct) => Some(ct.into()), + } + } + /// Unpack the `GenericArg` as a region when it is known certainly to be a region. pub fn expect_region(self) -> ty::Region<'tcx> { self.as_region().unwrap_or_else(|| bug!("expected a region, but found another kind")) diff --git a/compiler/rustc_middle/src/ty/instance.rs b/compiler/rustc_middle/src/ty/instance.rs index faad0a82acb..d660e7d0d60 100644 --- a/compiler/rustc_middle/src/ty/instance.rs +++ b/compiler/rustc_middle/src/ty/instance.rs @@ -147,6 +147,9 @@ pub enum InstanceKind<'tcx> { /// native support. ThreadLocalShim(DefId), + /// Proxy shim for async drop of future (def_id, proxy_cor_ty, impl_cor_ty) + FutureDropPollShim(DefId, Ty<'tcx>, Ty<'tcx>), + /// `core::ptr::drop_in_place::<T>`. /// /// The `DefId` is for `core::ptr::drop_in_place`. @@ -173,7 +176,13 @@ pub enum InstanceKind<'tcx> { /// /// The `DefId` is for `core::future::async_drop::async_drop_in_place`, the `Ty` /// is the type `T`. - AsyncDropGlueCtorShim(DefId, Option<Ty<'tcx>>), + AsyncDropGlueCtorShim(DefId, Ty<'tcx>), + + /// `core::future::async_drop::async_drop_in_place::<'_, T>::{closure}`. + /// + /// async_drop_in_place poll function implementation (for generated coroutine). + /// `Ty` here is `async_drop_in_place<T>::{closure}` coroutine type, not just `T` + AsyncDropGlue(DefId, Ty<'tcx>), } impl<'tcx> Instance<'tcx> { @@ -221,7 +230,9 @@ impl<'tcx> Instance<'tcx> { .upstream_monomorphizations_for(def) .and_then(|monos| monos.get(&self.args).cloned()), InstanceKind::DropGlue(_, Some(_)) => tcx.upstream_drop_glue_for(self.args), - InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => { + InstanceKind::AsyncDropGlue(_, _) => None, + InstanceKind::FutureDropPollShim(_, _, _) => None, + InstanceKind::AsyncDropGlueCtorShim(_, _) => { tcx.upstream_async_drop_glue_for(self.args) } _ => None, @@ -248,6 +259,8 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::DropGlue(def_id, _) | InstanceKind::CloneShim(def_id, _) | InstanceKind::FnPtrAddrShim(def_id, _) + | InstanceKind::FutureDropPollShim(def_id, _, _) + | InstanceKind::AsyncDropGlue(def_id, _) | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, } } @@ -257,7 +270,9 @@ impl<'tcx> InstanceKind<'tcx> { match self { ty::InstanceKind::Item(def) => Some(def), ty::InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) + | InstanceKind::AsyncDropGlueCtorShim(def_id, _) + | InstanceKind::AsyncDropGlue(def_id, _) + | InstanceKind::FutureDropPollShim(def_id, ..) | InstanceKind::ThreadLocalShim(def_id) => Some(def_id), InstanceKind::VTableShim(..) | InstanceKind::ReifyShim(..) @@ -267,7 +282,6 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::ClosureOnceShim { .. } | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::CloneShim(..) | InstanceKind::FnPtrAddrShim(..) => None, } @@ -292,7 +306,9 @@ impl<'tcx> InstanceKind<'tcx> { let def_id = match *self { ty::InstanceKind::Item(def) => def, ty::InstanceKind::DropGlue(_, Some(_)) => return false, - ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => return false, + ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) => return ty.is_coroutine(), + ty::InstanceKind::FutureDropPollShim(_, _, _) => return false, + ty::InstanceKind::AsyncDropGlue(_, _) => return false, ty::InstanceKind::ThreadLocalShim(_) => return false, _ => return true, }; @@ -325,11 +341,12 @@ impl<'tcx> InstanceKind<'tcx> { | InstanceKind::FnPtrAddrShim(..) | InstanceKind::FnPtrShim(..) | InstanceKind::DropGlue(_, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) => false, + | InstanceKind::FutureDropPollShim(..) + | InstanceKind::AsyncDropGlue(_, _) => false, + InstanceKind::AsyncDropGlueCtorShim(_, _) => false, InstanceKind::ClosureOnceShim { .. } | InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::Item(_) | InstanceKind::Intrinsic(..) | InstanceKind::ReifyShim(..) @@ -406,8 +423,11 @@ pub fn fmt_instance( InstanceKind::DropGlue(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), InstanceKind::CloneShim(_, ty) => write!(f, " - shim({ty})"), InstanceKind::FnPtrAddrShim(_, ty) => write!(f, " - shim({ty})"), - InstanceKind::AsyncDropGlueCtorShim(_, None) => write!(f, " - shim(None)"), - InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) => write!(f, " - shim(Some({ty}))"), + InstanceKind::FutureDropPollShim(_, proxy_ty, impl_ty) => { + write!(f, " - dropshim({proxy_ty}-{impl_ty})") + } + InstanceKind::AsyncDropGlue(_, ty) => write!(f, " - shim({ty})"), + InstanceKind::AsyncDropGlueCtorShim(_, ty) => write!(f, " - shim(Some({ty}))"), } } @@ -425,6 +445,51 @@ impl<'tcx> fmt::Display for Instance<'tcx> { } } +// async_drop_in_place<T>::coroutine.poll, when T is a standart coroutine, +// should be resolved to this coroutine's future_drop_poll (through FutureDropPollShim proxy). +// async_drop_in_place<async_drop_in_place<T>::coroutine>::coroutine.poll, +// when T is a standart coroutine, should be resolved to this coroutine's future_drop_poll. +// async_drop_in_place<async_drop_in_place<T>::coroutine>::coroutine.poll, +// when T is not a coroutine, should be resolved to the innermost +// async_drop_in_place<T>::coroutine's poll function (through FutureDropPollShim proxy) +fn resolve_async_drop_poll<'tcx>(mut cor_ty: Ty<'tcx>) -> Instance<'tcx> { + let first_cor = cor_ty; + let ty::Coroutine(poll_def_id, proxy_args) = first_cor.kind() else { + bug!(); + }; + let poll_def_id = *poll_def_id; + let mut child_ty = cor_ty; + loop { + if let ty::Coroutine(child_def, child_args) = child_ty.kind() { + cor_ty = child_ty; + if *child_def == poll_def_id { + child_ty = child_args.first().unwrap().expect_ty(); + continue; + } else { + return Instance { + def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + args: proxy_args, + }; + } + } else { + let ty::Coroutine(_, child_args) = cor_ty.kind() else { + bug!(); + }; + if first_cor != cor_ty { + return Instance { + def: ty::InstanceKind::FutureDropPollShim(poll_def_id, first_cor, cor_ty), + args: proxy_args, + }; + } else { + return Instance { + def: ty::InstanceKind::AsyncDropGlue(poll_def_id, cor_ty), + args: child_args, + }; + } + } + } +} + impl<'tcx> Instance<'tcx> { pub fn new(def_id: DefId, args: GenericArgsRef<'tcx>) -> Instance<'tcx> { assert!( @@ -736,6 +801,15 @@ impl<'tcx> Instance<'tcx> { ) } + pub fn resolve_async_drop_in_place_poll( + tcx: TyCtxt<'tcx>, + def_id: DefId, + ty: Ty<'tcx>, + ) -> ty::Instance<'tcx> { + let args = tcx.mk_args(&[ty.into()]); + Instance::expect_resolve(tcx, ty::TypingEnv::fully_monomorphized(), def_id, args, DUMMY_SP) + } + #[instrument(level = "debug", skip(tcx), ret)] pub fn fn_once_adapter_instance( tcx: TyCtxt<'tcx>, @@ -800,6 +874,9 @@ impl<'tcx> Instance<'tcx> { }; if tcx.is_lang_item(trait_item_id, coroutine_callable_item) { + if tcx.is_async_drop_in_place_coroutine(coroutine_def_id) { + return Some(resolve_async_drop_poll(rcvr_args.type_at(0))); + } let ty::Coroutine(_, id_args) = *tcx.type_of(coroutine_def_id).skip_binder().kind() else { bug!() diff --git a/compiler/rustc_middle/src/ty/mod.rs b/compiler/rustc_middle/src/ty/mod.rs index 3698cfb9870..eba6d61ba7d 100644 --- a/compiler/rustc_middle/src/ty/mod.rs +++ b/compiler/rustc_middle/src/ty/mod.rs @@ -17,7 +17,7 @@ use std::hash::{Hash, Hasher}; use std::marker::PhantomData; use std::num::NonZero; use std::ptr::NonNull; -use std::{fmt, str}; +use std::{fmt, iter, str}; pub use adt::*; pub use assoc::*; @@ -39,6 +39,7 @@ use rustc_hir::LangItem; use rustc_hir::def::{CtorKind, CtorOf, DefKind, DocLinkResMap, LifetimeRes, Res}; use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, LocalDefId, LocalDefIdMap}; use rustc_index::IndexVec; +use rustc_index::bit_set::BitMatrix; use rustc_macros::{ Decodable, Encodable, HashStable, TyDecodable, TyEncodable, TypeFoldable, TypeVisitable, extension, @@ -103,13 +104,14 @@ pub use self::visit::*; use crate::error::{OpaqueHiddenTypeMismatch, TypeMismatchReason}; use crate::metadata::ModChild; use crate::middle::privacy::EffectiveVisibilities; -use crate::mir::{Body, CoroutineLayout}; +use crate::mir::{Body, CoroutineLayout, CoroutineSavedLocal, SourceInfo}; use crate::query::{IntoQueryParam, Providers}; use crate::ty; use crate::ty::codec::{TyDecoder, TyEncoder}; pub use crate::ty::diagnostics::*; use crate::ty::fast_reject::SimplifiedType; use crate::ty::util::Discr; +use crate::ty::walk::TypeWalker; pub mod abstract_const; pub mod adjustment; @@ -631,6 +633,20 @@ impl<'tcx> Term<'tcx> { TermKind::Const(ct) => ct.is_ct_infer(), } } + + /// Iterator that walks `self` and any types reachable from + /// `self`, in depth-first order. Note that just walks the types + /// that appear in `self`, it does not descend into the fields of + /// structs or variants. For example: + /// + /// ```text + /// isize => { isize } + /// Foo<Bar<isize>> => { Foo<Bar<isize>>, Bar<isize>, isize } + /// [isize] => { [isize], isize } + /// ``` + pub fn walk(self) -> TypeWalker<TyCtxt<'tcx>> { + TypeWalker::new(self.into()) + } } const TAG_MASK: usize = 0b11; @@ -1724,11 +1740,13 @@ impl<'tcx> TyCtxt<'tcx> { | ty::InstanceKind::Virtual(..) | ty::InstanceKind::ClosureOnceShim { .. } | ty::InstanceKind::ConstructCoroutineInClosureShim { .. } + | ty::InstanceKind::FutureDropPollShim(..) | ty::InstanceKind::DropGlue(..) | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::FnPtrAddrShim(..) - | ty::InstanceKind::AsyncDropGlueCtorShim(..) => self.mir_shims(instance), + | ty::InstanceKind::AsyncDropGlueCtorShim(..) + | ty::InstanceKind::AsyncDropGlue(..) => self.mir_shims(instance), } } @@ -1844,12 +1862,12 @@ impl<'tcx> TyCtxt<'tcx> { self.def_kind(trait_def_id) == DefKind::TraitAlias } - /// Returns layout of a coroutine. Layout might be unavailable if the + /// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the /// coroutine is tainted by errors. /// /// Takes `coroutine_kind` which can be acquired from the `CoroutineArgs::kind_ty`, /// e.g. `args.as_coroutine().kind_ty()`. - pub fn coroutine_layout( + fn ordinary_coroutine_layout( self, def_id: DefId, coroutine_kind_ty: Ty<'tcx>, @@ -1883,6 +1901,51 @@ impl<'tcx> TyCtxt<'tcx> { } } + /// Returns layout of a `async_drop_in_place::{closure}` coroutine + /// (returned from `async fn async_drop_in_place<T>(..)`). + /// Layout might be unavailable if the coroutine is tainted by errors. + fn async_drop_coroutine_layout( + self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Option<&'tcx CoroutineLayout<'tcx>> { + let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args)); + self.mir_shims(instance).coroutine_layout_raw() + } + + /// Returns layout of a coroutine. Layout might be unavailable if the + /// coroutine is tainted by errors. + pub fn coroutine_layout( + self, + def_id: DefId, + args: GenericArgsRef<'tcx>, + ) -> Option<&'tcx CoroutineLayout<'tcx>> { + if self.is_async_drop_in_place_coroutine(def_id) { + // layout of `async_drop_in_place<T>::{closure}` in case, + // when T is a coroutine, contains this internal coroutine's ptr in upvars + // and doesn't require any locals. Here is an `empty coroutine's layout` + let arg_cor_ty = args.first().unwrap().expect_ty(); + if arg_cor_ty.is_coroutine() { + let span = self.def_span(def_id); + let source_info = SourceInfo::outermost(span); + let variant_fields: IndexVec<VariantIdx, IndexVec<FieldIdx, CoroutineSavedLocal>> = + iter::repeat(IndexVec::new()).take(CoroutineArgs::RESERVED_VARIANTS).collect(); + let proxy_layout = CoroutineLayout { + field_tys: [].into(), + field_names: [].into(), + variant_fields, + variant_source_info: [source_info].into(), + storage_conflicts: BitMatrix::new(0, 0), + }; + return Some(self.arena.alloc(proxy_layout)); + } else { + self.async_drop_coroutine_layout(def_id, args) + } + } else { + self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty()) + } + } + /// Given the `DefId` of an impl, returns the `DefId` of the trait it implements. /// If it implements no trait, returns `None`. pub fn trait_id_of_impl(self, def_id: DefId) -> Option<DefId> { diff --git a/compiler/rustc_middle/src/ty/print/pretty.rs b/compiler/rustc_middle/src/ty/print/pretty.rs index 42315b613f0..ad8677f7c7d 100644 --- a/compiler/rustc_middle/src/ty/print/pretty.rs +++ b/compiler/rustc_middle/src/ty/print/pretty.rs @@ -3247,7 +3247,7 @@ define_print! { ty::ClauseKind::ConstArgHasType(ct, ty) => { p!("the constant `", print(ct), "` has type `", print(ty), "`") }, - ty::ClauseKind::WellFormed(arg) => p!(print(arg), " well-formed"), + ty::ClauseKind::WellFormed(term) => p!(print(term), " well-formed"), ty::ClauseKind::ConstEvaluatable(ct) => { p!("the constant `", print(ct), "` can be evaluated") } diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs index ddce400e5b1..2165cf186bd 100644 --- a/compiler/rustc_middle/src/ty/sty.rs +++ b/compiler/rustc_middle/src/ty/sty.rs @@ -4,7 +4,6 @@ use std::assert_matches::debug_assert_matches; use std::borrow::Cow; -use std::iter; use std::ops::{ControlFlow, Range}; use hir::def::{CtorKind, DefKind}; @@ -19,7 +18,7 @@ use rustc_type_ir::TyKind::*; use rustc_type_ir::walk::TypeWalker; use rustc_type_ir::{self as ir, BoundVar, CollectAndApply, DynKind, TypeVisitableExt, elaborate}; use tracing::instrument; -use ty::util::{AsyncDropGlueMorphology, IntTypeExt}; +use ty::util::IntTypeExt; use super::GenericParamDefKind; use crate::infer::canonical::Canonical; @@ -78,8 +77,7 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> { #[inline] fn variant_range(&self, def_id: DefId, tcx: TyCtxt<'tcx>) -> Range<VariantIdx> { // FIXME requires optimized MIR - FIRST_VARIANT - ..tcx.coroutine_layout(def_id, tcx.types.unit).unwrap().variant_fields.next_index() + FIRST_VARIANT..tcx.coroutine_layout(def_id, self.args).unwrap().variant_fields.next_index() } /// The discriminant for the given variant. Panics if the `variant_index` is @@ -139,10 +137,14 @@ impl<'tcx> ty::CoroutineArgs<TyCtxt<'tcx>> { def_id: DefId, tcx: TyCtxt<'tcx>, ) -> impl Iterator<Item: Iterator<Item = Ty<'tcx>>> { - let layout = tcx.coroutine_layout(def_id, self.kind_ty()).unwrap(); + let layout = tcx.coroutine_layout(def_id, self.args).unwrap(); layout.variant_fields.iter().map(move |variant| { variant.iter().map(move |field| { - ty::EarlyBinder::bind(layout.field_tys[*field].ty).instantiate(tcx, self.args) + if tcx.is_async_drop_in_place_coroutine(def_id) { + layout.field_tys[*field].ty + } else { + ty::EarlyBinder::bind(layout.field_tys[*field].ty).instantiate(tcx, self.args) + } }) }) } @@ -1046,10 +1048,6 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> { self.discriminant_ty(interner) } - fn async_destructor_ty(self, interner: TyCtxt<'tcx>) -> Ty<'tcx> { - self.async_destructor_ty(interner) - } - fn has_unsafe_fields(self) -> bool { Ty::has_unsafe_fields(self) } @@ -1419,6 +1417,34 @@ impl<'tcx> Ty<'tcx> { cf.is_break() } + /// Returns the deepest `async_drop_in_place::{closure}` implementation. + /// + /// `async_drop_in_place<T>::{closure}`, when T is a coroutine, is a proxy-impl + /// to call async drop poll from impl coroutine. + pub fn find_async_drop_impl_coroutine<F: FnMut(Ty<'tcx>)>( + self, + tcx: TyCtxt<'tcx>, + mut f: F, + ) -> Ty<'tcx> { + assert!(self.is_coroutine()); + let mut cor_ty = self; + let mut ty = cor_ty; + loop { + if let ty::Coroutine(def_id, args) = ty.kind() { + cor_ty = ty; + f(ty); + if tcx.is_async_drop_in_place_coroutine(*def_id) { + ty = args.first().unwrap().expect_ty(); + continue; + } else { + return cor_ty; + } + } else { + return cor_ty; + } + } + } + /// Returns the type and mutability of `*ty`. /// /// The parameter `explicit` indicates if this is an *explicit* dereference. @@ -1560,125 +1586,6 @@ impl<'tcx> Ty<'tcx> { } } - /// Returns the type of the async destructor of this type. - pub fn async_destructor_ty(self, tcx: TyCtxt<'tcx>) -> Ty<'tcx> { - match self.async_drop_glue_morphology(tcx) { - AsyncDropGlueMorphology::Noop => { - return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop) - .instantiate_identity(); - } - AsyncDropGlueMorphology::DeferredDropInPlace => { - let drop_in_place = - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDeferredDropInPlace) - .instantiate(tcx, &[self.into()]); - return Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[drop_in_place.into()]); - } - AsyncDropGlueMorphology::Custom => (), - } - - match *self.kind() { - ty::Param(_) | ty::Alias(..) | ty::Infer(ty::TyVar(_)) => { - let assoc_items = tcx - .associated_item_def_ids(tcx.require_lang_item(LangItem::AsyncDestruct, None)); - Ty::new_projection(tcx, assoc_items[0], [self]) - } - - ty::Array(elem_ty, _) | ty::Slice(elem_ty) => { - let dtor = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropSlice) - .instantiate(tcx, &[elem_ty.into()]); - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[dtor.into()]) - } - - ty::Adt(adt_def, args) if adt_def.is_enum() || adt_def.is_struct() => self - .adt_async_destructor_ty( - tcx, - adt_def.variants().iter().map(|v| v.fields.iter().map(|f| f.ty(tcx, args))), - ), - ty::Tuple(tys) => self.adt_async_destructor_ty(tcx, iter::once(tys)), - ty::Closure(_, args) => { - self.adt_async_destructor_ty(tcx, iter::once(args.as_closure().upvar_tys())) - } - ty::CoroutineClosure(_, args) => self - .adt_async_destructor_ty(tcx, iter::once(args.as_coroutine_closure().upvar_tys())), - - ty::Adt(adt_def, _) => { - assert!(adt_def.is_union()); - - let surface_drop = self.surface_async_dropper_ty(tcx).unwrap(); - - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[surface_drop.into()]) - } - - ty::Bound(..) - | ty::Foreign(_) - | ty::Placeholder(_) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => { - bug!("`async_destructor_ty` applied to unexpected type: {self:?}") - } - - _ => bug!("`async_destructor_ty` is not yet implemented for type: {self:?}"), - } - } - - fn adt_async_destructor_ty<I>(self, tcx: TyCtxt<'tcx>, variants: I) -> Ty<'tcx> - where - I: Iterator + ExactSizeIterator, - I::Item: IntoIterator<Item = Ty<'tcx>>, - { - debug_assert_eq!(self.async_drop_glue_morphology(tcx), AsyncDropGlueMorphology::Custom); - - let defer = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropDefer); - let chain = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain); - - let noop = - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropNoop).instantiate_identity(); - let either = Ty::async_destructor_combinator(tcx, LangItem::AsyncDropEither); - - let variants_dtor = variants - .into_iter() - .map(|variant| { - variant - .into_iter() - .map(|ty| defer.instantiate(tcx, &[ty.into()])) - .reduce(|acc, next| chain.instantiate(tcx, &[acc.into(), next.into()])) - .unwrap_or(noop) - }) - .reduce(|other, matched| { - either.instantiate(tcx, &[other.into(), matched.into(), self.into()]) - }) - .unwrap(); - - let dtor = if let Some(dropper_ty) = self.surface_async_dropper_ty(tcx) { - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropChain) - .instantiate(tcx, &[dropper_ty.into(), variants_dtor.into()]) - } else { - variants_dtor - }; - - Ty::async_destructor_combinator(tcx, LangItem::AsyncDropFuse) - .instantiate(tcx, &[dtor.into()]) - } - - fn surface_async_dropper_ty(self, tcx: TyCtxt<'tcx>) -> Option<Ty<'tcx>> { - let adt_def = self.ty_adt_def()?; - let dropper = adt_def - .async_destructor(tcx) - .map(|_| LangItem::SurfaceAsyncDropInPlace) - .or_else(|| adt_def.destructor(tcx).map(|_| LangItem::AsyncDropSurfaceDropInPlace))?; - Some(Ty::async_destructor_combinator(tcx, dropper).instantiate(tcx, &[self.into()])) - } - - fn async_destructor_combinator( - tcx: TyCtxt<'tcx>, - lang_item: LangItem, - ) -> ty::EarlyBinder<'tcx, Ty<'tcx>> { - tcx.fn_sig(tcx.require_lang_item(lang_item, None)) - .map_bound(|fn_sig| fn_sig.output().no_bound_vars().unwrap()) - } - /// Returns the type of metadata for (potentially wide) pointers to this type, /// or the struct tail if the metadata type cannot be determined. pub fn ptr_metadata_ty_or_tail( @@ -2020,6 +1927,13 @@ impl<'tcx> Ty<'tcx> { } } + pub fn is_async_drop_in_place_coroutine(self, tcx: TyCtxt<'_>) -> bool { + match self.kind() { + ty::Coroutine(def, ..) => tcx.is_async_drop_in_place_coroutine(*def), + _ => false, + } + } + /// Returns `true` when the outermost type cannot be further normalized, /// resolved, or instantiated. This includes all primitive types, but also /// things like ADTs and trait objects, since even if their arguments or diff --git a/compiler/rustc_middle/src/ty/typeck_results.rs b/compiler/rustc_middle/src/ty/typeck_results.rs index 4c5c669771f..8c5827d36df 100644 --- a/compiler/rustc_middle/src/ty/typeck_results.rs +++ b/compiler/rustc_middle/src/ty/typeck_results.rs @@ -475,6 +475,21 @@ impl<'tcx> TypeckResults<'tcx> { has_ref_mut } + /// How should a deref pattern find the place for its inner pattern to match on? + /// + /// In most cases, if the pattern recursively contains a `ref mut` binding, we find the inner + /// pattern's scrutinee by calling `DerefMut::deref_mut`, and otherwise we call `Deref::deref`. + /// However, for boxes we can use a built-in deref instead, which doesn't borrow the scrutinee; + /// in this case, we return `ByRef::No`. + pub fn deref_pat_borrow_mode(&self, pointer_ty: Ty<'_>, inner: &hir::Pat<'_>) -> ByRef { + if pointer_ty.is_box() { + ByRef::No + } else { + let mutable = self.pat_has_ref_mut_binding(inner); + ByRef::Yes(if mutable { Mutability::Mut } else { Mutability::Not }) + } + } + /// For a given closure, returns the iterator of `ty::CapturedPlace`s that are captured /// by the closure. pub fn closure_min_captures_flattened( diff --git a/compiler/rustc_middle/src/ty/util.rs b/compiler/rustc_middle/src/ty/util.rs index 269425d67f3..f5158edffcf 100644 --- a/compiler/rustc_middle/src/ty/util.rs +++ b/compiler/rustc_middle/src/ty/util.rs @@ -468,25 +468,6 @@ impl<'tcx> TyCtxt<'tcx> { Some(ty::AsyncDestructor { impl_did: dtor_candidate? }) } - /// Returns async drop glue morphology for a definition. To get async drop - /// glue morphology for a type see [`Ty::async_drop_glue_morphology`]. - // - // FIXME: consider making this a query - pub fn async_drop_glue_morphology(self, did: DefId) -> AsyncDropGlueMorphology { - let ty: Ty<'tcx> = self.type_of(did).instantiate_identity(); - - // Async drop glue morphology is an internal detail, so - // using `TypingMode::PostAnalysis` probably should be fine. - let typing_env = ty::TypingEnv::fully_monomorphized(); - if ty.needs_async_drop(self, typing_env) { - AsyncDropGlueMorphology::Custom - } else if ty.needs_drop(self, typing_env) { - AsyncDropGlueMorphology::DeferredDropInPlace - } else { - AsyncDropGlueMorphology::Noop - } - } - /// Returns the set of types that are required to be alive in /// order to run the destructor of `def` (see RFCs 769 and /// 1238). @@ -1114,18 +1095,6 @@ impl<'tcx> TypeFolder<TyCtxt<'tcx>> for FreeAliasTypeExpander<'tcx> { } } -/// Indicates the form of `AsyncDestruct::Destructor`. Used to simplify async -/// drop glue for types not using async drop. -#[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub enum AsyncDropGlueMorphology { - /// Async destructor simply does nothing - Noop, - /// Async destructor simply runs `drop_in_place` - DeferredDropInPlace, - /// Async destructor has custom logic - Custom, -} - impl<'tcx> Ty<'tcx> { /// Returns the `Size` for primitive types (bool, uint, int, char, float). pub fn primitive_size(self, tcx: TyCtxt<'tcx>) -> Size { @@ -1295,16 +1264,17 @@ impl<'tcx> Ty<'tcx> { } } - /// Get morphology of the async drop glue, needed for types which do not - /// use async drop. To get async drop glue morphology for a definition see - /// [`TyCtxt::async_drop_glue_morphology`]. Used for `AsyncDestruct::Destructor` - /// type construction. - // - // FIXME: implement optimization to not instantiate a certain morphology of - // async drop glue too soon to allow per type optimizations, see array case - // for more info. Perhaps then remove this method and use `needs_(async_)drop` - // instead. - pub fn async_drop_glue_morphology(self, tcx: TyCtxt<'tcx>) -> AsyncDropGlueMorphology { + /// Checks whether values of this type `T` implement the `AsyncDrop` trait. + pub fn is_async_drop(self, tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>) -> bool { + !self.is_trivially_not_async_drop() + && tcx.is_async_drop_raw(typing_env.as_query_input(self)) + } + + /// Fast path helper for testing if a type is `AsyncDrop`. + /// + /// Returning true means the type is known to be `!AsyncDrop`. Returning + /// `false` means nothing -- could be `AsyncDrop`, might not be. + fn is_trivially_not_async_drop(self) -> bool { match self.kind() { ty::Int(_) | ty::Uint(_) @@ -1316,46 +1286,26 @@ impl<'tcx> Ty<'tcx> { | ty::Ref(..) | ty::RawPtr(..) | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Infer(ty::FreshIntTy(_)) - | ty::Infer(ty::FreshFloatTy(_)) => AsyncDropGlueMorphology::Noop, - + | ty::Error(_) + | ty::FnPtr(..) => true, // FIXME(unsafe_binders): ty::UnsafeBinder(_) => todo!(), - - ty::Tuple(tys) if tys.is_empty() => AsyncDropGlueMorphology::Noop, - ty::Adt(adt_def, _) if adt_def.is_manually_drop() => AsyncDropGlueMorphology::Noop, - - // Foreign types can never have destructors. - ty::Foreign(_) => AsyncDropGlueMorphology::Noop, - - // FIXME: implement dynamic types async drops - ty::Error(_) | ty::Dynamic(..) => AsyncDropGlueMorphology::DeferredDropInPlace, - - ty::Tuple(_) | ty::Array(_, _) | ty::Slice(_) => { - // Assume worst-case scenario, because we can instantiate async - // destructors in different orders: - // - // 1. Instantiate [T; N] with T = String and N = 0 - // 2. Instantiate <[String; 0] as AsyncDestruct>::Destructor - // - // And viceversa, thus we cannot rely on String not using async - // drop or array having zero (0) elements - AsyncDropGlueMorphology::Custom - } - ty::Pat(ty, _) => ty.async_drop_glue_morphology(tcx), - - ty::Adt(adt_def, _) => tcx.async_drop_glue_morphology(adt_def.did()), - - ty::Closure(did, _) - | ty::CoroutineClosure(did, _) - | ty::Coroutine(did, _) - | ty::CoroutineWitness(did, _) => tcx.async_drop_glue_morphology(*did), - - ty::Alias(..) | ty::Param(_) | ty::Bound(..) | ty::Placeholder(..) | ty::Infer(_) => { - // No specifics, but would usually mean forwarding async drop glue - AsyncDropGlueMorphology::Custom + ty::Tuple(fields) => fields.iter().all(Self::is_trivially_not_async_drop), + ty::Pat(elem_ty, _) | ty::Slice(elem_ty) | ty::Array(elem_ty, _) => { + elem_ty.is_trivially_not_async_drop() } + ty::Adt(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::CoroutineClosure(..) + | ty::Dynamic(..) + | ty::Foreign(_) + | ty::Coroutine(..) + | ty::CoroutineWitness(..) + | ty::Infer(_) + | ty::Alias(..) + | ty::Param(_) + | ty::Placeholder(_) => false, } } @@ -1401,9 +1351,6 @@ impl<'tcx> Ty<'tcx> { /// (Note that this implies that if `ty` has an async destructor attached, /// then `needs_async_drop` will definitely return `true` for `ty`.) /// - /// When constructing `AsyncDestruct::Destructor` type, use - /// [`Ty::async_drop_glue_morphology`] instead. - // // FIXME(zetanumbers): Note that this method is used to check eligible types // in unions. #[inline] diff --git a/compiler/rustc_middle/src/values.rs b/compiler/rustc_middle/src/values.rs index 39fcc686c55..4d70a708732 100644 --- a/compiler/rustc_middle/src/values.rs +++ b/compiler/rustc_middle/src/values.rs @@ -138,18 +138,26 @@ impl<'tcx> Value<TyCtxt<'tcx>> for &[ty::Variance] { cycle_error: &CycleError, _guar: ErrorGuaranteed, ) -> Self { - if let Some(frame) = cycle_error.cycle.get(0) - && frame.query.dep_kind == dep_kinds::variances_of - && let Some(def_id) = frame.query.def_id - { - let n = tcx.generics_of(def_id).own_params.len(); - vec![ty::Bivariant; n].leak() - } else { - span_bug!( - cycle_error.usage.as_ref().unwrap().0, - "only `variances_of` returns `&[ty::Variance]`" - ); - } + search_for_cycle_permutation( + &cycle_error.cycle, + |cycle| { + if let Some(frame) = cycle.get(0) + && frame.query.dep_kind == dep_kinds::variances_of + && let Some(def_id) = frame.query.def_id + { + let n = tcx.generics_of(def_id).own_params.len(); + ControlFlow::Break(vec![ty::Bivariant; n].leak()) + } else { + ControlFlow::Continue(()) + } + }, + || { + span_bug!( + cycle_error.usage.as_ref().unwrap().0, + "only `variances_of` returns `&[ty::Variance]`" + ) + }, + ) } } diff --git a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs index 5918498f239..494ee33fd8b 100644 --- a/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs +++ b/compiler/rustc_mir_build/src/builder/custom/parse/instruction.rs @@ -69,6 +69,8 @@ impl<'a, 'tcx> ParseCtxt<'a, 'tcx> { target: self.parse_return_to(args[1])?, unwind: self.parse_unwind_action(args[2])?, replace: false, + drop: None, + async_fut: None, }) }, @call(mir_call, args) => { diff --git a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs index f9791776f71..5a97b08db28 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_rvalue.rs @@ -762,6 +762,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: success, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); this.diverge_from(block); diff --git a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs index d66b38c5b00..3a7854a5e11 100644 --- a/compiler/rustc_mir_build/src/builder/matches/match_pair.rs +++ b/compiler/rustc_mir_build/src/builder/matches/match_pair.rs @@ -1,5 +1,6 @@ use std::sync::Arc; +use rustc_hir::ByRef; use rustc_middle::mir::*; use rustc_middle::thir::*; use rustc_middle::ty::{self, Ty, TypeVisitableExt}; @@ -260,7 +261,13 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::Deref { ref subpattern } => { + PatKind::Deref { ref subpattern } + | PatKind::DerefPattern { ref subpattern, borrow: ByRef::No } => { + if cfg!(debug_assertions) && matches!(pattern.kind, PatKind::DerefPattern { .. }) { + // Only deref patterns on boxes can be lowered using a built-in deref. + debug_assert!(pattern.ty.is_box()); + } + MatchPairTree::for_pattern( place_builder.deref(), subpattern, @@ -271,7 +278,7 @@ impl<'tcx> MatchPairTree<'tcx> { None } - PatKind::DerefPattern { ref subpattern, mutability } => { + PatKind::DerefPattern { ref subpattern, borrow: ByRef::Yes(mutability) } => { // Create a new temporary for each deref pattern. // FIXME(deref_patterns): dedup temporaries to avoid multiple `deref()` calls? let temp = cx.temp( diff --git a/compiler/rustc_mir_build/src/builder/scope.rs b/compiler/rustc_mir_build/src/builder/scope.rs index 4e4b11b8fa6..7c287129820 100644 --- a/compiler/rustc_mir_build/src/builder/scope.rs +++ b/compiler/rustc_mir_build/src/builder/scope.rs @@ -89,6 +89,7 @@ use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::middle::region; use rustc_middle::mir::*; use rustc_middle::thir::{ExprId, LintLevel}; +use rustc_middle::ty::{self, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint::Level; use rustc_span::source_map::Spanned; @@ -405,6 +406,8 @@ impl DropTree { unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), place: drop_node.data.local.into(), replace: false, + drop: None, + async_fut: None, }; cfg.terminate(block, drop_node.data.source_info, terminator); } @@ -848,6 +851,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: next, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); block = next; @@ -879,6 +884,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { block.unit() } + fn is_async_drop_impl( + tcx: TyCtxt<'tcx>, + local_decls: &IndexVec<Local, LocalDecl<'tcx>>, + typing_env: ty::TypingEnv<'tcx>, + local: Local, + ) -> bool { + let ty = local_decls[local].ty; + if ty.is_async_drop(tcx, typing_env) || ty.is_coroutine() { + return true; + } + ty.needs_async_drop(tcx, typing_env) + } + fn is_async_drop(&self, local: Local) -> bool { + Self::is_async_drop_impl(self.tcx, &self.local_decls, self.typing_env(), local) + } + fn leave_top_scope(&mut self, block: BasicBlock) -> BasicBlock { // If we are emitting a `drop` statement, we need to have the cached // diverge cleanup pads ready in case that drop panics. @@ -887,14 +908,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let unwind_to = if needs_cleanup { self.diverge_cleanup() } else { DropIdx::MAX }; let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); + let has_async_drops = is_coroutine + && scope.drops.iter().any(|v| v.kind == DropKind::Value && self.is_async_drop(v.local)); + let dropline_to = if has_async_drops { Some(self.diverge_dropline()) } else { None }; + let scope = self.scopes.scopes.last().expect("leave_top_scope called with no scopes"); + let typing_env = self.typing_env(); build_scope_drops( &mut self.cfg, &mut self.scopes.unwind_drops, + &mut self.scopes.coroutine_drops, scope, block, unwind_to, + dropline_to, is_coroutine && needs_cleanup, self.arg_count, + |v: Local| Self::is_async_drop_impl(self.tcx, &self.local_decls, typing_env, v), ) .into_block() } @@ -1310,22 +1339,22 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { self.scopes.unwind_drops.add_entry_point(start, next_drop); } - /// Sets up a path that performs all required cleanup for dropping a - /// coroutine, starting from the given block that ends in - /// [TerminatorKind::Yield]. - /// - /// This path terminates in CoroutineDrop. - pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) { + /// Returns the [DropIdx] for the innermost drop for dropline (coroutine drop path). + /// The `DropIdx` will be created if it doesn't already exist. + fn diverge_dropline(&mut self) -> DropIdx { + // It is okay to use dummy span because the getting scope index on the topmost scope + // must always succeed. + self.diverge_dropline_target(self.scopes.topmost(), DUMMY_SP) + } + + /// Similar to diverge_cleanup_target, but for dropline (coroutine drop path) + fn diverge_dropline_target(&mut self, target_scope: region::Scope, span: Span) -> DropIdx { debug_assert!( - matches!( - self.cfg.block_data(yield_block).terminator().kind, - TerminatorKind::Yield { .. } - ), - "coroutine_drop_cleanup called on block with non-yield terminator." + self.coroutine.is_some(), + "diverge_dropline_target is valid only for coroutine" ); - let (uncached_scope, mut cached_drop) = self - .scopes - .scopes + let target = self.scopes.scope_index(target_scope, span); + let (uncached_scope, mut cached_drop) = self.scopes.scopes[..=target] .iter() .enumerate() .rev() @@ -1334,13 +1363,34 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { }) .unwrap_or((0, ROOT_NODE)); - for scope in &mut self.scopes.scopes[uncached_scope..] { + if uncached_scope > target { + return cached_drop; + } + + for scope in &mut self.scopes.scopes[uncached_scope..=target] { for drop in &scope.drops { cached_drop = self.scopes.coroutine_drops.add_drop(*drop, cached_drop); } scope.cached_coroutine_drop_block = Some(cached_drop); } + cached_drop + } + + /// Sets up a path that performs all required cleanup for dropping a + /// coroutine, starting from the given block that ends in + /// [TerminatorKind::Yield]. + /// + /// This path terminates in CoroutineDrop. + pub(crate) fn coroutine_drop_cleanup(&mut self, yield_block: BasicBlock) { + debug_assert!( + matches!( + self.cfg.block_data(yield_block).terminator().kind, + TerminatorKind::Yield { .. } + ), + "coroutine_drop_cleanup called on block with non-yield terminator." + ); + let cached_drop = self.diverge_dropline(); self.scopes.coroutine_drops.add_entry_point(yield_block, cached_drop); } @@ -1371,6 +1421,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { target: assign, unwind: UnwindAction::Cleanup(assign_unwind), replace: true, + drop: None, + async_fut: None, }, ); self.diverge_from(block); @@ -1432,18 +1484,26 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { /// * `unwind_to`, describes the drops that would occur at this point in the code if a /// panic occurred (a subset of the drops in `scope`, since we sometimes elide StorageDead and other /// instructions on unwinding) +/// * `dropline_to`, describes the drops that would occur at this point in the code if a +/// coroutine drop occured. /// * `storage_dead_on_unwind`, if true, then we should emit `StorageDead` even when unwinding /// * `arg_count`, number of MIR local variables corresponding to fn arguments (used to assert that we don't drop those) -fn build_scope_drops<'tcx>( +fn build_scope_drops<'tcx, F>( cfg: &mut CFG<'tcx>, unwind_drops: &mut DropTree, + coroutine_drops: &mut DropTree, scope: &Scope, block: BasicBlock, unwind_to: DropIdx, + dropline_to: Option<DropIdx>, storage_dead_on_unwind: bool, arg_count: usize, -) -> BlockAnd<()> { - debug!("build_scope_drops({:?} -> {:?})", block, scope); + is_async_drop: F, +) -> BlockAnd<()> +where + F: Fn(Local) -> bool, +{ + debug!("build_scope_drops({:?} -> {:?}), dropline_to={:?}", block, scope, dropline_to); // Build up the drops in evaluation order. The end result will // look like: @@ -1476,6 +1536,9 @@ fn build_scope_drops<'tcx>( // will branch to `drops[n]`. let mut block = block; + // `dropline_to` indicates what needs to be dropped should coroutine drop occur. + let mut dropline_to = dropline_to; + for drop_data in scope.drops.iter().rev() { let source_info = drop_data.source_info; let local = drop_data.local; @@ -1492,6 +1555,12 @@ fn build_scope_drops<'tcx>( debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); unwind_to = unwind_drops.drops[unwind_to].next; + if let Some(idx) = dropline_to { + debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drops[idx].next); + } + // If the operand has been moved, and we are not on an unwind // path, then don't generate the drop. (We only take this into // account for non-unwind paths so as not to disturb the @@ -1501,6 +1570,12 @@ fn build_scope_drops<'tcx>( } unwind_drops.add_entry_point(block, unwind_to); + if let Some(to) = dropline_to + && is_async_drop(local) + { + coroutine_drops.add_entry_point(block, to); + } + let next = cfg.start_new_block(); cfg.terminate( block, @@ -1510,6 +1585,8 @@ fn build_scope_drops<'tcx>( target: next, unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, ); block = next; @@ -1556,6 +1633,11 @@ fn build_scope_drops<'tcx>( debug_assert_eq!(unwind_drops.drops[unwind_to].data.kind, drop_data.kind); unwind_to = unwind_drops.drops[unwind_to].next; } + if let Some(idx) = dropline_to { + debug_assert_eq!(coroutine_drops.drops[idx].data.local, drop_data.local); + debug_assert_eq!(coroutine_drops.drops[idx].data.kind, drop_data.kind); + dropline_to = Some(coroutine_drops.drops[idx].next); + } // Only temps and vars need their storage dead. assert!(local.index() > arg_count); cfg.push(block, Statement { source_info, kind: StatementKind::StorageDead(local) }); @@ -1611,6 +1693,39 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { } } } + // Link the exit drop tree to dropline drop tree (coroutine drop path) for async drops + if is_coroutine + && drops.drops.iter().any(|DropNode { data, next: _ }| { + data.kind == DropKind::Value && self.is_async_drop(data.local) + }) + { + let dropline_target = self.diverge_dropline_target(else_scope, span); + let mut dropline_indices = IndexVec::from_elem_n(dropline_target, 1); + for (drop_idx, drop_data) in drops.drops.iter_enumerated().skip(1) { + match drop_data.data.kind { + DropKind::Storage | DropKind::ForLint => { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); + dropline_indices.push(coroutine_drop); + } + DropKind::Value => { + let coroutine_drop = self + .scopes + .coroutine_drops + .add_drop(drop_data.data, dropline_indices[drop_data.next]); + if self.is_async_drop(drop_data.data.local) { + self.scopes.coroutine_drops.add_entry_point( + blocks[drop_idx].unwrap(), + dropline_indices[drop_data.next], + ); + } + dropline_indices.push(coroutine_drop); + } + } + } + } blocks[ROOT_NODE].map(BasicBlock::unit) } @@ -1655,9 +1770,11 @@ impl<'a, 'tcx: 'a> Builder<'a, 'tcx> { // to be captured by the coroutine. I'm not sure how important this // optimization is, but it is here. for (drop_idx, drop_node) in drops.drops.iter_enumerated() { - if let DropKind::Value = drop_node.data.kind { + if let DropKind::Value = drop_node.data.kind + && let Some(bb) = blocks[drop_idx] + { debug_assert!(drop_node.next < drops.drops.next_index()); - drops.entry_points.push((drop_node.next, blocks[drop_idx].unwrap())); + drops.entry_points.push((drop_node.next, bb)); } } Self::build_unwind_tree(cfg, drops, fn_span, resume_block); @@ -1709,6 +1826,8 @@ impl<'tcx> DropTreeBuilder<'tcx> for CoroutineDrop { let term = cfg.block_data_mut(from).terminator_mut(); if let TerminatorKind::Yield { ref mut drop, .. } = term.kind { *drop = Some(to); + } else if let TerminatorKind::Drop { ref mut drop, .. } = term.kind { + *drop = Some(to); } else { span_bug!( term.source_info.span, diff --git a/compiler/rustc_mir_build/src/thir/pattern/mod.rs b/compiler/rustc_mir_build/src/thir/pattern/mod.rs index 8f058efdfac..8e69ff568b9 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/mod.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/mod.rs @@ -111,10 +111,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { let kind = match adjust.kind { PatAdjust::BuiltinDeref => PatKind::Deref { subpattern: thir_pat }, PatAdjust::OverloadedDeref => { - let mutable = self.typeck_results.pat_has_ref_mut_binding(pat); - let mutability = - if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - PatKind::DerefPattern { subpattern: thir_pat, mutability } + let borrow = self.typeck_results.deref_pat_borrow_mode(adjust.source, pat); + PatKind::DerefPattern { subpattern: thir_pat, borrow } } }; Box::new(Pat { span, ty: adjust.source, kind }) @@ -308,9 +306,8 @@ impl<'a, 'tcx> PatCtxt<'a, 'tcx> { } hir::PatKind::Deref(subpattern) => { - let mutable = self.typeck_results.pat_has_ref_mut_binding(subpattern); - let mutability = if mutable { hir::Mutability::Mut } else { hir::Mutability::Not }; - PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), mutability } + let borrow = self.typeck_results.deref_pat_borrow_mode(ty, subpattern); + PatKind::DerefPattern { subpattern: self.lower_pattern(subpattern), borrow } } hir::PatKind::Ref(subpattern, _) => { // Track the default binding mode for the Rust 2024 migration suggestion. diff --git a/compiler/rustc_mir_dataflow/src/impls/initialized.rs b/compiler/rustc_mir_dataflow/src/impls/initialized.rs index f5ffc42d52a..18165b0b9bd 100644 --- a/compiler/rustc_mir_dataflow/src/impls/initialized.rs +++ b/compiler/rustc_mir_dataflow/src/impls/initialized.rs @@ -376,7 +376,14 @@ impl<'tcx> Analysis<'tcx> for MaybeInitializedPlaces<'_, 'tcx> { // the result of `is_unwind_dead`. let mut edges = terminator.edges(); if self.skip_unreachable_unwind - && let mir::TerminatorKind::Drop { target, unwind, place, replace: _ } = terminator.kind + && let mir::TerminatorKind::Drop { + target, + unwind, + place, + replace: _, + drop: _, + async_fut: _, + } = terminator.kind && matches!(unwind, mir::UnwindAction::Cleanup(_)) && self.is_unwind_dead(place, state) { diff --git a/compiler/rustc_mir_dataflow/src/value_analysis.rs b/compiler/rustc_mir_dataflow/src/value_analysis.rs index 36fb1c2b36d..83fd8ccba60 100644 --- a/compiler/rustc_mir_dataflow/src/value_analysis.rs +++ b/compiler/rustc_mir_dataflow/src/value_analysis.rs @@ -405,6 +405,9 @@ impl<'tcx> Map<'tcx> { if exclude.contains(local) { continue; } + if decl.ty.is_async_drop_in_place_coroutine(tcx) { + continue; + } // Create a place for the local. debug_assert!(self.locals[local].is_none()); diff --git a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs index b33326cb873..a414d120e68 100644 --- a/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs +++ b/compiler/rustc_mir_transform/src/add_moves_for_packed_drops.rs @@ -83,7 +83,9 @@ fn add_move_for_packed_drop<'tcx>( is_cleanup: bool, ) { debug!("add_move_for_packed_drop({:?} @ {:?})", terminator, loc); - let TerminatorKind::Drop { ref place, target, unwind, replace } = terminator.kind else { + let TerminatorKind::Drop { ref place, target, unwind, replace, drop, async_fut } = + terminator.kind + else { unreachable!(); }; @@ -106,6 +108,8 @@ fn add_move_for_packed_drop<'tcx>( target: storage_dead_block, unwind, replace, + drop, + async_fut, }, ); } diff --git a/compiler/rustc_mir_transform/src/coroutine.rs b/compiler/rustc_mir_transform/src/coroutine.rs index 0eed46c72f9..263f0c40f5a 100644 --- a/compiler/rustc_mir_transform/src/coroutine.rs +++ b/compiler/rustc_mir_transform/src/coroutine.rs @@ -51,9 +51,15 @@ //! Otherwise it drops all the values in scope at the last suspension point. mod by_move_body; +mod drop; use std::{iter, ops}; pub(super) use by_move_body::coroutine_by_move_body_def_id; +use drop::{ + cleanup_async_drops, create_coroutine_drop_shim, create_coroutine_drop_shim_async, + create_coroutine_drop_shim_proxy_async, elaborate_coroutine_drops, expand_async_drops, + has_expandable_async_drops, insert_clean_drop, +}; use rustc_abi::{FieldIdx, VariantIdx}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::pluralize; @@ -64,6 +70,7 @@ use rustc_index::bit_set::{BitMatrix, DenseBitSet, GrowableBitSet}; use rustc_index::{Idx, IndexVec}; use rustc_middle::mir::visit::{MutVisitor, PlaceContext, Visitor}; use rustc_middle::mir::*; +use rustc_middle::ty::util::Discr; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, GenericArgsRef, InstanceKind, Ty, TyCtxt, TypingMode, }; @@ -74,7 +81,9 @@ use rustc_mir_dataflow::impls::{ }; use rustc_mir_dataflow::{Analysis, Results, ResultsVisitor}; use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::{Span, sym}; +use rustc_span::source_map::dummy_spanned; +use rustc_span::symbol::sym; +use rustc_span::{DUMMY_SP, Span}; use rustc_target::spec::PanicStrategy; use rustc_trait_selection::error_reporting::InferCtxtErrorExt; use rustc_trait_selection::infer::TyCtxtInferExt as _; @@ -159,6 +168,7 @@ fn replace_base<'tcx>(place: &mut Place<'tcx>, new_base: Place<'tcx>, tcx: TyCtx } const SELF_ARG: Local = Local::from_u32(1); +const CTX_ARG: Local = Local::from_u32(2); /// A `yield` point in the coroutine. struct SuspensionPoint<'tcx> { @@ -539,11 +549,11 @@ fn replace_local<'tcx>( /// The async lowering step and the type / lifetime inference / checking are /// still using the `ResumeTy` indirection for the time being, and that indirection /// is removed here. After this transform, the coroutine body only knows about `&mut Context<'_>`. -fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> Ty<'tcx> { let context_mut_ref = Ty::new_task_context(tcx); // replace the type of the `resume` argument - replace_resume_ty_local(tcx, body, Local::new(2), context_mut_ref); + replace_resume_ty_local(tcx, body, CTX_ARG, context_mut_ref); let get_context_def_id = tcx.require_lang_item(LangItem::GetContext, None); @@ -569,6 +579,7 @@ fn transform_async_context<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { _ => {} } } + context_mut_ref } fn eliminate_get_context_call<'tcx>(bb_data: &mut BasicBlockData<'tcx>) -> Local { @@ -1036,9 +1047,8 @@ fn insert_switch<'tcx>( body: &mut Body<'tcx>, cases: Vec<(usize, BasicBlock)>, transform: &TransformVisitor<'tcx>, - default: TerminatorKind<'tcx>, + default_block: BasicBlock, ) { - let default_block = insert_term_block(body, default); let (assign, discr) = transform.get_discr(body); let switch_targets = SwitchTargets::new(cases.iter().map(|(i, bb)| ((*i) as u128, *bb)), default_block); @@ -1061,129 +1071,39 @@ fn insert_switch<'tcx>( } } -fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { - use crate::elaborate_drop::{Unwind, elaborate_drop}; - use crate::patch::MirPatch; - use crate::shim::DropShimElaborator; - - // Note that `elaborate_drops` only drops the upvars of a coroutine, and - // this is ok because `open_drop` can only be reached within that own - // coroutine's resume function. - let typing_env = body.typing_env(tcx); - - let mut elaborator = DropShimElaborator { body, patch: MirPatch::new(body), tcx, typing_env }; - - for (block, block_data) in body.basic_blocks.iter_enumerated() { - let (target, unwind, source_info) = match block_data.terminator() { - Terminator { - source_info, - kind: TerminatorKind::Drop { place, target, unwind, replace: _ }, - } => { - if let Some(local) = place.as_local() - && local == SELF_ARG - { - (target, unwind, source_info) - } else { - continue; - } - } - _ => continue, - }; - let unwind = if block_data.is_cleanup { - Unwind::InCleanup - } else { - Unwind::To(match *unwind { - UnwindAction::Cleanup(tgt) => tgt, - UnwindAction::Continue => elaborator.patch.resume_block(), - UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(), - UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason), - }) - }; - elaborate_drop( - &mut elaborator, - *source_info, - Place::from(SELF_ARG), - (), - *target, - unwind, - block, - ); - } - elaborator.patch.apply(body); -} - -fn create_coroutine_drop_shim<'tcx>( - tcx: TyCtxt<'tcx>, - transform: &TransformVisitor<'tcx>, - coroutine_ty: Ty<'tcx>, - body: &Body<'tcx>, - drop_clean: BasicBlock, -) -> Body<'tcx> { - let mut body = body.clone(); - // Take the coroutine info out of the body, since the drop shim is - // not a coroutine body itself; it just has its drop built out of it. - let _ = body.coroutine.take(); - // Make sure the resume argument is not included here, since we're - // building a body for `drop_in_place`. - body.arg_count = 1; - +fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); + body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false, + }) +} - let mut cases = create_cases(&mut body, transform, Operation::Drop); - - cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); - - // The returned state and the poisoned state fall through to the default - // case which is just to return - - insert_switch(&mut body, cases, transform, TerminatorKind::Return); - - for block in body.basic_blocks_mut() { - let kind = &mut block.terminator_mut().kind; - if let TerminatorKind::CoroutineDrop = *kind { - *kind = TerminatorKind::Return; - } +fn return_poll_ready_assign<'tcx>(tcx: TyCtxt<'tcx>, source_info: SourceInfo) -> Statement<'tcx> { + // Poll::Ready(()) + let poll_def_id = tcx.require_lang_item(LangItem::Poll, None); + let args = tcx.mk_args(&[tcx.types.unit.into()]); + let val = Operand::Constant(Box::new(ConstOperand { + span: source_info.span, + user_ty: None, + const_: Const::zero_sized(tcx.types.unit), + })); + let ready_val = Rvalue::Aggregate( + Box::new(AggregateKind::Adt(poll_def_id, VariantIdx::from_usize(0), args, None, None)), + IndexVec::from_raw(vec![val]), + ); + Statement { + kind: StatementKind::Assign(Box::new((Place::return_place(), ready_val))), + source_info, } - - // Replace the return variable - body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); - - make_coroutine_state_argument_indirect(tcx, &mut body); - - // Change the coroutine argument from &mut to *mut - body.local_decls[SELF_ARG] = - LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); - - // Make sure we remove dead blocks to remove - // unrelated code from the resume part of the function - simplify::remove_dead_blocks(&mut body); - - // Update the body's def to become the drop glue. - let coroutine_instance = body.source.instance; - let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); - let drop_instance = InstanceKind::DropGlue(drop_in_place, Some(coroutine_ty)); - - // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible - // filename. - body.source.instance = coroutine_instance; - dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); - body.source.instance = drop_instance; - - // Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)` - // but the pass manager doesn't update the phase of the coroutine drop shim. Update the - // phase of the drop shim so that later on when we run the pass manager on the shim, in - // the `mir_shims` query, we don't ICE on the intra-pass validation before we've updated - // the phase of the body from analysis. - body.phase = MirPhase::Runtime(RuntimePhase::Initial); - - body } -fn insert_term_block<'tcx>(body: &mut Body<'tcx>, kind: TerminatorKind<'tcx>) -> BasicBlock { +fn insert_poll_ready_block<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) -> BasicBlock { let source_info = SourceInfo::outermost(body.span); body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind }), + statements: [return_poll_ready_assign(tcx, source_info)].to_vec(), + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), is_cleanup: false, }) } @@ -1263,45 +1183,50 @@ fn can_unwind<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) -> bool { false } +// Poison the coroutine when it unwinds +fn generate_poison_block_and_redirect_unwinds_there<'tcx>( + transform: &TransformVisitor<'tcx>, + body: &mut Body<'tcx>, +) { + let source_info = SourceInfo::outermost(body.span); + let poison_block = body.basic_blocks_mut().push(BasicBlockData { + statements: vec![ + transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), + ], + terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), + is_cleanup: true, + }); + + for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { + let source_info = block.terminator().source_info; + + if let TerminatorKind::UnwindResume = block.terminator().kind { + // An existing `Resume` terminator is redirected to jump to our dedicated + // "poisoning block" above. + if idx != poison_block { + *block.terminator_mut() = + Terminator { source_info, kind: TerminatorKind::Goto { target: poison_block } }; + } + } else if !block.is_cleanup + // Any terminators that *can* unwind but don't have an unwind target set are also + // pointed at our poisoning block (unless they're part of the cleanup path). + && let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() + { + *unwind = UnwindAction::Cleanup(poison_block); + } + } +} + fn create_coroutine_resume_function<'tcx>( tcx: TyCtxt<'tcx>, transform: TransformVisitor<'tcx>, body: &mut Body<'tcx>, can_return: bool, + can_unwind: bool, ) { - let can_unwind = can_unwind(tcx, body); - // Poison the coroutine when it unwinds if can_unwind { - let source_info = SourceInfo::outermost(body.span); - let poison_block = body.basic_blocks_mut().push(BasicBlockData { - statements: vec![ - transform.set_discr(VariantIdx::new(CoroutineArgs::POISONED), source_info), - ], - terminator: Some(Terminator { source_info, kind: TerminatorKind::UnwindResume }), - is_cleanup: true, - }); - - for (idx, block) in body.basic_blocks_mut().iter_enumerated_mut() { - let source_info = block.terminator().source_info; - - if let TerminatorKind::UnwindResume = block.terminator().kind { - // An existing `Resume` terminator is redirected to jump to our dedicated - // "poisoning block" above. - if idx != poison_block { - *block.terminator_mut() = Terminator { - source_info, - kind: TerminatorKind::Goto { target: poison_block }, - }; - } - } else if !block.is_cleanup - // Any terminators that *can* unwind but don't have an unwind target set are also - // pointed at our poisoning block (unless they're part of the cleanup path). - && let Some(unwind @ UnwindAction::Continue) = block.terminator_mut().unwind_mut() - { - *unwind = UnwindAction::Cleanup(poison_block); - } - } + generate_poison_block_and_redirect_unwinds_there(&transform, body); } let mut cases = create_cases(body, &transform, Operation::Resume); @@ -1326,7 +1251,13 @@ fn create_coroutine_resume_function<'tcx>( let block = match transform.coroutine_kind { CoroutineKind::Desugared(CoroutineDesugaring::Async, _) | CoroutineKind::Coroutine(_) => { - insert_panic_block(tcx, body, ResumedAfterReturn(transform.coroutine_kind)) + // For `async_drop_in_place<T>::{closure}` we just keep return Poll::Ready, + // because async drop of such coroutine keeps polling original coroutine + if tcx.is_async_drop_in_place_coroutine(body.source.def_id()) { + insert_poll_ready_block(tcx, body) + } else { + insert_panic_block(tcx, body, ResumedAfterReturn(transform.coroutine_kind)) + } } CoroutineKind::Desugared(CoroutineDesugaring::AsyncGen, _) | CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => { @@ -1336,7 +1267,8 @@ fn create_coroutine_resume_function<'tcx>( cases.insert(1, (CoroutineArgs::RETURNED, block)); } - insert_switch(body, cases, &transform, TerminatorKind::Unreachable); + let default_block = insert_term_block(body, TerminatorKind::Unreachable); + insert_switch(body, cases, &transform, default_block); make_coroutine_state_argument_indirect(tcx, body); @@ -1360,25 +1292,6 @@ fn create_coroutine_resume_function<'tcx>( dump_mir(tcx, false, "coroutine_resume", &0, body, |_, _| Ok(())); } -fn insert_clean_drop(body: &mut Body<'_>) -> BasicBlock { - let return_block = insert_term_block(body, TerminatorKind::Return); - - let term = TerminatorKind::Drop { - place: Place::from(SELF_ARG), - target: return_block, - unwind: UnwindAction::Continue, - replace: false, - }; - let source_info = SourceInfo::outermost(body.span); - - // Create a block to destroy an unresumed coroutines. This can only destroy upvars. - body.basic_blocks_mut().push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { source_info, kind: term }), - is_cleanup: false, - }) -} - /// An operation that can be performed on a coroutine. #[derive(PartialEq, Copy, Clone)] enum Operation { @@ -1423,7 +1336,7 @@ fn create_cases<'tcx>( if operation == Operation::Resume { // Move the resume argument to the destination place of the `Yield` terminator - let resume_arg = Local::new(2); // 0 = return, 1 = self + let resume_arg = CTX_ARG; statements.push(Statement { source_info, kind: StatementKind::Assign(Box::new(( @@ -1530,7 +1443,9 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { }; let old_ret_ty = body.return_ty(); - assert!(body.coroutine_drop().is_none()); + assert!(body.coroutine_drop().is_none() && body.coroutine_drop_async().is_none()); + + dump_mir(tcx, false, "coroutine_before", &0, body, |_, _| Ok(())); // The first argument is the coroutine type passed by value let coroutine_ty = body.local_decls.raw[1].ty; @@ -1574,19 +1489,32 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { // RETURN_PLACE then is a fresh unused local with type ret_ty. let old_ret_local = replace_local(RETURN_PLACE, new_ret_ty, body, tcx); + // We need to insert clean drop for unresumed state and perform drop elaboration + // (finally in open_drop_for_tuple) before async drop expansion. + // Async drops, produced by this drop elaboration, will be expanded, + // and corresponding futures kept in layout. + let has_async_drops = matches!( + coroutine_kind, + CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) + ) && has_expandable_async_drops(tcx, body, coroutine_ty); + // Replace all occurrences of `ResumeTy` with `&mut Context<'_>` within async bodies. if matches!( coroutine_kind, CoroutineKind::Desugared(CoroutineDesugaring::Async | CoroutineDesugaring::AsyncGen, _) ) { - transform_async_context(tcx, body); + let context_mut_ref = transform_async_context(tcx, body); + expand_async_drops(tcx, body, context_mut_ref, coroutine_kind, coroutine_ty); + dump_mir(tcx, false, "coroutine_async_drop_expand", &0, body, |_, _| Ok(())); + } else { + cleanup_async_drops(body); } // We also replace the resume argument and insert an `Assign`. // This is needed because the resume argument `_2` might be live across a `yield`, in which // case there is no `Assign` to it that the transform can turn into a store to the coroutine // state. After the yield the slot in the coroutine state would then be uninitialized. - let resume_local = Local::new(2); + let resume_local = CTX_ARG; let resume_ty = body.local_decls[resume_local].ty; let old_resume_local = replace_local(resume_local, resume_ty, body, tcx); @@ -1667,10 +1595,14 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { body.coroutine.as_mut().unwrap().resume_ty = None; body.coroutine.as_mut().unwrap().coroutine_layout = Some(layout); + // FIXME: Drops, produced by insert_clean_drop + elaborate_coroutine_drops, + // are currently sync only. To allow async for them, we need to move those calls + // before expand_async_drops, and fix the related problems. + // // Insert `drop(coroutine_struct)` which is used to drop upvars for coroutines in // the unresumed state. // This is expanded to a drop ladder in `elaborate_coroutine_drops`. - let drop_clean = insert_clean_drop(body); + let drop_clean = insert_clean_drop(tcx, body, has_async_drops); dump_mir(tcx, false, "coroutine_pre-elab", &0, body, |_, _| Ok(())); @@ -1681,13 +1613,32 @@ impl<'tcx> crate::MirPass<'tcx> for StateTransform { dump_mir(tcx, false, "coroutine_post-transform", &0, body, |_, _| Ok(())); - // Create a copy of our MIR and use it to create the drop shim for the coroutine - let drop_shim = create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); + let can_unwind = can_unwind(tcx, body); - body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim); + // Create a copy of our MIR and use it to create the drop shim for the coroutine + if has_async_drops { + // If coroutine has async drops, generating async drop shim + let mut drop_shim = + create_coroutine_drop_shim_async(tcx, &transform, body, drop_clean, can_unwind); + // Run derefer to fix Derefs that are not in the first place + deref_finder(tcx, &mut drop_shim); + body.coroutine.as_mut().unwrap().coroutine_drop_async = Some(drop_shim); + } else { + // If coroutine has no async drops, generating sync drop shim + let mut drop_shim = + create_coroutine_drop_shim(tcx, &transform, coroutine_ty, body, drop_clean); + // Run derefer to fix Derefs that are not in the first place + deref_finder(tcx, &mut drop_shim); + body.coroutine.as_mut().unwrap().coroutine_drop = Some(drop_shim); + + // For coroutine with sync drop, generating async proxy for `future_drop_poll` call + let mut proxy_shim = create_coroutine_drop_shim_proxy_async(tcx, body); + deref_finder(tcx, &mut proxy_shim); + body.coroutine.as_mut().unwrap().coroutine_drop_proxy_async = Some(proxy_shim); + } // Create the Coroutine::resume / Future::poll function - create_coroutine_resume_function(tcx, transform, body, can_return); + create_coroutine_resume_function(tcx, transform, body, can_return, can_unwind); // Run derefer to fix Derefs that are not in the first place deref_finder(tcx, body); diff --git a/compiler/rustc_mir_transform/src/coroutine/drop.rs b/compiler/rustc_mir_transform/src/coroutine/drop.rs new file mode 100644 index 00000000000..6b266da5a69 --- /dev/null +++ b/compiler/rustc_mir_transform/src/coroutine/drop.rs @@ -0,0 +1,725 @@ +//! Drops and async drops related logic for coroutine transformation pass + +use super::*; + +// Fix return Poll<Rv>::Pending statement into Poll<()>::Pending for async drop function +struct FixReturnPendingVisitor<'tcx> { + tcx: TyCtxt<'tcx>, +} + +impl<'tcx> MutVisitor<'tcx> for FixReturnPendingVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_assign( + &mut self, + place: &mut Place<'tcx>, + rvalue: &mut Rvalue<'tcx>, + _location: Location, + ) { + if place.local != RETURN_PLACE { + return; + } + + // Converting `_0 = Poll::<Rv>::Pending` to `_0 = Poll::<()>::Pending` + if let Rvalue::Aggregate(kind, _) = rvalue { + if let AggregateKind::Adt(_, _, ref mut args, _, _) = **kind { + *args = self.tcx.mk_args(&[self.tcx.types.unit.into()]); + } + } + } +} + +// rv = call fut.poll() +fn build_poll_call<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + poll_unit_place: &Place<'tcx>, + switch_block: BasicBlock, + fut_pin_place: &Place<'tcx>, + fut_ty: Ty<'tcx>, + context_ref_place: &Place<'tcx>, + unwind: UnwindAction, +) -> BasicBlock { + let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, None); + let poll_fn = Ty::new_fn_def(tcx, poll_fn, [fut_ty]); + let poll_fn = Operand::Constant(Box::new(ConstOperand { + span: DUMMY_SP, + user_ty: None, + const_: Const::zero_sized(poll_fn), + })); + let call = TerminatorKind::Call { + func: poll_fn.clone(), + args: [ + dummy_spanned(Operand::Move(*fut_pin_place)), + dummy_spanned(Operand::Move(*context_ref_place)), + ] + .into(), + destination: *poll_unit_place, + target: Some(switch_block), + unwind, + call_source: CallSource::Misc, + fn_span: DUMMY_SP, + }; + insert_term_block(body, call) +} + +// pin_fut = Pin::new_unchecked(&mut fut) +fn build_pin_fut<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + fut_place: Place<'tcx>, + unwind: UnwindAction, +) -> (BasicBlock, Place<'tcx>) { + let span = body.span; + let source_info = SourceInfo::outermost(span); + let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; + let fut_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, fut_ty); + let fut_ref_place = Place::from(body.local_decls.push(LocalDecl::new(fut_ref_ty, span))); + let pin_fut_new_unchecked_fn = Ty::new_fn_def( + tcx, + tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)), + [fut_ref_ty], + ); + let fut_pin_ty = pin_fut_new_unchecked_fn.fn_sig(tcx).output().skip_binder(); + let fut_pin_place = Place::from(body.local_decls.push(LocalDecl::new(fut_pin_ty, span))); + let pin_fut_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: Const::zero_sized(pin_fut_new_unchecked_fn), + })); + + let storage_live = + Statement { source_info, kind: StatementKind::StorageLive(fut_pin_place.local) }; + + let fut_ref_assign = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + fut_ref_place, + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + fut_place, + ), + ))), + }; + + // call Pin<FutTy>::new_unchecked(&mut fut) + let pin_fut_bb = body.basic_blocks_mut().push(BasicBlockData { + statements: [storage_live, fut_ref_assign].to_vec(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: pin_fut_new_unchecked_fn, + args: [dummy_spanned(Operand::Move(fut_ref_place))].into(), + destination: fut_pin_place, + target: None, // will be fixed later + unwind, + call_source: CallSource::Misc, + fn_span: span, + }, + }), + is_cleanup: false, + }); + (pin_fut_bb, fut_pin_place) +} + +// Build Poll switch for async drop +// match rv { +// Ready() => ready_block +// Pending => yield_block +//} +fn build_poll_switch<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + poll_enum: Ty<'tcx>, + poll_unit_place: &Place<'tcx>, + ready_block: BasicBlock, + yield_block: BasicBlock, +) -> BasicBlock { + let poll_enum_adt = poll_enum.ty_adt_def().unwrap(); + + let Discr { val: poll_ready_discr, ty: poll_discr_ty } = poll_enum + .discriminant_for_variant( + tcx, + poll_enum_adt.variant_index_with_id(tcx.require_lang_item(LangItem::PollReady, None)), + ) + .unwrap(); + let poll_pending_discr = poll_enum + .discriminant_for_variant( + tcx, + poll_enum_adt.variant_index_with_id(tcx.require_lang_item(LangItem::PollPending, None)), + ) + .unwrap() + .val; + let source_info = SourceInfo::outermost(body.span); + let poll_discr_place = + Place::from(body.local_decls.push(LocalDecl::new(poll_discr_ty, source_info.span))); + let discr_assign = Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + poll_discr_place, + Rvalue::Discriminant(*poll_unit_place), + ))), + }; + let unreachable_block = insert_term_block(body, TerminatorKind::Unreachable); + body.basic_blocks_mut().push(BasicBlockData { + statements: [discr_assign].to_vec(), + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::SwitchInt { + discr: Operand::Move(poll_discr_place), + targets: SwitchTargets::new( + [(poll_ready_discr, ready_block), (poll_pending_discr, yield_block)] + .into_iter(), + unreachable_block, + ), + }, + }), + is_cleanup: false, + }) +} + +// Gather blocks, reachable through 'drop' targets of Yield and Drop terminators (chained) +fn gather_dropline_blocks<'tcx>(body: &mut Body<'tcx>) -> DenseBitSet<BasicBlock> { + let mut dropline: DenseBitSet<BasicBlock> = DenseBitSet::new_empty(body.basic_blocks.len()); + for (bb, data) in traversal::reverse_postorder(body) { + if dropline.contains(bb) { + data.terminator().successors().for_each(|v| { + dropline.insert(v); + }); + } else { + match data.terminator().kind { + TerminatorKind::Yield { drop: Some(v), .. } => { + dropline.insert(v); + } + TerminatorKind::Drop { drop: Some(v), .. } => { + dropline.insert(v); + } + _ => (), + } + } + } + dropline +} + +/// Cleanup all async drops (reset to sync) +pub(super) fn cleanup_async_drops<'tcx>(body: &mut Body<'tcx>) { + for block in body.basic_blocks_mut() { + if let TerminatorKind::Drop { + place: _, + target: _, + unwind: _, + replace: _, + ref mut drop, + ref mut async_fut, + } = block.terminator_mut().kind + { + if drop.is_some() || async_fut.is_some() { + *drop = None; + *async_fut = None; + } + } + } +} + +pub(super) fn has_expandable_async_drops<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + coroutine_ty: Ty<'tcx>, +) -> bool { + for bb in START_BLOCK..body.basic_blocks.next_index() { + // Drops in unwind path (cleanup blocks) are not expanded to async drops, only sync drops in unwind path + if body[bb].is_cleanup { + continue; + } + let TerminatorKind::Drop { place, target: _, unwind: _, replace: _, drop: _, async_fut } = + body[bb].terminator().kind + else { + continue; + }; + let place_ty = place.ty(&body.local_decls, tcx).ty; + if place_ty == coroutine_ty { + continue; + } + if async_fut.is_none() { + continue; + } + return true; + } + return false; +} + +/// Expand Drop terminator for async drops into mainline poll-switch and dropline poll-switch +pub(super) fn expand_async_drops<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + context_mut_ref: Ty<'tcx>, + coroutine_kind: hir::CoroutineKind, + coroutine_ty: Ty<'tcx>, +) { + let dropline = gather_dropline_blocks(body); + // Clean drop and async_fut fields if potentially async drop is not expanded (stays sync) + let remove_asyncness = |block: &mut BasicBlockData<'tcx>| { + if let TerminatorKind::Drop { + place: _, + target: _, + unwind: _, + replace: _, + ref mut drop, + ref mut async_fut, + } = block.terminator_mut().kind + { + *drop = None; + *async_fut = None; + } + }; + for bb in START_BLOCK..body.basic_blocks.next_index() { + // Drops in unwind path (cleanup blocks) are not expanded to async drops, only sync drops in unwind path + if body[bb].is_cleanup { + remove_asyncness(&mut body[bb]); + continue; + } + let TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut } = + body[bb].terminator().kind + else { + continue; + }; + + let place_ty = place.ty(&body.local_decls, tcx).ty; + if place_ty == coroutine_ty { + remove_asyncness(&mut body[bb]); + continue; + } + + let Some(fut_local) = async_fut else { + remove_asyncness(&mut body[bb]); + continue; + }; + + let is_dropline_bb = dropline.contains(bb); + + if !is_dropline_bb && drop.is_none() { + remove_asyncness(&mut body[bb]); + continue; + } + + let fut_place = Place::from(fut_local); + let fut_ty = fut_place.ty(&body.local_decls, tcx).ty; + + // poll-code: + // state_call_drop: + // #bb_pin: fut_pin = Pin<FutT>::new_unchecked(&mut fut) + // #bb_call: rv = call fut.poll() (or future_drop_poll(fut) for internal future drops) + // #bb_check: match (rv) + // pending => return rv (yield) + // ready => *continue_bb|drop_bb* + + // Compute Poll<> (aka Poll with void return) + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + let poll_decl = LocalDecl::new(poll_enum, body.span); + let poll_unit_place = Place::from(body.local_decls.push(poll_decl)); + + // First state-loop yield for mainline + let context_ref_place = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span))); + let source_info = body[bb].terminator.as_ref().unwrap().source_info; + let arg = Rvalue::Use(Operand::Move(Place::from(CTX_ARG))); + body[bb].statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new((context_ref_place, arg))), + }); + let yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield + let switch_block = + build_poll_switch(tcx, body, poll_enum, &poll_unit_place, target, yield_block); + let (pin_bb, fut_pin_place) = + build_pin_fut(tcx, body, fut_place.clone(), UnwindAction::Continue); + let call_bb = build_poll_call( + tcx, + body, + &poll_unit_place, + switch_block, + &fut_pin_place, + fut_ty, + &context_ref_place, + unwind, + ); + + // Second state-loop yield for transition to dropline (when coroutine async drop started) + let mut dropline_transition_bb: Option<BasicBlock> = None; + let mut dropline_yield_bb: Option<BasicBlock> = None; + let mut dropline_context_ref: Option<Place<'_>> = None; + let mut dropline_call_bb: Option<BasicBlock> = None; + if !is_dropline_bb { + let context_ref_place2: Place<'_> = + Place::from(body.local_decls.push(LocalDecl::new(context_mut_ref, body.span))); + let drop_yield_block = insert_term_block(body, TerminatorKind::Unreachable); // `kind` replaced later to yield + let drop_switch_block = build_poll_switch( + tcx, + body, + poll_enum, + &poll_unit_place, + drop.unwrap(), + drop_yield_block, + ); + let (pin_bb2, fut_pin_place2) = + build_pin_fut(tcx, body, fut_place, UnwindAction::Continue); + let drop_call_bb = build_poll_call( + tcx, + body, + &poll_unit_place, + drop_switch_block, + &fut_pin_place2, + fut_ty, + &context_ref_place2, + unwind, + ); + dropline_transition_bb = Some(pin_bb2); + dropline_yield_bb = Some(drop_yield_block); + dropline_context_ref = Some(context_ref_place2); + dropline_call_bb = Some(drop_call_bb); + } + + // value needed only for return-yields or gen-coroutines, so just const here + let value = Operand::Constant(Box::new(ConstOperand { + span: body.span, + user_ty: None, + const_: Const::from_bool(tcx, false), + })); + use rustc_middle::mir::AssertKind::ResumedAfterDrop; + let panic_bb = insert_panic_block(tcx, body, ResumedAfterDrop(coroutine_kind)); + + if is_dropline_bb { + body[yield_block].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: panic_bb, + resume_arg: context_ref_place, + drop: Some(pin_bb), + }; + } else { + body[yield_block].terminator_mut().kind = TerminatorKind::Yield { + value: value.clone(), + resume: pin_bb, + resume_arg: context_ref_place, + drop: dropline_transition_bb, + }; + body[dropline_yield_bb.unwrap()].terminator_mut().kind = TerminatorKind::Yield { + value, + resume: panic_bb, + resume_arg: dropline_context_ref.unwrap(), + drop: dropline_transition_bb, + }; + } + + if let TerminatorKind::Call { ref mut target, .. } = body[pin_bb].terminator_mut().kind { + *target = Some(call_bb); + } else { + bug!() + } + if !is_dropline_bb { + if let TerminatorKind::Call { ref mut target, .. } = + body[dropline_transition_bb.unwrap()].terminator_mut().kind + { + *target = dropline_call_bb; + } else { + bug!() + } + } + + body[bb].terminator_mut().kind = TerminatorKind::Goto { target: pin_bb }; + } +} + +pub(super) fn elaborate_coroutine_drops<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { + use crate::elaborate_drop::{Unwind, elaborate_drop}; + use crate::patch::MirPatch; + use crate::shim::DropShimElaborator; + + // Note that `elaborate_drops` only drops the upvars of a coroutine, and + // this is ok because `open_drop` can only be reached within that own + // coroutine's resume function. + let typing_env = body.typing_env(tcx); + + let mut elaborator = DropShimElaborator { + body, + patch: MirPatch::new(body), + tcx, + typing_env, + produce_async_drops: false, + }; + + for (block, block_data) in body.basic_blocks.iter_enumerated() { + let (target, unwind, source_info, dropline) = match block_data.terminator() { + Terminator { + source_info, + kind: TerminatorKind::Drop { place, target, unwind, replace: _, drop, async_fut: _ }, + } => { + if let Some(local) = place.as_local() + && local == SELF_ARG + { + (target, unwind, source_info, *drop) + } else { + continue; + } + } + _ => continue, + }; + let unwind = if block_data.is_cleanup { + Unwind::InCleanup + } else { + Unwind::To(match *unwind { + UnwindAction::Cleanup(tgt) => tgt, + UnwindAction::Continue => elaborator.patch.resume_block(), + UnwindAction::Unreachable => elaborator.patch.unreachable_cleanup_block(), + UnwindAction::Terminate(reason) => elaborator.patch.terminate_block(reason), + }) + }; + elaborate_drop( + &mut elaborator, + *source_info, + Place::from(SELF_ARG), + (), + *target, + unwind, + block, + dropline, + ); + } + elaborator.patch.apply(body); +} + +pub(super) fn insert_clean_drop<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + has_async_drops: bool, +) -> BasicBlock { + let source_info = SourceInfo::outermost(body.span); + let return_block = if has_async_drops { + insert_poll_ready_block(tcx, body) + } else { + insert_term_block(body, TerminatorKind::Return) + }; + + // FIXME: When move insert_clean_drop + elaborate_coroutine_drops before async drops expand, + // also set dropline here: + // let dropline = if has_async_drops { Some(return_block) } else { None }; + let dropline = None; + + let term = TerminatorKind::Drop { + place: Place::from(SELF_ARG), + target: return_block, + unwind: UnwindAction::Continue, + replace: false, + drop: dropline, + async_fut: None, + }; + + // Create a block to destroy an unresumed coroutines. This can only destroy upvars. + body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: Some(Terminator { source_info, kind: term }), + is_cleanup: false, + }) +} + +pub(super) fn create_coroutine_drop_shim<'tcx>( + tcx: TyCtxt<'tcx>, + transform: &TransformVisitor<'tcx>, + coroutine_ty: Ty<'tcx>, + body: &Body<'tcx>, + drop_clean: BasicBlock, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + // Make sure the resume argument is not included here, since we're + // building a body for `drop_in_place`. + body.arg_count = 1; + + let source_info = SourceInfo::outermost(body.span); + + let mut cases = create_cases(&mut body, transform, Operation::Drop); + + cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); + + // The returned state and the poisoned state fall through to the default + // case which is just to return + + let default_block = insert_term_block(&mut body, TerminatorKind::Return); + insert_switch(&mut body, cases, transform, default_block); + + for block in body.basic_blocks_mut() { + let kind = &mut block.terminator_mut().kind; + if let TerminatorKind::CoroutineDrop = *kind { + *kind = TerminatorKind::Return; + } + } + + // Replace the return variable + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(tcx.types.unit, source_info); + + make_coroutine_state_argument_indirect(tcx, &mut body); + + // Change the coroutine argument from &mut to *mut + body.local_decls[SELF_ARG] = + LocalDecl::with_source_info(Ty::new_mut_ptr(tcx, coroutine_ty), source_info); + + // Make sure we remove dead blocks to remove + // unrelated code from the resume part of the function + simplify::remove_dead_blocks(&mut body); + + // Update the body's def to become the drop glue. + let coroutine_instance = body.source.instance; + let drop_in_place = tcx.require_lang_item(LangItem::DropInPlace, None); + let drop_instance = InstanceKind::DropGlue(drop_in_place, Some(coroutine_ty)); + + // Temporary change MirSource to coroutine's instance so that dump_mir produces more sensible + // filename. + body.source.instance = coroutine_instance; + dump_mir(tcx, false, "coroutine_drop", &0, &body, |_, _| Ok(())); + body.source.instance = drop_instance; + + // Creating a coroutine drop shim happens on `Analysis(PostCleanup) -> Runtime(Initial)` + // but the pass manager doesn't update the phase of the coroutine drop shim. Update the + // phase of the drop shim so that later on when we run the pass manager on the shim, in + // the `mir_shims` query, we don't ICE on the intra-pass validation before we've updated + // the phase of the body from analysis. + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + + body +} + +// Create async drop shim function to drop coroutine itself +pub(super) fn create_coroutine_drop_shim_async<'tcx>( + tcx: TyCtxt<'tcx>, + transform: &TransformVisitor<'tcx>, + body: &Body<'tcx>, + drop_clean: BasicBlock, + can_unwind: bool, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + + FixReturnPendingVisitor { tcx }.visit_body(&mut body); + + // Poison the coroutine when it unwinds + if can_unwind { + generate_poison_block_and_redirect_unwinds_there(transform, &mut body); + } + + let source_info = SourceInfo::outermost(body.span); + + let mut cases = create_cases(&mut body, transform, Operation::Drop); + + cases.insert(0, (CoroutineArgs::UNRESUMED, drop_clean)); + + use rustc_middle::mir::AssertKind::ResumedAfterPanic; + // Panic when resumed on the returned or poisoned state + if can_unwind { + cases.insert( + 1, + ( + CoroutineArgs::POISONED, + insert_panic_block(tcx, &mut body, ResumedAfterPanic(transform.coroutine_kind)), + ), + ); + } + + // RETURNED state also goes to default_block with `return Ready<()>`. + // For fully-polled coroutine, async drop has nothing to do. + let default_block = insert_poll_ready_block(tcx, &mut body); + insert_switch(&mut body, cases, transform, default_block); + + for block in body.basic_blocks_mut() { + let kind = &mut block.terminator_mut().kind; + if let TerminatorKind::CoroutineDrop = *kind { + *kind = TerminatorKind::Return; + block.statements.push(return_poll_ready_assign(tcx, source_info)); + } + } + + // Replace the return variable: Poll<RetT> to Poll<()> + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); + + make_coroutine_state_argument_indirect(tcx, &mut body); + + match transform.coroutine_kind { + // Iterator::next doesn't accept a pinned argument, + // unlike for all other coroutine kinds. + CoroutineKind::Desugared(CoroutineDesugaring::Gen, _) => {} + _ => { + make_coroutine_state_argument_pinned(tcx, &mut body); + } + } + + // Make sure we remove dead blocks to remove + // unrelated code from the resume part of the function + simplify::remove_dead_blocks(&mut body); + + pm::run_passes_no_validate( + tcx, + &mut body, + &[&abort_unwinding_calls::AbortUnwindingCalls], + None, + ); + + dump_mir(tcx, false, "coroutine_drop_async", &0, &body, |_, _| Ok(())); + + body +} + +// Create async drop shim proxy function for future_drop_poll +// It is just { call coroutine_drop(); return Poll::Ready(); } +pub(super) fn create_coroutine_drop_shim_proxy_async<'tcx>( + tcx: TyCtxt<'tcx>, + body: &Body<'tcx>, +) -> Body<'tcx> { + let mut body = body.clone(); + // Take the coroutine info out of the body, since the drop shim is + // not a coroutine body itself; it just has its drop built out of it. + let _ = body.coroutine.take(); + let basic_blocks: IndexVec<BasicBlock, BasicBlockData<'tcx>> = IndexVec::new(); + body.basic_blocks = BasicBlocks::new(basic_blocks); + body.var_debug_info.clear(); + + // Keeping return value and args + body.local_decls.truncate(1 + body.arg_count); + + let source_info = SourceInfo::outermost(body.span); + + // Replace the return variable: Poll<RetT> to Poll<()> + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let poll_enum = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + body.local_decls[RETURN_PLACE] = LocalDecl::with_source_info(poll_enum, source_info); + + // call coroutine_drop() + let call_bb = body.basic_blocks_mut().push(BasicBlockData { + statements: Vec::new(), + terminator: None, + is_cleanup: false, + }); + + // return Poll::Ready() + let ret_bb = insert_poll_ready_block(tcx, &mut body); + + let kind = TerminatorKind::Drop { + place: Place::from(SELF_ARG), + target: ret_bb, + unwind: UnwindAction::Continue, + replace: false, + drop: None, + async_fut: None, + }; + body.basic_blocks_mut()[call_bb].terminator = Some(Terminator { source_info, kind }); + + dump_mir(tcx, false, "coroutine_drop_proxy_async", &0, &body, |_, _| Ok(())); + + body +} diff --git a/compiler/rustc_mir_transform/src/coverage/mod.rs b/compiler/rustc_mir_transform/src/coverage/mod.rs index aa4c0ef1e1f..f2e8f9e1bcd 100644 --- a/compiler/rustc_mir_transform/src/coverage/mod.rs +++ b/compiler/rustc_mir_transform/src/coverage/mod.rs @@ -1,8 +1,7 @@ -pub(super) mod query; - mod counters; mod graph; mod mappings; +pub(super) mod query; mod spans; #[cfg(test)] mod tests; @@ -90,7 +89,7 @@ fn instrument_function_for_coverage<'tcx>(tcx: TyCtxt<'tcx>, mir_body: &mut mir: // Use the coverage graph to prepare intermediate data that will eventually // be used to assign physical counters and counter expressions to points in - // the control-flow graph + // the control-flow graph. let BcbCountersData { node_flow_data, priority_list } = counters::prepare_bcb_counters_data(&graph); diff --git a/compiler/rustc_mir_transform/src/elaborate_drop.rs b/compiler/rustc_mir_transform/src/elaborate_drop.rs index e3057a2f648..6f867f8105d 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drop.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drop.rs @@ -4,12 +4,12 @@ use rustc_abi::{FIRST_VARIANT, FieldIdx, VariantIdx}; use rustc_hir::lang_items::LangItem; use rustc_index::Idx; use rustc_middle::mir::*; -use rustc_middle::span_bug; use rustc_middle::ty::adjustment::PointerCoercion; use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::ty::{self, GenericArg, GenericArgsRef, Ty, TyCtxt}; +use rustc_middle::{bug, span_bug, traits}; use rustc_span::DUMMY_SP; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Spanned, dummy_spanned}; use tracing::{debug, instrument}; use crate::patch::MirPatch; @@ -94,6 +94,9 @@ pub(crate) trait DropElaborator<'a, 'tcx>: fmt::Debug { fn body(&self) -> &'a Body<'tcx>; fn tcx(&self) -> TyCtxt<'tcx>; fn typing_env(&self) -> ty::TypingEnv<'tcx>; + fn allow_async_drops(&self) -> bool; + + fn terminator_loc(&self, bb: BasicBlock) -> Location; // Drop logic @@ -149,6 +152,7 @@ where path: D::Path, succ: BasicBlock, unwind: Unwind, + dropline: Option<BasicBlock>, } /// "Elaborates" a drop of `place`/`path` and patches `bb`'s terminator to execute it. @@ -167,11 +171,12 @@ pub(crate) fn elaborate_drop<'b, 'tcx, D>( succ: BasicBlock, unwind: Unwind, bb: BasicBlock, + dropline: Option<BasicBlock>, ) where D: DropElaborator<'b, 'tcx>, 'tcx: 'b, { - DropCtxt { elaborator, source_info, place, path, succ, unwind }.elaborate_drop(bb) + DropCtxt { elaborator, source_info, place, path, succ, unwind, dropline }.elaborate_drop(bb) } impl<'a, 'b, 'tcx, D> DropCtxt<'a, 'b, 'tcx, D> @@ -195,6 +200,209 @@ where self.elaborator.tcx() } + // Generates three blocks: + // * #1:pin_obj_bb: call Pin<ObjTy>::new_unchecked(&mut obj) + // * #2:call_drop_bb: fut = call obj.<AsyncDrop::drop>() OR call async_drop_in_place<T>(obj) + // * #3:drop_term_bb: drop (obj, fut, ...) + // We keep async drop unexpanded to poll-loop here, to expand it later, at StateTransform - + // into states expand. + // call_destructor_only - to call only AsyncDrop::drop, not full async_drop_in_place glue + fn build_async_drop( + &mut self, + place: Place<'tcx>, + drop_ty: Ty<'tcx>, + bb: Option<BasicBlock>, + succ: BasicBlock, + unwind: Unwind, + dropline: Option<BasicBlock>, + call_destructor_only: bool, + ) -> BasicBlock { + let tcx = self.tcx(); + let span = self.source_info.span; + + let pin_obj_bb = bb.unwrap_or_else(|| { + self.elaborator.patch().new_block(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + // Temporary terminator, will be replaced by patch + source_info: self.source_info, + kind: TerminatorKind::Return, + }), + is_cleanup: false, + }) + }); + + let (fut_ty, drop_fn_def_id, trait_args) = if call_destructor_only { + // Resolving obj.<AsyncDrop::drop>() + let trait_ref = ty::TraitRef::new( + tcx, + tcx.require_lang_item(LangItem::AsyncDrop, Some(span)), + [drop_ty], + ); + let (drop_trait, trait_args) = match tcx.codegen_select_candidate( + ty::TypingEnv::fully_monomorphized().as_query_input(trait_ref), + ) { + Ok(traits::ImplSource::UserDefined(traits::ImplSourceUserDefinedData { + impl_def_id, + args, + .. + })) => (*impl_def_id, *args), + impl_source => { + span_bug!(span, "invalid `AsyncDrop` impl_source: {:?}", impl_source); + } + }; + let drop_fn_def_id = tcx.associated_item_def_ids(drop_trait)[0]; + let drop_fn = Ty::new_fn_def(tcx, drop_fn_def_id, trait_args); + let sig = drop_fn.fn_sig(tcx); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + (sig.output(), drop_fn_def_id, trait_args) + } else { + // Resolving async_drop_in_place<T> function for drop_ty + let drop_fn_def_id = tcx.require_lang_item(LangItem::AsyncDropInPlace, Some(span)); + let trait_args = tcx.mk_args(&[drop_ty.into()]); + let sig = tcx.fn_sig(drop_fn_def_id).instantiate(tcx, trait_args); + let sig = tcx.instantiate_bound_regions_with_erased(sig); + (sig.output(), drop_fn_def_id, trait_args) + }; + + let fut = Place::from(self.new_temp(fut_ty)); + + // #1:pin_obj_bb >>> obj_ref = &mut obj + let obj_ref_ty = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, drop_ty); + let obj_ref_place = Place::from(self.new_temp(obj_ref_ty)); + + let term_loc = self.elaborator.terminator_loc(pin_obj_bb); + self.elaborator.patch().add_assign( + term_loc, + obj_ref_place, + Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + place, + ), + ); + + // pin_obj_place preparation + let pin_obj_new_unchecked_fn = Ty::new_fn_def( + tcx, + tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)), + [GenericArg::from(obj_ref_ty)], + ); + let pin_obj_ty = pin_obj_new_unchecked_fn.fn_sig(tcx).output().no_bound_vars().unwrap(); + let pin_obj_place = Place::from(self.new_temp(pin_obj_ty)); + let pin_obj_new_unchecked_fn = Operand::Constant(Box::new(ConstOperand { + span, + user_ty: None, + const_: Const::zero_sized(pin_obj_new_unchecked_fn), + })); + + // #3:drop_term_bb + let drop_term_bb = self.new_block( + unwind, + TerminatorKind::Drop { + place, + target: succ, + unwind: unwind.into_action(), + replace: false, + drop: dropline, + async_fut: Some(fut.local), + }, + ); + + // #2:call_drop_bb + let mut call_statements = Vec::new(); + let drop_arg = if call_destructor_only { + pin_obj_place + } else { + let ty::Adt(adt_def, adt_args) = pin_obj_ty.kind() else { + bug!(); + }; + let obj_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty); + let obj_ptr_place = Place::from(self.new_temp(obj_ptr_ty)); + let unwrap_ty = adt_def.non_enum_variant().fields[FieldIdx::ZERO].ty(tcx, adt_args); + let addr = Rvalue::RawPtr( + RawPtrKind::Mut, + pin_obj_place.project_deeper( + &[ProjectionElem::Field(FieldIdx::ZERO, unwrap_ty), ProjectionElem::Deref], + tcx, + ), + ); + call_statements.push(self.assign(obj_ptr_place, addr)); + obj_ptr_place + }; + call_statements.push(Statement { + source_info: self.source_info, + kind: StatementKind::StorageLive(fut.local), + }); + + let call_drop_bb = self.new_block_with_statements( + unwind, + call_statements, + TerminatorKind::Call { + func: Operand::function_handle(tcx, drop_fn_def_id, trait_args, span), + args: [Spanned { node: Operand::Move(drop_arg), span: DUMMY_SP }].into(), + destination: fut, + target: Some(drop_term_bb), + unwind: unwind.into_action(), + call_source: CallSource::Misc, + fn_span: self.source_info.span, + }, + ); + + // StorageDead(fut) in self.succ block (at the begin) + self.elaborator.patch().add_statement( + Location { block: self.succ, statement_index: 0 }, + StatementKind::StorageDead(fut.local), + ); + + // #1:pin_obj_bb >>> call Pin<ObjTy>::new_unchecked(&mut obj) + self.elaborator.patch().patch_terminator( + pin_obj_bb, + TerminatorKind::Call { + func: pin_obj_new_unchecked_fn, + args: [dummy_spanned(Operand::Move(obj_ref_place))].into(), + destination: pin_obj_place, + target: Some(call_drop_bb), + unwind: unwind.into_action(), + call_source: CallSource::Misc, + fn_span: span, + }, + ); + pin_obj_bb + } + + fn build_drop(&mut self, bb: BasicBlock) { + let drop_ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !self.elaborator.body()[bb].is_cleanup + && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + self.place, + drop_ty, + Some(bb), + self.succ, + self.unwind, + self.dropline, + false, + ); + } else { + self.elaborator.patch().patch_terminator( + bb, + TerminatorKind::Drop { + place: self.place, + target: self.succ, + unwind: self.unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + } + } + /// This elaborates a single drop instruction, located at `bb`, and /// patches over it. /// @@ -222,15 +430,7 @@ where .patch_terminator(bb, TerminatorKind::Goto { target: self.succ }); } DropStyle::Static => { - self.elaborator.patch().patch_terminator( - bb, - TerminatorKind::Drop { - place: self.place, - target: self.succ, - unwind: self.unwind.into_action(), - replace: false, - }, - ); + self.build_drop(bb); } DropStyle::Conditional => { let drop_bb = self.complete_drop(self.succ, self.unwind); @@ -289,6 +489,7 @@ where path: Option<D::Path>, succ: BasicBlock, unwind: Unwind, + dropline: Option<BasicBlock>, ) -> BasicBlock { if let Some(path) = path { debug!("drop_subpath: for std field {:?}", place); @@ -300,6 +501,7 @@ where place, succ, unwind, + dropline, } .elaborated_drop_block() } else { @@ -311,6 +513,7 @@ where place, succ, unwind, + dropline, // Using `self.path` here to condition the drop on // our own drop flag. path: self.path, @@ -325,25 +528,36 @@ where /// /// `unwind_ladder` is such a list of steps in reverse order, /// which is called if the matching step of the drop glue panics. + /// + /// `dropline_ladder` is a similar list of steps in reverse order, + /// which is called if the matching step of the drop glue will contain async drop + /// (expanded later to Yield) and the containing coroutine will be dropped at this point. fn drop_halfladder( &mut self, unwind_ladder: &[Unwind], + dropline_ladder: &[Option<BasicBlock>], mut succ: BasicBlock, fields: &[(Place<'tcx>, Option<D::Path>)], ) -> Vec<BasicBlock> { iter::once(succ) - .chain(fields.iter().rev().zip(unwind_ladder).map(|(&(place, path), &unwind_succ)| { - succ = self.drop_subpath(place, path, succ, unwind_succ); - succ - })) + .chain(itertools::izip!(fields.iter().rev(), unwind_ladder, dropline_ladder).map( + |(&(place, path), &unwind_succ, &dropline_to)| { + succ = self.drop_subpath(place, path, succ, unwind_succ, dropline_to); + succ + }, + )) .collect() } - fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind) { + fn drop_ladder_bottom(&mut self) -> (BasicBlock, Unwind, Option<BasicBlock>) { // Clear the "master" drop flag at the end. This is needed // because the "master" drop protects the ADT's discriminant, // which is invalidated after the ADT is dropped. - (self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), self.unwind) + ( + self.drop_flag_reset_block(DropFlagMode::Shallow, self.succ, self.unwind), + self.unwind, + self.dropline, + ) } /// Creates a full drop ladder, consisting of 2 connected half-drop-ladders @@ -361,6 +575,22 @@ where /// .c2: /// ELAB(drop location.2 [target=`self.unwind`]) /// + /// For possible-async drops in coroutines we also need dropline ladder + /// .d0 (mainline): + /// ELAB(drop location.0 [target=.d1, unwind=.c1, drop=.e1]) + /// .d1 (mainline): + /// ELAB(drop location.1 [target=.d2, unwind=.c2, drop=.e2]) + /// .d2 (mainline): + /// ELAB(drop location.2 [target=`self.succ`, unwind=`self.unwind`, drop=`self.drop`]) + /// .c1 (unwind): + /// ELAB(drop location.1 [target=.c2]) + /// .c2 (unwind): + /// ELAB(drop location.2 [target=`self.unwind`]) + /// .e1 (dropline): + /// ELAB(drop location.1 [target=.e2, unwind=.c2]) + /// .e2 (dropline): + /// ELAB(drop location.2 [target=`self.drop`, unwind=`self.unwind`]) + /// /// NOTE: this does not clear the master drop flag, so you need /// to point succ/unwind on a `drop_ladder_bottom`. fn drop_ladder( @@ -368,8 +598,13 @@ where fields: Vec<(Place<'tcx>, Option<D::Path>)>, succ: BasicBlock, unwind: Unwind, - ) -> (BasicBlock, Unwind) { + dropline: Option<BasicBlock>, + ) -> (BasicBlock, Unwind, Option<BasicBlock>) { debug!("drop_ladder({:?}, {:?})", self, fields); + assert!( + if unwind.is_cleanup() { dropline.is_none() } else { true }, + "Dropline is set for cleanup drop ladder" + ); let mut fields = fields; fields.retain(|&(place, _)| { @@ -378,17 +613,28 @@ where debug!("drop_ladder - fields needing drop: {:?}", fields); + let dropline_ladder: Vec<Option<BasicBlock>> = vec![None; fields.len() + 1]; let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let unwind_ladder: Vec<_> = if let Unwind::To(target) = unwind { - let halfladder = self.drop_halfladder(&unwind_ladder, target, &fields); + let unwind_ladder: Vec<_> = if let Unwind::To(succ) = unwind { + let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); halfladder.into_iter().map(Unwind::To).collect() } else { unwind_ladder }; + let dropline_ladder: Vec<_> = if let Some(succ) = dropline { + let halfladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); + halfladder.into_iter().map(Some).collect() + } else { + dropline_ladder + }; - let normal_ladder = self.drop_halfladder(&unwind_ladder, succ, &fields); + let normal_ladder = self.drop_halfladder(&unwind_ladder, &dropline_ladder, succ, &fields); - (*normal_ladder.last().unwrap(), *unwind_ladder.last().unwrap()) + ( + *normal_ladder.last().unwrap(), + *unwind_ladder.last().unwrap(), + *dropline_ladder.last().unwrap(), + ) } fn open_drop_for_tuple(&mut self, tys: &[Ty<'tcx>]) -> BasicBlock { @@ -405,8 +651,8 @@ where }) .collect(); - let (succ, unwind) = self.drop_ladder_bottom(); - self.drop_ladder(fields, succ, unwind).0 + let (succ, unwind, dropline) = self.drop_ladder_bottom(); + self.drop_ladder(fields, succ, unwind, dropline).0 } /// Drops the T contained in a `Box<T>` if it has not been moved out of @@ -417,6 +663,7 @@ where args: GenericArgsRef<'tcx>, succ: BasicBlock, unwind: Unwind, + dropline: Option<BasicBlock>, ) -> BasicBlock { // drop glue is sent straight to codegen // box cannot be directly dereferenced @@ -433,7 +680,7 @@ where let interior = self.tcx().mk_place_deref(Place::from(ptr_local)); let interior_path = self.elaborator.deref_subpath(self.path); - let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind); + let do_drop_bb = self.drop_subpath(interior, interior_path, succ, unwind, dropline); let setup_bbd = BasicBlockData { statements: vec![self.assign( @@ -468,19 +715,22 @@ where let skip_contents = adt.is_union() || adt.is_manually_drop(); let contents_drop = if skip_contents { - (self.succ, self.unwind) + (self.succ, self.unwind, self.dropline) } else { self.open_drop_for_adt_contents(adt, args) }; if adt.is_box() { // we need to drop the inside of the box before running the destructor - let succ = self.destructor_call_block(contents_drop); + let succ = self.destructor_call_block_sync((contents_drop.0, contents_drop.1)); let unwind = contents_drop .1 - .map(|unwind| self.destructor_call_block((unwind, Unwind::InCleanup))); + .map(|unwind| self.destructor_call_block_sync((unwind, Unwind::InCleanup))); + let dropline = contents_drop + .2 + .map(|dropline| self.destructor_call_block_sync((dropline, contents_drop.1))); - self.open_drop_for_box_contents(adt, args, succ, unwind) + self.open_drop_for_box_contents(adt, args, succ, unwind, dropline) } else if adt.has_dtor(self.tcx()) { self.destructor_call_block(contents_drop) } else { @@ -492,14 +742,14 @@ where &mut self, adt: ty::AdtDef<'tcx>, args: GenericArgsRef<'tcx>, - ) -> (BasicBlock, Unwind) { - let (succ, unwind) = self.drop_ladder_bottom(); + ) -> (BasicBlock, Unwind, Option<BasicBlock>) { + let (succ, unwind, dropline) = self.drop_ladder_bottom(); if !adt.is_enum() { let fields = self.move_paths_for_fields(self.place, self.path, adt.variant(FIRST_VARIANT), args); - self.drop_ladder(fields, succ, unwind) + self.drop_ladder(fields, succ, unwind, dropline) } else { - self.open_drop_for_multivariant(adt, args, succ, unwind) + self.open_drop_for_multivariant(adt, args, succ, unwind, dropline) } } @@ -509,11 +759,14 @@ where args: GenericArgsRef<'tcx>, succ: BasicBlock, unwind: Unwind, - ) -> (BasicBlock, Unwind) { + dropline: Option<BasicBlock>, + ) -> (BasicBlock, Unwind, Option<BasicBlock>) { let mut values = Vec::with_capacity(adt.variants().len()); let mut normal_blocks = Vec::with_capacity(adt.variants().len()); let mut unwind_blocks = if unwind.is_cleanup() { None } else { Some(Vec::with_capacity(adt.variants().len())) }; + let mut dropline_blocks = + if dropline.is_none() { None } else { Some(Vec::with_capacity(adt.variants().len())) }; let mut have_otherwise_with_drop_glue = false; let mut have_otherwise = false; @@ -551,11 +804,16 @@ where let unwind_blocks = unwind_blocks.as_mut().unwrap(); let unwind_ladder = vec![Unwind::InCleanup; fields.len() + 1]; - let halfladder = self.drop_halfladder(&unwind_ladder, unwind, &fields); + let dropline_ladder: Vec<Option<BasicBlock>> = vec![None; fields.len() + 1]; + let halfladder = + self.drop_halfladder(&unwind_ladder, &dropline_ladder, unwind, &fields); unwind_blocks.push(halfladder.last().cloned().unwrap()); } - let (normal, _) = self.drop_ladder(fields, succ, unwind); + let (normal, _, drop_bb) = self.drop_ladder(fields, succ, unwind, dropline); normal_blocks.push(normal); + if dropline.is_some() { + dropline_blocks.as_mut().unwrap().push(drop_bb.unwrap()); + } } else { have_otherwise = true; @@ -595,6 +853,9 @@ where Unwind::InCleanup, ) }), + dropline.map(|dropline| { + self.adt_switch_block(adt, dropline_blocks.unwrap(), &values, dropline, unwind) + }), ) } @@ -634,8 +895,8 @@ where self.drop_flag_test_block(switch_block, succ, unwind) } - fn destructor_call_block(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { - debug!("destructor_call_block({:?}, {:?})", self, succ); + fn destructor_call_block_sync(&mut self, (succ, unwind): (BasicBlock, Unwind)) -> BasicBlock { + debug!("destructor_call_block_sync({:?}, {:?})", self, succ); let tcx = self.tcx(); let drop_trait = tcx.require_lang_item(LangItem::Drop, None); let drop_fn = tcx.associated_item_def_ids(drop_trait)[0]; @@ -683,6 +944,30 @@ where self.drop_flag_test_block(destructor_block, succ, unwind) } + fn destructor_call_block( + &mut self, + (succ, unwind, dropline): (BasicBlock, Unwind, Option<BasicBlock>), + ) -> BasicBlock { + debug!("destructor_call_block({:?}, {:?})", self, succ); + let ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && ty.is_async_drop(self.tcx(), self.elaborator.typing_env()) + { + let destructor_block = + self.build_async_drop(self.place, ty, None, succ, unwind, dropline, true); + + let block_start = Location { block: destructor_block, statement_index: 0 }; + self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); + + self.drop_flag_test_block(destructor_block, succ, unwind) + } else { + self.destructor_call_block_sync((succ, unwind)) + } + } + /// Create a loop that drops an array: /// /// ```text @@ -701,6 +986,7 @@ where len: Local, ety: Ty<'tcx>, unwind: Unwind, + dropline: Option<BasicBlock>, ) -> BasicBlock { let copy = |place: Place<'tcx>| Operand::Copy(place); let move_ = |place: Place<'tcx>| Operand::Move(place); @@ -744,16 +1030,35 @@ where }; let loop_block = self.elaborator.patch().new_block(loop_block); - self.elaborator.patch().patch_terminator( - drop_block, - TerminatorKind::Drop { - place: tcx.mk_place_deref(ptr), - target: loop_block, - unwind: unwind.into_action(), - replace: false, - }, - ); - + let place = tcx.mk_place_deref(ptr); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && ety.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + place, + ety, + Some(drop_block), + loop_block, + unwind, + dropline, + false, + ); + } else { + self.elaborator.patch().patch_terminator( + drop_block, + TerminatorKind::Drop { + place, + target: loop_block, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }, + ); + } loop_block } @@ -820,8 +1125,8 @@ where (tcx.mk_place_elem(self.place, project), path) }) .collect::<Vec<_>>(); - let (succ, unwind) = self.drop_ladder_bottom(); - return self.drop_ladder(fields, succ, unwind).0; + let (succ, unwind, dropline) = self.drop_ladder_bottom(); + return self.drop_ladder(fields, succ, unwind, dropline).0; } } @@ -855,7 +1160,7 @@ where &mut self.place, Place::from(slice_ptr).project_deeper(&[PlaceElem::Deref], tcx), ); - let slice_block = self.drop_loop_pair_for_slice(ety); + let slice_block = self.drop_loop_trio_for_slice(ety); self.place = array_place; delegate_block.terminator = Some(Terminator { @@ -865,18 +1170,22 @@ where self.elaborator.patch().new_block(delegate_block) } - /// Creates a pair of drop-loops of `place`, which drops its contents, even - /// in the case of 1 panic. - fn drop_loop_pair_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { - debug!("drop_loop_pair_for_slice({:?})", ety); + /// Creates a trio of drop-loops of `place`, which drops its contents, even + /// in the case of 1 panic or in the case of coroutine drop + fn drop_loop_trio_for_slice(&mut self, ety: Ty<'tcx>) -> BasicBlock { + debug!("drop_loop_trio_for_slice({:?})", ety); let tcx = self.tcx(); let len = self.new_temp(tcx.types.usize); let cur = self.new_temp(tcx.types.usize); - let unwind = - self.unwind.map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup)); + let unwind = self + .unwind + .map(|unwind| self.drop_loop(unwind, cur, len, ety, Unwind::InCleanup, None)); - let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind); + let dropline = + self.dropline.map(|dropline| self.drop_loop(dropline, cur, len, ety, unwind, None)); + + let loop_block = self.drop_loop(self.succ, cur, len, ety, unwind, dropline); let [PlaceElem::Deref] = self.place.projection.as_slice() else { span_bug!( @@ -940,7 +1249,7 @@ where let size = size.try_to_target_usize(self.tcx()); self.open_drop_for_array(ty, *ety, size) } - ty::Slice(ety) => self.drop_loop_pair_for_slice(*ety), + ty::Slice(ety) => self.drop_loop_trio_for_slice(*ety), _ => span_bug!(self.source_info.span, "open drop from non-ADT `{:?}`", ty), } @@ -977,21 +1286,53 @@ where fn elaborated_drop_block(&mut self) -> BasicBlock { debug!("elaborated_drop_block({:?})", self); - let blk = self.drop_block(self.succ, self.unwind); + let blk = self.drop_block_simple(self.succ, self.unwind); self.elaborate_drop(blk); blk } - fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + fn drop_block_simple(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = TerminatorKind::Drop { place: self.place, target, unwind: unwind.into_action(), replace: false, + drop: self.dropline, + async_fut: None, }; self.new_block(unwind, block) } + fn drop_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { + let drop_ty = self.place_ty(self.place); + if self.tcx().features().async_drop() + && self.elaborator.body().coroutine.is_some() + && self.elaborator.allow_async_drops() + && !unwind.is_cleanup() + && drop_ty.needs_async_drop(self.tcx(), self.elaborator.typing_env()) + { + self.build_async_drop( + self.place, + drop_ty, + None, + self.succ, + unwind, + self.dropline, + false, + ) + } else { + let block = TerminatorKind::Drop { + place: self.place, + target, + unwind: unwind.into_action(), + replace: false, + drop: None, + async_fut: None, + }; + self.new_block(unwind, block) + } + } + fn goto_block(&mut self, target: BasicBlock, unwind: Unwind) -> BasicBlock { let block = TerminatorKind::Goto { target }; self.new_block(unwind, block) @@ -1033,6 +1374,19 @@ where }) } + fn new_block_with_statements( + &mut self, + unwind: Unwind, + statements: Vec<Statement<'tcx>>, + k: TerminatorKind<'tcx>, + ) -> BasicBlock { + self.elaborator.patch().new_block(BasicBlockData { + statements, + terminator: Some(Terminator { source_info: self.source_info, kind: k }), + is_cleanup: unwind.is_cleanup(), + }) + } + fn new_temp(&mut self, ty: Ty<'tcx>) -> Local { self.elaborator.patch().new_temp(ty, self.source_info.span) } diff --git a/compiler/rustc_mir_transform/src/elaborate_drops.rs b/compiler/rustc_mir_transform/src/elaborate_drops.rs index 530c72ca549..42c8cb0b906 100644 --- a/compiler/rustc_mir_transform/src/elaborate_drops.rs +++ b/compiler/rustc_mir_transform/src/elaborate_drops.rs @@ -158,6 +158,14 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for ElaborateDropsCtxt<'a, 'tcx> { self.env.typing_env } + fn allow_async_drops(&self) -> bool { + true + } + + fn terminator_loc(&self, bb: BasicBlock) -> Location { + self.patch.terminator_loc(self.body, bb) + } + #[instrument(level = "debug", skip(self), ret)] fn drop_style(&self, path: Self::Path, mode: DropFlagMode) -> DropStyle { let ((maybe_init, maybe_uninit), multipart) = match mode { @@ -328,7 +336,9 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { // This function should mirror what `collect_drop_flags` does. for (bb, data) in self.body.basic_blocks.iter_enumerated() { let terminator = data.terminator(); - let TerminatorKind::Drop { place, target, unwind, replace } = terminator.kind else { + let TerminatorKind::Drop { place, target, unwind, replace, drop, async_fut: _ } = + terminator.kind + else { continue; }; @@ -364,7 +374,16 @@ impl<'a, 'tcx> ElaborateDropsCtxt<'a, 'tcx> { } }; self.init_data.seek_before(self.body.terminator_loc(bb)); - elaborate_drop(self, terminator.source_info, place, path, target, unwind, bb) + elaborate_drop( + self, + terminator.source_info, + place, + path, + target, + unwind, + bb, + drop, + ) } LookupResult::Parent(None) => {} LookupResult::Parent(Some(_)) => { diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index c5732194424..9785c039d53 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -413,7 +413,15 @@ impl<'tcx> Inliner<'tcx> for NormalInliner<'tcx> { let term = blk.terminator(); let caller_attrs = tcx.codegen_fn_attrs(self.caller_def_id()); - if let TerminatorKind::Drop { ref place, target, unwind, replace: _ } = term.kind { + if let TerminatorKind::Drop { + ref place, + target, + unwind, + replace: _, + drop: _, + async_fut: _, + } = term.kind + { work_list.push(target); // If the place doesn't actually need dropping, treat it like a regular goto. @@ -726,6 +734,20 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( debug!("still needs substitution"); return Err("implementation limitation -- HACK for dropping polymorphic type"); } + InstanceKind::AsyncDropGlue(_, ty) | InstanceKind::AsyncDropGlueCtorShim(_, ty) => { + return if ty.still_further_specializable() { + Err("still needs substitution") + } else { + Ok(()) + }; + } + InstanceKind::FutureDropPollShim(_, ty, ty2) => { + return if ty.still_further_specializable() || ty2.still_further_specializable() { + Err("still needs substitution") + } else { + Ok(()) + }; + } // This cannot result in an immediate cycle since the callee MIR is a shim, which does // not get any optimizations run on it. Any subsequent inlining may cause cycles, but we @@ -739,8 +761,7 @@ fn check_mir_is_available<'tcx, I: Inliner<'tcx>>( | InstanceKind::DropGlue(..) | InstanceKind::CloneShim(..) | InstanceKind::ThreadLocalShim(..) - | InstanceKind::FnPtrAddrShim(..) - | InstanceKind::AsyncDropGlueCtorShim(..) => return Ok(()), + | InstanceKind::FnPtrAddrShim(..) => return Ok(()), } if inliner.tcx().is_constructor(callee_def_id) { @@ -1345,8 +1366,8 @@ fn try_instance_mir<'tcx>( tcx: TyCtxt<'tcx>, instance: InstanceKind<'tcx>, ) -> Result<&'tcx Body<'tcx>, &'static str> { - if let ty::InstanceKind::DropGlue(_, Some(ty)) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(ty)) = instance + if let ty::InstanceKind::DropGlue(_, Some(ty)) | ty::InstanceKind::AsyncDropGlueCtorShim(_, ty) = + instance && let ty::Adt(def, args) = ty.kind() { let fields = def.all_fields(); diff --git a/compiler/rustc_mir_transform/src/inline/cycle.rs b/compiler/rustc_mir_transform/src/inline/cycle.rs index a40768300f5..292278800f8 100644 --- a/compiler/rustc_mir_transform/src/inline/cycle.rs +++ b/compiler/rustc_mir_transform/src/inline/cycle.rs @@ -95,7 +95,10 @@ pub(crate) fn mir_callgraph_reachable<'tcx>( InstanceKind::FnPtrAddrShim(..) => { continue; } - InstanceKind::DropGlue(..) | InstanceKind::AsyncDropGlueCtorShim(..) => { + InstanceKind::DropGlue(..) + | InstanceKind::FutureDropPollShim(..) + | InstanceKind::AsyncDropGlue(..) + | InstanceKind::AsyncDropGlueCtorShim(..) => { // FIXME: A not fully instantiated drop shim can cause ICEs if one attempts to // have its MIR built. Likely oli-obk just screwed up the `ParamEnv`s, so this // needs some more analysis. diff --git a/compiler/rustc_mir_transform/src/known_panics_lint.rs b/compiler/rustc_mir_transform/src/known_panics_lint.rs index f8db8de4e82..481c7941909 100644 --- a/compiler/rustc_mir_transform/src/known_panics_lint.rs +++ b/compiler/rustc_mir_transform/src/known_panics_lint.rs @@ -888,7 +888,14 @@ impl CanConstProp { }; for (local, val) in cpv.can_const_prop.iter_enumerated_mut() { let ty = body.local_decls[local].ty; - if ty.is_union() { + if ty.is_async_drop_in_place_coroutine(tcx) { + // No const propagation for async drop coroutine (AsyncDropGlue). + // Otherwise, tcx.layout_of(typing_env.as_query_input(ty)) will be called + // (early layout request for async drop coroutine) to calculate layout size. + // Layout for `async_drop_in_place<T>::{closure}` may only be known with known T. + *val = ConstPropMode::NoPropagation; + continue; + } else if ty.is_union() { // Unions are incompatible with the current implementation of // const prop because Rust has no concept of an active // variant of a union diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 5db62b7e902..24f4c11a66d 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -652,7 +652,7 @@ fn run_runtime_cleanup_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { } } -fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { +pub(crate) fn run_optimization_passes<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) { fn o1<T>(x: T) -> WithMinOptLevel<T> { WithMinOptLevel(1, x) } diff --git a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs index 537f152938e..b8cb101f93c 100644 --- a/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs +++ b/compiler/rustc_mir_transform/src/lint_tail_expr_drop_order.rs @@ -134,6 +134,8 @@ impl<'a, 'mir, 'tcx> DropsReachable<'a, 'mir, 'tcx> { target: _, unwind: _, replace: _, + drop: _, + async_fut: _, } = &terminator.kind && place_has_common_prefix(dropped_place, self.place) { diff --git a/compiler/rustc_mir_transform/src/patch.rs b/compiler/rustc_mir_transform/src/patch.rs index 12ace04c5e2..c7eb2a921c7 100644 --- a/compiler/rustc_mir_transform/src/patch.rs +++ b/compiler/rustc_mir_transform/src/patch.rs @@ -148,6 +148,14 @@ impl<'tcx> MirPatch<'tcx> { self.term_patch_map[bb].is_some() } + pub(crate) fn terminator_loc(&self, body: &Body<'tcx>, bb: BasicBlock) -> Location { + let offset = match bb.index().checked_sub(body.basic_blocks.len()) { + Some(index) => self.new_blocks[index].statements.len(), + None => body[bb].statements.len(), + }; + Location { block: bb, statement_index: offset } + } + /// Queues the addition of a new temporary with additional local info. pub(crate) fn new_local_with_info( &mut self, diff --git a/compiler/rustc_mir_transform/src/remove_zsts.rs b/compiler/rustc_mir_transform/src/remove_zsts.rs index 78d94a03867..c4dc8638b26 100644 --- a/compiler/rustc_mir_transform/src/remove_zsts.rs +++ b/compiler/rustc_mir_transform/src/remove_zsts.rs @@ -59,6 +59,11 @@ fn trivially_zst<'tcx>(ty: Ty<'tcx>, tcx: TyCtxt<'tcx>) -> Option<bool> { | ty::RawPtr(..) | ty::Ref(..) | ty::FnPtr(..) => Some(false), + ty::Coroutine(def_id, _) => { + // For async_drop_in_place::{closure} this is load bearing, not just a perf fix, + // because we don't want to compute the layout before mir analysis is done + if tcx.is_async_drop_in_place_coroutine(*def_id) { Some(false) } else { None } + } // check `layout_of` to see (including unreachable things we won't actually see) _ => None, } diff --git a/compiler/rustc_mir_transform/src/shim.rs b/compiler/rustc_mir_transform/src/shim.rs index c13ffae3649..0d9a04b760a 100644 --- a/compiler/rustc_mir_transform/src/shim.rs +++ b/compiler/rustc_mir_transform/src/shim.rs @@ -6,13 +6,14 @@ use rustc_hir as hir; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_index::{Idx, IndexVec}; +use rustc_middle::mir::visit::{MutVisitor, PlaceContext}; use rustc_middle::mir::*; use rustc_middle::query::Providers; use rustc_middle::ty::{ self, CoroutineArgs, CoroutineArgsExt, EarlyBinder, GenericArgs, Ty, TyCtxt, }; use rustc_middle::{bug, span_bug}; -use rustc_span::source_map::Spanned; +use rustc_span::source_map::{Spanned, dummy_spanned}; use rustc_span::{DUMMY_SP, Span}; use tracing::{debug, instrument}; @@ -20,7 +21,8 @@ use crate::elaborate_drop::{DropElaborator, DropFlagMode, DropStyle, Unwind, ela use crate::patch::MirPatch; use crate::{ abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator, inline, - instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, simplify, + instsimplify, mentioned_items, pass_manager as pm, remove_noop_landing_pads, + run_optimization_passes, simplify, }; mod async_destructor_ctor; @@ -29,6 +31,40 @@ pub(super) fn provide(providers: &mut Providers) { providers.mir_shims = make_shim; } +// Replace Pin<&mut ImplCoroutine> accesses (_1.0) into Pin<&mut ProxyCoroutine> acceses +struct FixProxyFutureDropVisitor<'tcx> { + tcx: TyCtxt<'tcx>, + replace_to: Local, +} + +impl<'tcx> MutVisitor<'tcx> for FixProxyFutureDropVisitor<'tcx> { + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + + fn visit_place( + &mut self, + place: &mut Place<'tcx>, + _context: PlaceContext, + _location: Location, + ) { + if place.local == Local::from_u32(1) { + if place.projection.len() == 1 { + assert!(matches!( + place.projection.first(), + Some(ProjectionElem::Field(FieldIdx::ZERO, _)) + )); + *place = Place::from(self.replace_to); + } else if place.projection.len() == 2 { + assert!(matches!(place.projection[0], ProjectionElem::Field(FieldIdx::ZERO, _))); + assert!(matches!(place.projection[1], ProjectionElem::Deref)); + *place = + Place::from(self.replace_to).project_deeper(&[ProjectionElem::Deref], self.tcx); + } + } + } +} + fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body<'tcx> { debug!("make_shim({:?})", instance); @@ -129,8 +165,53 @@ fn make_shim<'tcx>(tcx: TyCtxt<'tcx>, instance: ty::InstanceKind<'tcx>) -> Body< ty::InstanceKind::ThreadLocalShim(..) => build_thread_local_shim(tcx, instance), ty::InstanceKind::CloneShim(def_id, ty) => build_clone_shim(tcx, def_id, ty), ty::InstanceKind::FnPtrAddrShim(def_id, ty) => build_fn_ptr_addr_shim(tcx, def_id, ty), + ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty) => { + let mut body = + async_destructor_ctor::build_future_drop_poll_shim(tcx, def_id, proxy_ty, impl_ty); + + pm::run_passes( + tcx, + &mut body, + &[ + &mentioned_items::MentionedItems, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + ], + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); + run_optimization_passes(tcx, &mut body); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; + } + ty::InstanceKind::AsyncDropGlue(def_id, ty) => { + let mut body = async_destructor_ctor::build_async_drop_shim(tcx, def_id, ty); + + // Main pass required here is StateTransform to convert sync drop ladder + // into coroutine. + // Others are minimal passes as for sync drop glue shim + pm::run_passes( + tcx, + &mut body, + &[ + &mentioned_items::MentionedItems, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + &simplify::SimplifyCfg::MakeShim, + &crate::coroutine::StateTransform, + ], + Some(MirPhase::Runtime(RuntimePhase::PostCleanup)), + pm::Optimizations::Allowed, + ); + run_optimization_passes(tcx, &mut body); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; + } + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) => { - async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty) + let body = async_destructor_ctor::build_async_destructor_ctor_shim(tcx, def_id, ty); + debug!("make_shim({:?}) = {:?}", instance, body); + return body; } ty::InstanceKind::Virtual(..) => { bug!("InstanceKind::Virtual ({:?}) is for direct calls only", instance) @@ -214,6 +295,43 @@ fn local_decls_for_sig<'tcx>( .collect() } +fn dropee_emit_retag<'tcx>( + tcx: TyCtxt<'tcx>, + body: &mut Body<'tcx>, + dropee_ptr: Place<'tcx>, + span: Span, +) -> Place<'tcx> { + let mut dropee_ptr = dropee_ptr; + if tcx.sess.opts.unstable_opts.mir_emit_retag { + let source_info = SourceInfo::outermost(span); + // We want to treat the function argument as if it was passed by `&mut`. As such, we + // generate + // ``` + // temp = &mut *arg; + // Retag(temp, FnEntry) + // ``` + // It's important that we do this first, before anything that depends on `dropee_ptr` + // has been put into the body. + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(dropee_ptr), + ); + let ref_ty = reborrow.ty(body.local_decls(), tcx); + dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into(); + let new_statements = [ + StatementKind::Assign(Box::new((dropee_ptr, reborrow))), + StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)), + ]; + for s in new_statements { + body.basic_blocks_mut()[START_BLOCK] + .statements + .push(Statement { source_info, kind: s }); + } + } + dropee_ptr +} + fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) -> Body<'tcx> { debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); @@ -248,38 +366,18 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) // The first argument (index 0), but add 1 for the return value. let mut dropee_ptr = Place::from(Local::new(1 + 0)); - if tcx.sess.opts.unstable_opts.mir_emit_retag { - // We want to treat the function argument as if it was passed by `&mut`. As such, we - // generate - // ``` - // temp = &mut *arg; - // Retag(temp, FnEntry) - // ``` - // It's important that we do this first, before anything that depends on `dropee_ptr` - // has been put into the body. - let reborrow = Rvalue::Ref( - tcx.lifetimes.re_erased, - BorrowKind::Mut { kind: MutBorrowKind::Default }, - tcx.mk_place_deref(dropee_ptr), - ); - let ref_ty = reborrow.ty(body.local_decls(), tcx); - dropee_ptr = body.local_decls.push(LocalDecl::new(ref_ty, span)).into(); - let new_statements = [ - StatementKind::Assign(Box::new((dropee_ptr, reborrow))), - StatementKind::Retag(RetagKind::FnEntry, Box::new(dropee_ptr)), - ]; - for s in new_statements { - body.basic_blocks_mut()[START_BLOCK] - .statements - .push(Statement { source_info, kind: s }); - } - } + dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span); if ty.is_some() { let patch = { let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - let mut elaborator = - DropShimElaborator { body: &body, patch: MirPatch::new(&body), tcx, typing_env }; + let mut elaborator = DropShimElaborator { + body: &body, + patch: MirPatch::new(&body), + tcx, + typing_env, + produce_async_drops: false, + }; let dropee = tcx.mk_place_deref(dropee_ptr); let resume_block = elaborator.patch.resume_block(); elaborate_drop( @@ -290,6 +388,7 @@ fn build_drop_shim<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, ty: Option<Ty<'tcx>>) return_block, Unwind::To(resume_block), START_BLOCK, + None, ); elaborator.patch }; @@ -338,6 +437,7 @@ pub(super) struct DropShimElaborator<'a, 'tcx> { pub patch: MirPatch<'tcx>, pub tcx: TyCtxt<'tcx>, pub typing_env: ty::TypingEnv<'tcx>, + pub produce_async_drops: bool, } impl fmt::Debug for DropShimElaborator<'_, '_> { @@ -365,6 +465,13 @@ impl<'a, 'tcx> DropElaborator<'a, 'tcx> for DropShimElaborator<'a, 'tcx> { self.typing_env } + fn terminator_loc(&self, bb: BasicBlock) -> Location { + self.patch.terminator_loc(self.body, bb) + } + fn allow_async_drops(&self) -> bool { + self.produce_async_drops + } + fn drop_style(&self, _path: Self::Path, mode: DropFlagMode) -> DropStyle { match mode { DropFlagMode::Shallow => { @@ -621,6 +728,8 @@ impl<'tcx> CloneShimBuilder<'tcx> { target: unwind, unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), replace: false, + drop: None, + async_fut: None, }, /* is_cleanup */ true, ); @@ -886,6 +995,8 @@ fn build_call_shim<'tcx>( target: BasicBlock::new(2), unwind: UnwindAction::Continue, replace: false, + drop: None, + async_fut: None, }, false, ); @@ -903,6 +1014,8 @@ fn build_call_shim<'tcx>( target: BasicBlock::new(4), unwind: UnwindAction::Terminate(UnwindTerminateReason::InCleanup), replace: false, + drop: None, + async_fut: None, }, /* is_cleanup */ true, ); diff --git a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs index 94b1b4b1855..7976b65aae7 100644 --- a/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs +++ b/compiler/rustc_mir_transform/src/shim/async_destructor_ctor.rs @@ -1,639 +1,430 @@ -use std::iter; - -use itertools::Itertools; -use rustc_abi::{FieldIdx, VariantIdx}; -use rustc_const_eval::interpret; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; +use rustc_hir::{CoroutineDesugaring, CoroutineKind, CoroutineSource, Safety}; use rustc_index::{Idx, IndexVec}; -use rustc_middle::mir::*; -use rustc_middle::ty::adjustment::PointerCoercion; -use rustc_middle::ty::util::{AsyncDropGlueMorphology, Discr}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_middle::{bug, span_bug}; -use rustc_span::source_map::respan; -use rustc_span::{Span, Symbol}; -use rustc_target::spec::PanicStrategy; -use tracing::debug; - -use super::{local_decls_for_sig, new_body}; +use rustc_middle::mir::{ + BasicBlock, BasicBlockData, Body, Local, LocalDecl, MirSource, Operand, Place, Rvalue, + SourceInfo, Statement, StatementKind, Terminator, TerminatorKind, +}; +use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt}; + +use super::*; +use crate::patch::MirPatch; pub(super) fn build_async_destructor_ctor_shim<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - ty: Option<Ty<'tcx>>, + ty: Ty<'tcx>, ) -> Body<'tcx> { - debug!("build_drop_shim(def_id={:?}, ty={:?})", def_id, ty); - - AsyncDestructorCtorShimBuilder::new(tcx, def_id, ty).build() + debug!("build_async_destructor_ctor_shim(def_id={:?}, ty={:?})", def_id, ty); + debug_assert_eq!(Some(def_id), tcx.lang_items().async_drop_in_place_fn()); + let generic_body = tcx.optimized_mir(def_id); + let args = tcx.mk_args(&[ty.into()]); + let mut body = EarlyBinder::bind(generic_body.clone()).instantiate(tcx, args); + + // Minimal shim passes except MentionedItems, + // it causes error "mentioned_items for DefId(...async_drop_in_place...) have already been set + pm::run_passes( + tcx, + &mut body, + &[ + &simplify::SimplifyCfg::MakeShim, + &abort_unwinding_calls::AbortUnwindingCalls, + &add_call_guards::CriticalCallEdges, + ], + None, + pm::Optimizations::Allowed, + ); + body } -/// Builder for async_drop_in_place shim. Functions as a stack machine -/// to build up an expression using combinators. Stack contains pairs -/// of locals and types. Combinator is a not yet instantiated pair of a -/// function and a type, is considered to be an operator which consumes -/// operands from the stack by instantiating its function and its type -/// with operand types and moving locals into the function call. Top -/// pair is considered to be the last operand. -// FIXME: add mir-opt tests -struct AsyncDestructorCtorShimBuilder<'tcx> { +// build_drop_shim analog for async drop glue (for generated coroutine poll function) +pub(super) fn build_async_drop_shim<'tcx>( tcx: TyCtxt<'tcx>, def_id: DefId, - self_ty: Option<Ty<'tcx>>, - span: Span, - source_info: SourceInfo, - typing_env: ty::TypingEnv<'tcx>, - - stack: Vec<Operand<'tcx>>, - last_bb: BasicBlock, - top_cleanup_bb: Option<BasicBlock>, - - locals: IndexVec<Local, LocalDecl<'tcx>>, - bbs: IndexVec<BasicBlock, BasicBlockData<'tcx>>, -} - -#[derive(Clone, Copy)] -enum SurfaceDropKind { - Async, - Sync, -} - -impl<'tcx> AsyncDestructorCtorShimBuilder<'tcx> { - const SELF_PTR: Local = Local::from_u32(1); - const INPUT_COUNT: usize = 1; - const MAX_STACK_LEN: usize = 2; - - fn new(tcx: TyCtxt<'tcx>, def_id: DefId, self_ty: Option<Ty<'tcx>>) -> Self { - let args = if let Some(ty) = self_ty { - tcx.mk_args(&[ty.into()]) + ty: Ty<'tcx>, +) -> Body<'tcx> { + debug!("build_async_drop_shim(def_id={:?}, ty={:?})", def_id, ty); + let ty::Coroutine(_, parent_args) = ty.kind() else { + bug!(); + }; + let typing_env = ty::TypingEnv::fully_monomorphized(); + + let drop_ty = parent_args.first().unwrap().expect_ty(); + let drop_ptr_ty = Ty::new_mut_ptr(tcx, drop_ty); + + assert!(tcx.is_coroutine(def_id)); + let coroutine_kind = tcx.coroutine_kind(def_id).unwrap(); + + assert!(matches!( + coroutine_kind, + CoroutineKind::Desugared(CoroutineDesugaring::Async, CoroutineSource::Fn) + )); + + let needs_async_drop = drop_ty.needs_async_drop(tcx, typing_env); + let needs_sync_drop = !needs_async_drop && drop_ty.needs_drop(tcx, typing_env); + + let resume_adt = tcx.adt_def(tcx.require_lang_item(LangItem::ResumeTy, None)); + let resume_ty = Ty::new_adt(tcx, resume_adt, ty::List::empty()); + + let fn_sig = ty::Binder::dummy(tcx.mk_fn_sig( + [ty, resume_ty], + tcx.types.unit, + false, + Safety::Safe, + ExternAbi::Rust, + )); + let sig = tcx.instantiate_bound_regions_with_erased(fn_sig); + + assert!(!drop_ty.is_coroutine()); + let span = tcx.def_span(def_id); + let source_info = SourceInfo::outermost(span); + + // The first argument (index 0), but add 1 for the return value. + let coroutine_layout = Place::from(Local::new(1 + 0)); + let coroutine_layout_dropee = + tcx.mk_place_field(coroutine_layout, FieldIdx::new(0), drop_ptr_ty); + + let return_block = BasicBlock::new(1); + let mut blocks = IndexVec::with_capacity(2); + let block = |blocks: &mut IndexVec<_, _>, kind| { + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind }), + is_cleanup: false, + }) + }; + block( + &mut blocks, + if needs_sync_drop { + TerminatorKind::Drop { + place: tcx.mk_place_deref(coroutine_layout_dropee), + target: return_block, + unwind: UnwindAction::Continue, + replace: false, + drop: None, + async_fut: None, + } } else { - ty::GenericArgs::identity_for_item(tcx, def_id) - }; - let sig = tcx.fn_sig(def_id).instantiate(tcx, args); - let sig = tcx.instantiate_bound_regions_with_erased(sig); - let span = tcx.def_span(def_id); + TerminatorKind::Goto { target: return_block } + }, + ); + block(&mut blocks, TerminatorKind::Return); + + let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlue(def_id, ty)); + let mut body = + new_body(source, blocks, local_decls_for_sig(&sig, span), sig.inputs().len(), span); + + body.coroutine = Some(Box::new(CoroutineInfo::initial( + coroutine_kind, + parent_args.as_coroutine().yield_ty(), + parent_args.as_coroutine().resume_ty(), + ))); + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + if !needs_async_drop { + // Returning noop body for types without `need async drop` + // (or sync Drop in case of !`need async drop` && `need drop`) + return body; + } - let source_info = SourceInfo::outermost(span); + let mut dropee_ptr = Place::from(body.local_decls.push(LocalDecl::new(drop_ptr_ty, span))); + let st_kind = StatementKind::Assign(Box::new(( + dropee_ptr, + Rvalue::Use(Operand::Move(coroutine_layout_dropee)), + ))); + body.basic_blocks_mut()[START_BLOCK].statements.push(Statement { source_info, kind: st_kind }); + dropee_ptr = dropee_emit_retag(tcx, &mut body, dropee_ptr, span); - debug_assert_eq!(sig.inputs().len(), Self::INPUT_COUNT); - let locals = local_decls_for_sig(&sig, span); + let dropline = body.basic_blocks.last_index(); - // Usual case: noop() + unwind resume + return - let mut bbs = IndexVec::with_capacity(3); - let typing_env = ty::TypingEnv::post_analysis(tcx, def_id); - AsyncDestructorCtorShimBuilder { + let patch = { + let mut elaborator = DropShimElaborator { + body: &body, + patch: MirPatch::new(&body), tcx, - def_id, - self_ty, - span, - source_info, typing_env, - - stack: Vec::with_capacity(Self::MAX_STACK_LEN), - last_bb: bbs.push(BasicBlockData::new(None, false)), - top_cleanup_bb: match tcx.sess.panic_strategy() { - PanicStrategy::Unwind => { - // Don't drop input arg because it's just a pointer - Some(bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::UnwindResume, - }), - is_cleanup: true, - })) - } - PanicStrategy::Abort => None, - }, - - locals, - bbs, - } - } - - fn build(self) -> Body<'tcx> { - let (tcx, Some(self_ty)) = (self.tcx, self.self_ty) else { - return self.build_zst_output(); - }; - match self_ty.async_drop_glue_morphology(tcx) { - AsyncDropGlueMorphology::Noop => span_bug!( - self.span, - "async drop glue shim generator encountered type with noop async drop glue morphology" - ), - AsyncDropGlueMorphology::DeferredDropInPlace => { - return self.build_deferred_drop_in_place(); - } - AsyncDropGlueMorphology::Custom => (), - } - - let surface_drop_kind = || { - let adt_def = self_ty.ty_adt_def()?; - if adt_def.async_destructor(tcx).is_some() { - Some(SurfaceDropKind::Async) - } else if adt_def.destructor(tcx).is_some() { - Some(SurfaceDropKind::Sync) - } else { - None - } + produce_async_drops: true, }; + let dropee = tcx.mk_place_deref(dropee_ptr); + let resume_block = elaborator.patch.resume_block(); + elaborate_drop( + &mut elaborator, + source_info, + dropee, + (), + return_block, + Unwind::To(resume_block), + START_BLOCK, + dropline, + ); + elaborator.patch + }; + patch.apply(&mut body); - match self_ty.kind() { - ty::Array(elem_ty, _) => self.build_slice(true, *elem_ty), - ty::Slice(elem_ty) => self.build_slice(false, *elem_ty), - - ty::Tuple(elem_tys) => self.build_chain(None, elem_tys.iter()), - ty::Adt(adt_def, args) if adt_def.is_struct() => { - let field_tys = adt_def.non_enum_variant().fields.iter().map(|f| f.ty(tcx, args)); - self.build_chain(surface_drop_kind(), field_tys) - } - ty::Closure(_, args) => self.build_chain(None, args.as_closure().upvar_tys().iter()), - ty::CoroutineClosure(_, args) => { - self.build_chain(None, args.as_coroutine_closure().upvar_tys().iter()) - } + body +} - ty::Adt(adt_def, args) if adt_def.is_enum() => { - self.build_enum(*adt_def, *args, surface_drop_kind()) - } +// * For async drop a "normal" coroutine: +// `async_drop_in_place<T>::{closure}.poll()` is converted into `T.future_drop_poll()`. +// Every coroutine has its `poll` (calculate yourself a little further) +// and its `future_drop_poll` (drop yourself a little further). +// +// * For async drop of "async drop coroutine" (`async_drop_in_place<T>::{closure}`): +// Correct drop of such coroutine means normal execution of nested async drop. +// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll(). +pub(super) fn build_future_drop_poll_shim<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, +) -> Body<'tcx> { + let instance = ty::InstanceKind::FutureDropPollShim(def_id, proxy_ty, impl_ty); + let ty::Coroutine(coroutine_def_id, _) = impl_ty.kind() else { + bug!("build_future_drop_poll_shim not for coroutine impl type: ({:?})", instance); + }; - ty::Adt(adt_def, _) => { - assert!(adt_def.is_union()); - match surface_drop_kind().unwrap() { - SurfaceDropKind::Async => self.build_fused_async_surface(), - SurfaceDropKind::Sync => self.build_fused_sync_surface(), - } - } + let span = tcx.def_span(def_id); - ty::Bound(..) - | ty::Foreign(_) - | ty::Placeholder(_) - | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) | ty::TyVar(_)) - | ty::Param(_) - | ty::Alias(..) => { - bug!("Building async destructor for unexpected type: {self_ty:?}") - } - - _ => { - bug!( - "Building async destructor constructor shim is not yet implemented for type: {self_ty:?}" - ) - } - } + if tcx.is_async_drop_in_place_coroutine(*coroutine_def_id) { + build_adrop_for_adrop_shim(tcx, proxy_ty, impl_ty, span, instance) + } else { + build_adrop_for_coroutine_shim(tcx, proxy_ty, impl_ty, span, instance) } +} - fn build_enum( - mut self, - adt_def: ty::AdtDef<'tcx>, - args: ty::GenericArgsRef<'tcx>, - surface_drop: Option<SurfaceDropKind>, - ) -> Body<'tcx> { - let tcx = self.tcx; - - let surface = match surface_drop { - None => None, - Some(kind) => { - self.put_self(); - Some(match kind { - SurfaceDropKind::Async => self.combine_async_surface(), - SurfaceDropKind::Sync => self.combine_sync_surface(), - }) - } - }; - - let mut other = None; - for (variant_idx, discr) in adt_def.discriminants(tcx) { - let variant = adt_def.variant(variant_idx); - - let mut chain = None; - for (field_idx, field) in variant.fields.iter_enumerated() { - let field_ty = field.ty(tcx, args); - self.put_variant_field(variant.name, variant_idx, field_idx, field_ty); - let defer = self.combine_defer(field_ty); - chain = Some(match chain { - None => defer, - Some(chain) => self.combine_chain(chain, defer), - }) - } - let variant_dtor = chain.unwrap_or_else(|| self.put_noop()); - - other = Some(match other { - None => variant_dtor, - Some(other) => { - self.put_self(); - self.put_discr(discr); - self.combine_either(other, variant_dtor) - } - }); - } - let variants_dtor = other.unwrap_or_else(|| self.put_noop()); - - let dtor = match surface { - None => variants_dtor, - Some(surface) => self.combine_chain(surface, variants_dtor), - }; - self.combine_fuse(dtor); - self.return_() - } +// For async drop a "normal" coroutine: +// `async_drop_in_place<T>::{closure}.poll()` is converted into `T.future_drop_poll()`. +// Every coroutine has its `poll` (calculate yourself a little further) +// and its `future_drop_poll` (drop yourself a little further). +fn build_adrop_for_coroutine_shim<'tcx>( + tcx: TyCtxt<'tcx>, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, + span: Span, + instance: ty::InstanceKind<'tcx>, +) -> Body<'tcx> { + let ty::Coroutine(coroutine_def_id, impl_args) = impl_ty.kind() else { + bug!("build_adrop_for_coroutine_shim not for coroutine impl type: ({:?})", instance); + }; + let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty); + // taking _1.0 (impl from Pin) + let pin_proxy_layout_local = Local::new(1); + let source_info = SourceInfo::outermost(span); + // converting `(_1: Pin<&mut CorLayout>, _2: &mut Context<'_>) -> Poll<()>` + // into `(_1: Pin<&mut ProxyLayout>, _2: &mut Context<'_>) -> Poll<()>` + // let mut _x: &mut CorLayout = &*_1.0.0; + // Replace old _1.0 accesses into _x accesses; + let body = tcx.optimized_mir(*coroutine_def_id).future_drop_poll().unwrap(); + let mut body: Body<'tcx> = EarlyBinder::bind(body.clone()).instantiate(tcx, impl_args); + body.source.instance = instance; + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + body.var_debug_info.clear(); + let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, Some(span))); + let args = tcx.mk_args(&[proxy_ref.into()]); + let pin_proxy_ref = Ty::new_adt(tcx, pin_adt_ref, args); + + let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty); + + let proxy_ref_local = body.local_decls.push(LocalDecl::new(proxy_ref, span)); + let cor_ref_local = body.local_decls.push(LocalDecl::new(cor_ref, span)); + + FixProxyFutureDropVisitor { tcx, replace_to: cor_ref_local }.visit_body(&mut body); + // Now changing first arg from Pin<&mut ImplCoroutine> to Pin<&mut ProxyCoroutine> + body.local_decls[pin_proxy_layout_local] = LocalDecl::new(pin_proxy_ref, span); - fn build_chain<I>(mut self, surface_drop: Option<SurfaceDropKind>, elem_tys: I) -> Body<'tcx> - where - I: Iterator<Item = Ty<'tcx>> + ExactSizeIterator, { - let surface = match surface_drop { - None => None, - Some(kind) => { - self.put_self(); - Some(match kind { - SurfaceDropKind::Async => self.combine_async_surface(), - SurfaceDropKind::Sync => self.combine_sync_surface(), - }) - } - }; - - let mut chain = None; - for (field_idx, field_ty) in elem_tys.enumerate().map(|(i, ty)| (FieldIdx::new(i), ty)) { - self.put_field(field_idx, field_ty); - let defer = self.combine_defer(field_ty); - chain = Some(match chain { - None => defer, - Some(chain) => self.combine_chain(chain, defer), - }) - } - let chain = chain.unwrap_or_else(|| self.put_noop()); - - let dtor = match surface { - None => chain, - Some(surface) => self.combine_chain(surface, chain), - }; - self.combine_fuse(dtor); - self.return_() - } - - fn build_zst_output(mut self) -> Body<'tcx> { - self.put_zst_output(); - self.return_() - } - - fn build_deferred_drop_in_place(mut self) -> Body<'tcx> { - self.put_self(); - let deferred = self.combine_deferred_drop_in_place(); - self.combine_fuse(deferred); - self.return_() - } - - fn build_fused_async_surface(mut self) -> Body<'tcx> { - self.put_self(); - let surface = self.combine_async_surface(); - self.combine_fuse(surface); - self.return_() - } - - fn build_fused_sync_surface(mut self) -> Body<'tcx> { - self.put_self(); - let surface = self.combine_sync_surface(); - self.combine_fuse(surface); - self.return_() - } - - fn build_slice(mut self, is_array: bool, elem_ty: Ty<'tcx>) -> Body<'tcx> { - if is_array { - self.put_array_as_slice(elem_ty) - } else { - self.put_self() - } - let dtor = self.combine_slice(elem_ty); - self.combine_fuse(dtor); - self.return_() - } - - fn put_zst_output(&mut self) { - let return_ty = self.locals[RETURN_PLACE].ty; - self.put_operand(Operand::Constant(Box::new(ConstOperand { - span: self.span, - user_ty: None, - const_: Const::zero_sized(return_ty), - }))); - } - - /// Puts `to_drop: *mut Self` on top of the stack. - fn put_self(&mut self) { - self.put_operand(Operand::Copy(Self::SELF_PTR.into())) - } - - /// Given that `Self is [ElemTy; N]` puts `to_drop: *mut [ElemTy]` - /// on top of the stack. - fn put_array_as_slice(&mut self, elem_ty: Ty<'tcx>) { - let slice_ptr_ty = Ty::new_mut_ptr(self.tcx, Ty::new_slice(self.tcx, elem_ty)); - self.put_temp_rvalue(Rvalue::Cast( - CastKind::PointerCoercion(PointerCoercion::Unsize, CoercionSource::Implicit), - Operand::Copy(Self::SELF_PTR.into()), - slice_ptr_ty, - )) - } - - /// If given Self is a struct puts `to_drop: *mut FieldTy` on top - /// of the stack. - fn put_field(&mut self, field: FieldIdx, field_ty: Ty<'tcx>) { - let place = Place { - local: Self::SELF_PTR, - projection: self - .tcx - .mk_place_elems(&[PlaceElem::Deref, PlaceElem::Field(field, field_ty)]), - }; - self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) - } - - /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of - /// the stack. - fn put_variant_field( - &mut self, - variant_sym: Symbol, - variant: VariantIdx, - field: FieldIdx, - field_ty: Ty<'tcx>, - ) { - let place = Place { - local: Self::SELF_PTR, - projection: self.tcx.mk_place_elems(&[ - PlaceElem::Deref, - PlaceElem::Downcast(Some(variant_sym), variant), - PlaceElem::Field(field, field_ty), - ]), - }; - self.put_temp_rvalue(Rvalue::RawPtr(RawPtrKind::Mut, place)) - } - - /// If given Self is an enum puts `to_drop: *mut FieldTy` on top of - /// the stack. - fn put_discr(&mut self, discr: Discr<'tcx>) { - let (size, _) = discr.ty.int_size_and_signed(self.tcx); - self.put_operand(Operand::const_from_scalar( - self.tcx, - discr.ty, - interpret::Scalar::from_uint(discr.val, size), - self.span, - )); - } - - /// Puts `x: RvalueType` on top of the stack. - fn put_temp_rvalue(&mut self, rvalue: Rvalue<'tcx>) { - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - let source_info = self.source_info; - - let local_ty = rvalue.ty(&self.locals, self.tcx); - // We need to create a new local to be able to "consume" it with - // a combinator - let local = self.locals.push(LocalDecl::with_source_info(local_ty, source_info)); - last_bb.statements.extend_from_slice(&[ - Statement { source_info, kind: StatementKind::StorageLive(local) }, + let mut idx: usize = 0; + // _proxy = _1.0 : Pin<&ProxyLayout> ==> &ProxyLayout + let proxy_ref_place = Place::from(pin_proxy_layout_local) + .project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx); + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, Statement { source_info, - kind: StatementKind::Assign(Box::new((local.into(), rvalue))), + kind: StatementKind::Assign(Box::new(( + Place::from(proxy_ref_local), + Rvalue::CopyForDeref(proxy_ref_place), + ))), }, - ]); - - self.put_operand(Operand::Move(local.into())); - } - - /// Puts operand on top of the stack. - fn put_operand(&mut self, operand: Operand<'tcx>) { - if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { - let source_info = self.source_info; - match &operand { - Operand::Copy(_) | Operand::Constant(_) => { - *top_cleanup_bb = self.bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: TerminatorKind::Goto { target: *top_cleanup_bb }, - }), - is_cleanup: true, - }); - } - Operand::Move(place) => { - let local = place.as_local().unwrap(); - *top_cleanup_bb = self.bbs.push(BasicBlockData { - statements: Vec::new(), - terminator: Some(Terminator { - source_info, - kind: if self.locals[local].ty.needs_drop(self.tcx, self.typing_env) { - TerminatorKind::Drop { - place: local.into(), - target: *top_cleanup_bb, - unwind: UnwindAction::Terminate( - UnwindTerminateReason::InCleanup, - ), - replace: false, - } - } else { - TerminatorKind::Goto { target: *top_cleanup_bb } - }, - }), - is_cleanup: true, - }); - } - }; - } - self.stack.push(operand); - } - - /// Puts `noop: async_drop::Noop` on top of the stack - fn put_noop(&mut self) -> Ty<'tcx> { - self.apply_combinator(0, LangItem::AsyncDropNoop, &[]) - } - - fn combine_async_surface(&mut self) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::SurfaceAsyncDropInPlace, &[self.self_ty.unwrap().into()]) - } - - fn combine_sync_surface(&mut self) -> Ty<'tcx> { - self.apply_combinator( - 1, - LangItem::AsyncDropSurfaceDropInPlace, - &[self.self_ty.unwrap().into()], - ) - } - - fn combine_deferred_drop_in_place(&mut self) -> Ty<'tcx> { - self.apply_combinator( - 1, - LangItem::AsyncDropDeferredDropInPlace, - &[self.self_ty.unwrap().into()], - ) - } - - fn combine_fuse(&mut self, inner_future_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropFuse, &[inner_future_ty.into()]) - } - - fn combine_slice(&mut self, elem_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropSlice, &[elem_ty.into()]) - } - - fn combine_defer(&mut self, to_drop_ty: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(1, LangItem::AsyncDropDefer, &[to_drop_ty.into()]) - } - - fn combine_chain(&mut self, first: Ty<'tcx>, second: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator(2, LangItem::AsyncDropChain, &[first.into(), second.into()]) - } - - fn combine_either(&mut self, other: Ty<'tcx>, matched: Ty<'tcx>) -> Ty<'tcx> { - self.apply_combinator( - 4, - LangItem::AsyncDropEither, - &[other.into(), matched.into(), self.self_ty.unwrap().into()], - ) - } - - fn return_(mut self) -> Body<'tcx> { - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - let source_info = self.source_info; - - let (1, Some(output)) = (self.stack.len(), self.stack.pop()) else { - span_bug!( - self.span, - "async destructor ctor shim builder finished with invalid number of stack items: expected 1 found {}", - self.stack.len(), - ) - }; - #[cfg(debug_assertions)] - if let Some(ty) = self.self_ty { - debug_assert_eq!( - output.ty(&self.locals, self.tcx), - ty.async_destructor_ty(self.tcx), - "output async destructor types did not match for type: {ty:?}", - ); - } - - let dead_storage = match &output { - Operand::Move(place) => Some(Statement { - source_info, - kind: StatementKind::StorageDead(place.as_local().unwrap()), - }), - _ => None, - }; - - last_bb.statements.extend( - iter::once(Statement { - source_info, - kind: StatementKind::Assign(Box::new((RETURN_PLACE.into(), Rvalue::Use(output)))), - }) - .chain(dead_storage), ); - - last_bb.terminator = Some(Terminator { source_info, kind: TerminatorKind::Return }); - - let source = MirSource::from_instance(ty::InstanceKind::AsyncDropGlueCtorShim( - self.def_id, - self.self_ty, - )); - new_body(source, self.bbs, self.locals, Self::INPUT_COUNT, self.span) - } - - fn apply_combinator( - &mut self, - arity: usize, - function: LangItem, - args: &[ty::GenericArg<'tcx>], - ) -> Ty<'tcx> { - let function = self.tcx.require_lang_item(function, Some(self.span)); - let operands_split = self - .stack - .len() - .checked_sub(arity) - .expect("async destructor ctor shim combinator tried to consume too many items"); - let operands = &self.stack[operands_split..]; - - let func_ty = Ty::new_fn_def(self.tcx, function, args.iter().copied()); - let func_sig = func_ty.fn_sig(self.tcx).no_bound_vars().unwrap(); - #[cfg(debug_assertions)] - operands.iter().zip(func_sig.inputs()).for_each(|(operand, expected_ty)| { - let operand_ty = operand.ty(&self.locals, self.tcx); - if operand_ty == *expected_ty { - return; - } - - // If projection of Discriminant then compare with `Ty::discriminant_ty` - if let ty::Alias(ty::Projection, ty::AliasTy { args, def_id, .. }) = expected_ty.kind() - && self.tcx.is_lang_item(*def_id, LangItem::Discriminant) - && args.first().unwrap().as_type().unwrap().discriminant_ty(self.tcx) == operand_ty - { - return; + idx += 1; + let mut cor_ptr_local = proxy_ref_local; + proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| { + if ty != proxy_ty { + let ty_ptr = Ty::new_mut_ptr(tcx, ty); + let impl_ptr_place = Place::from(cor_ptr_local).project_deeper( + &[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], + tcx, + ); + cor_ptr_local = body.local_decls.push(LocalDecl::new(ty_ptr, span)); + // _cor_ptr = _proxy.0.0 (... .0) + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, + Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(cor_ptr_local), + Rvalue::CopyForDeref(impl_ptr_place), + ))), + }, + ); + idx += 1; } - - span_bug!( - self.span, - "Operand type and combinator argument type are not equal. - operand_ty: {:?} - argument_ty: {:?} -", - operand_ty, - expected_ty - ); - }); - - let target = self.bbs.push(BasicBlockData { - statements: operands - .iter() - .rev() - .filter_map(|o| { - if let Operand::Move(Place { local, projection }) = o { - assert!(projection.is_empty()); - Some(Statement { - source_info: self.source_info, - kind: StatementKind::StorageDead(*local), - }) - } else { - None - } - }) - .collect(), - terminator: None, - is_cleanup: false, }); - let dest_ty = func_sig.output(); - let dest = - self.locals.push(LocalDecl::with_source_info(dest_ty, self.source_info).immutable()); - - let unwind = if let Some(top_cleanup_bb) = &mut self.top_cleanup_bb { - for _ in 0..arity { - *top_cleanup_bb = - self.bbs[*top_cleanup_bb].terminator().successors().exactly_one().ok().unwrap(); - } - UnwindAction::Cleanup(*top_cleanup_bb) - } else { - UnwindAction::Unreachable - }; + // _cor_ref = &*cor_ptr + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(Place::from(cor_ptr_local)), + ); + body.basic_blocks_mut()[START_BLOCK].statements.insert( + idx, + Statement { + source_info, + kind: StatementKind::Assign(Box::new((Place::from(cor_ref_local), reborrow))), + }, + ); + } + body +} - let last_bb = &mut self.bbs[self.last_bb]; - debug_assert!(last_bb.terminator.is_none()); - last_bb.statements.push(Statement { - source_info: self.source_info, - kind: StatementKind::StorageLive(dest), - }); - last_bb.terminator = Some(Terminator { - source_info: self.source_info, +// When dropping async drop coroutine, we continue its execution. +// async_drop(async_drop(T))::future_drop_poll() => async_drop(T)::poll() +fn build_adrop_for_adrop_shim<'tcx>( + tcx: TyCtxt<'tcx>, + proxy_ty: Ty<'tcx>, + impl_ty: Ty<'tcx>, + span: Span, + instance: ty::InstanceKind<'tcx>, +) -> Body<'tcx> { + let source_info = SourceInfo::outermost(span); + let proxy_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, proxy_ty); + // taking _1.0 (impl from Pin) + let pin_proxy_layout_local = Local::new(1); + let proxy_ref_place = Place::from(pin_proxy_layout_local) + .project_deeper(&[PlaceElem::Field(FieldIdx::ZERO, proxy_ref)], tcx); + let cor_ref = Ty::new_mut_ref(tcx, tcx.lifetimes.re_erased, impl_ty); + + // ret_ty = `Poll<()>` + let poll_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Poll, None)); + let ret_ty = Ty::new_adt(tcx, poll_adt_ref, tcx.mk_args(&[tcx.types.unit.into()])); + // env_ty = `Pin<&mut proxy_ty>` + let pin_adt_ref = tcx.adt_def(tcx.require_lang_item(LangItem::Pin, None)); + let env_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[proxy_ref.into()])); + // sig = `fn (Pin<&mut proxy_ty>, &mut Context) -> Poll<()>` + let sig = tcx.mk_fn_sig( + [env_ty, Ty::new_task_context(tcx)], + ret_ty, + false, + hir::Safety::Safe, + ExternAbi::Rust, + ); + // This function will be called with pinned proxy coroutine layout. + // We need to extract `Arg0.0` to get proxy layout, and then get `.0` + // further to receive impl coroutine (may be needed) + let mut locals = local_decls_for_sig(&sig, span); + let mut blocks = IndexVec::with_capacity(3); + + let proxy_ref_local = locals.push(LocalDecl::new(proxy_ref, span)); + + let call_bb = BasicBlock::new(1); + let return_bb = BasicBlock::new(2); + + let mut statements = Vec::new(); + + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(proxy_ref_local), + Rvalue::CopyForDeref(proxy_ref_place), + ))), + }); + + let mut cor_ptr_local = proxy_ref_local; + proxy_ty.find_async_drop_impl_coroutine(tcx, |ty| { + if ty != proxy_ty { + let ty_ptr = Ty::new_mut_ptr(tcx, ty); + let impl_ptr_place = Place::from(cor_ptr_local) + .project_deeper(&[PlaceElem::Deref, PlaceElem::Field(FieldIdx::ZERO, ty_ptr)], tcx); + cor_ptr_local = locals.push(LocalDecl::new(ty_ptr, span)); + // _cor_ptr = _proxy.0.0 (... .0) + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new(( + Place::from(cor_ptr_local), + Rvalue::CopyForDeref(impl_ptr_place), + ))), + }); + } + }); + + // convert impl coroutine ptr into ref + let reborrow = Rvalue::Ref( + tcx.lifetimes.re_erased, + BorrowKind::Mut { kind: MutBorrowKind::Default }, + tcx.mk_place_deref(Place::from(cor_ptr_local)), + ); + let cor_ref_place = Place::from(locals.push(LocalDecl::new(cor_ref, span))); + statements.push(Statement { + source_info, + kind: StatementKind::Assign(Box::new((cor_ref_place, reborrow))), + }); + + // cor_pin_ty = `Pin<&mut cor_ref>` + let cor_pin_ty = Ty::new_adt(tcx, pin_adt_ref, tcx.mk_args(&[cor_ref.into()])); + let cor_pin_place = Place::from(locals.push(LocalDecl::new(cor_pin_ty, span))); + + let pin_fn = tcx.require_lang_item(LangItem::PinNewUnchecked, Some(span)); + // call Pin<FutTy>::new_unchecked(&mut impl_cor) + blocks.push(BasicBlockData { + statements, + terminator: Some(Terminator { + source_info, kind: TerminatorKind::Call { - func: Operand::Constant(Box::new(ConstOperand { - span: self.span, - user_ty: None, - const_: Const::Val(ConstValue::ZeroSized, func_ty), - })), - destination: dest.into(), - target: Some(target), - unwind, + func: Operand::function_handle(tcx, pin_fn, [cor_ref.into()], span), + args: [dummy_spanned(Operand::Move(cor_ref_place))].into(), + destination: cor_pin_place, + target: Some(call_bb), + unwind: UnwindAction::Continue, call_source: CallSource::Misc, - fn_span: self.span, - args: self.stack.drain(operands_split..).map(|o| respan(self.span, o)).collect(), + fn_span: span, }, - }); - - self.put_operand(Operand::Move(dest.into())); - self.last_bb = target; - - dest_ty - } + }), + is_cleanup: false, + }); + // When dropping async drop coroutine, we continue its execution: + // we call impl::poll (impl_layout, ctx) + let poll_fn = tcx.require_lang_item(LangItem::FuturePoll, None); + let resume_ctx = Place::from(Local::new(2)); + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { + source_info, + kind: TerminatorKind::Call { + func: Operand::function_handle(tcx, poll_fn, [impl_ty.into()], span), + args: [ + dummy_spanned(Operand::Move(cor_pin_place)), + dummy_spanned(Operand::Move(resume_ctx)), + ] + .into(), + destination: Place::return_place(), + target: Some(return_bb), + unwind: UnwindAction::Continue, + call_source: CallSource::Misc, + fn_span: span, + }, + }), + is_cleanup: false, + }); + blocks.push(BasicBlockData { + statements: vec![], + terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }), + is_cleanup: false, + }); + + let source = MirSource::from_instance(instance); + let mut body = new_body(source, blocks, locals, sig.inputs().len(), span); + body.phase = MirPhase::Runtime(RuntimePhase::Initial); + return body; } diff --git a/compiler/rustc_mir_transform/src/validate.rs b/compiler/rustc_mir_transform/src/validate.rs index 66fe3ef4141..f541a32cd26 100644 --- a/compiler/rustc_mir_transform/src/validate.rs +++ b/compiler/rustc_mir_transform/src/validate.rs @@ -372,9 +372,12 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { ); } } - TerminatorKind::Drop { target, unwind, .. } => { + TerminatorKind::Drop { target, unwind, drop, .. } => { self.check_edge(location, *target, EdgeKind::Normal); self.check_unwind_edge(location, *unwind); + if let Some(drop) = drop { + self.check_edge(location, *drop, EdgeKind::Normal); + } } TerminatorKind::Call { func, args, .. } | TerminatorKind::TailCall { func, args, .. } => { @@ -747,7 +750,9 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // since we may be in the process of computing this MIR in the // first place. let layout = if def_id == self.caller_body.source.def_id() { - self.caller_body.coroutine_layout_raw() + self.caller_body + .coroutine_layout_raw() + .or_else(|| self.tcx.coroutine_layout(def_id, args)) } else if self.tcx.needs_coroutine_by_move_body_def_id(def_id) && let ty::ClosureKind::FnOnce = args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap() @@ -757,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { // Same if this is the by-move body of a coroutine-closure. self.caller_body.coroutine_layout_raw() } else { - self.tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) + self.tcx.coroutine_layout(def_id, args) }; let Some(layout) = layout else { diff --git a/compiler/rustc_monomorphize/src/collector.rs b/compiler/rustc_monomorphize/src/collector.rs index d1d0f7cacae..76dad6b3571 100644 --- a/compiler/rustc_monomorphize/src/collector.rs +++ b/compiler/rustc_monomorphize/src/collector.rs @@ -929,14 +929,16 @@ fn visit_instance_use<'tcx>( ty::InstanceKind::ThreadLocalShim(..) => { bug!("{:?} being reified", instance); } - ty::InstanceKind::DropGlue(_, None) | ty::InstanceKind::AsyncDropGlueCtorShim(_, None) => { + ty::InstanceKind::DropGlue(_, None) => { // Don't need to emit noop drop glue if we are calling directly. if !is_direct_call { output.push(create_fn_mono_item(tcx, instance, source)); } } ty::InstanceKind::DropGlue(_, Some(_)) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, Some(_)) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(_, _) + | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) | ty::InstanceKind::VTableShim(..) | ty::InstanceKind::ReifyShim(..) | ty::InstanceKind::ClosureOnceShim { .. } diff --git a/compiler/rustc_monomorphize/src/partitioning.rs b/compiler/rustc_monomorphize/src/partitioning.rs index 6bc8a0fc88c..6948dceddf9 100644 --- a/compiler/rustc_monomorphize/src/partitioning.rs +++ b/compiler/rustc_monomorphize/src/partitioning.rs @@ -644,6 +644,8 @@ fn characteristic_def_id_of_mono_item<'tcx>( | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::ThreadLocalShim(..) | ty::InstanceKind::FnPtrAddrShim(..) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) => return None, }; @@ -796,7 +798,9 @@ fn mono_item_visibility<'tcx>( let def_id = match instance.def { InstanceKind::Item(def_id) | InstanceKind::DropGlue(def_id, Some(_)) - | InstanceKind::AsyncDropGlueCtorShim(def_id, Some(_)) => def_id, + | InstanceKind::FutureDropPollShim(def_id, _, _) + | InstanceKind::AsyncDropGlue(def_id, _) + | InstanceKind::AsyncDropGlueCtorShim(def_id, _) => def_id, // We match the visibility of statics here InstanceKind::ThreadLocalShim(def_id) => { @@ -812,7 +816,6 @@ fn mono_item_visibility<'tcx>( | InstanceKind::ClosureOnceShim { .. } | InstanceKind::ConstructCoroutineInClosureShim { .. } | InstanceKind::DropGlue(..) - | InstanceKind::AsyncDropGlueCtorShim(..) | InstanceKind::CloneShim(..) | InstanceKind::FnPtrAddrShim(..) => return Visibility::Hidden, }; diff --git a/compiler/rustc_next_trait_solver/src/delegate.rs b/compiler/rustc_next_trait_solver/src/delegate.rs index c4b6b18c45d..25493970a0c 100644 --- a/compiler/rustc_next_trait_solver/src/delegate.rs +++ b/compiler/rustc_next_trait_solver/src/delegate.rs @@ -36,7 +36,7 @@ pub trait SolverDelegate: Deref<Target = Self::Infcx> + Sized { fn well_formed_goals( &self, param_env: <Self::Interner as Interner>::ParamEnv, - arg: <Self::Interner as Interner>::GenericArg, + term: <Self::Interner as Interner>::Term, ) -> Option<Vec<Goal<Self::Interner, <Self::Interner as Interner>::Predicate>>>; fn clone_opaque_types_for_query_response( diff --git a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs index 6a817c37737..101129a231e 100644 --- a/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/assembly/mod.rs @@ -10,6 +10,7 @@ use rustc_type_ir::{ }; use tracing::{debug, instrument}; +use super::has_only_region_constraints; use super::trait_goals::TraitGoalProvenVia; use crate::delegate::SolverDelegate; use crate::solve::inspect::ProbeKind; @@ -255,11 +256,6 @@ where goal: Goal<I, Self>, ) -> Result<Candidate<I>, NoSolution>; - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution>; - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, @@ -476,9 +472,6 @@ where Some(TraitSolverLangItem::DiscriminantKind) => { G::consider_builtin_discriminant_kind_candidate(self, goal) } - Some(TraitSolverLangItem::AsyncDestruct) => { - G::consider_builtin_async_destruct_candidate(self, goal) - } Some(TraitSolverLangItem::Destruct) => { G::consider_builtin_destruct_candidate(self, goal) } @@ -771,6 +764,69 @@ where } }) } +} + +pub(super) enum AllowInferenceConstraints { + Yes, + No, +} + +impl<D, I> EvalCtxt<'_, D> +where + D: SolverDelegate<Interner = I>, + I: Interner, +{ + /// Check whether we can ignore impl candidates due to specialization. + /// + /// This is only necessary for `feature(specialization)` and seems quite ugly. + pub(super) fn filter_specialized_impls( + &mut self, + allow_inference_constraints: AllowInferenceConstraints, + candidates: &mut Vec<Candidate<I>>, + ) { + match self.typing_mode() { + TypingMode::Coherence => return, + TypingMode::Analysis { .. } + | TypingMode::Borrowck { .. } + | TypingMode::PostBorrowckAnalysis { .. } + | TypingMode::PostAnalysis => {} + } + + let mut i = 0; + 'outer: while i < candidates.len() { + let CandidateSource::Impl(victim_def_id) = candidates[i].source else { + i += 1; + continue; + }; + + for (j, c) in candidates.iter().enumerate() { + if i == j { + continue; + } + + let CandidateSource::Impl(other_def_id) = c.source else { + continue; + }; + + // See if we can toss out `victim` based on specialization. + // + // While this requires us to know *for sure* that the `lhs` impl applies + // we still use modulo regions here. This is fine as specialization currently + // assumes that specializing impls have to be always applicable, meaning that + // the only allowed region constraints may be constraints also present on the default impl. + if matches!(allow_inference_constraints, AllowInferenceConstraints::Yes) + || has_only_region_constraints(c.result) + { + if self.cx().impl_specializes(other_def_id, victim_def_id) { + candidates.remove(i); + continue 'outer; + } + } + } + + i += 1; + } + } /// Assemble and merge candidates for goals which are related to an underlying trait /// goal. Right now, this is normalizes-to and host effect goals. @@ -857,7 +913,7 @@ where } } TraitGoalProvenVia::Misc => { - let candidates = + let mut candidates = self.assemble_and_evaluate_candidates(goal, AssembleCandidatesFrom::All); // Prefer "orphaned" param-env normalization predicates, which are used @@ -871,6 +927,13 @@ where return Ok(response); } + // We drop specialized impls to allow normalization via a final impl here. In case + // the specializing impl has different inference constraints from the specialized + // impl, proving the trait goal is already ambiguous, so we never get here. This + // means we can just ignore inference constraints and don't have to special-case + // constraining the normalized-to `term`. + self.filter_specialized_impls(AllowInferenceConstraints::Yes, &mut candidates); + let responses: Vec<_> = candidates.iter().map(|c| c.result).collect(); if let Some(response) = self.try_merge_responses(&responses) { Ok(response) diff --git a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs index 7752a705cd1..5edc777262b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/effect_goals.rs @@ -336,13 +336,6 @@ where unreachable!("DiscriminantKind is not const") } - fn consider_builtin_async_destruct_candidate( - _ecx: &mut EvalCtxt<'_, D>, - _goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - unreachable!("AsyncDestruct is not const") - } - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs index 04f80a056f9..dded84f6768 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/canonical.rs @@ -81,12 +81,19 @@ where /// the values inferred while solving the instantiated goal. /// - `external_constraints`: additional constraints which aren't expressible /// using simple unification of inference variables. + /// + /// This takes the `shallow_certainty` which represents whether we're confident + /// that the final result of the current goal only depends on the nested goals. + /// + /// In case this is `Certainy::Maybe`, there may still be additional nested goals + /// or inference constraints required for this candidate to be hold. The candidate + /// always requires all already added constraints and nested goals. #[instrument(level = "trace", skip(self), ret)] pub(in crate::solve) fn evaluate_added_goals_and_make_canonical_response( &mut self, - certainty: Certainty, + shallow_certainty: Certainty, ) -> QueryResult<I> { - self.inspect.make_canonical_response(certainty); + self.inspect.make_canonical_response(shallow_certainty); let goals_certainty = self.try_evaluate_added_goals()?; assert_eq!( @@ -103,26 +110,29 @@ where NoSolution })?; - // When normalizing, we've replaced the expected term with an unconstrained - // inference variable. This means that we dropped information which could - // have been important. We handle this by instead returning the nested goals - // to the caller, where they are then handled. - // - // As we return all ambiguous nested goals, we can ignore the certainty returned - // by `try_evaluate_added_goals()`. - let (certainty, normalization_nested_goals) = match self.current_goal_kind { - CurrentGoalKind::NormalizesTo => { - let goals = std::mem::take(&mut self.nested_goals); - if goals.is_empty() { - assert!(matches!(goals_certainty, Certainty::Yes)); + let (certainty, normalization_nested_goals) = + match (self.current_goal_kind, shallow_certainty) { + // When normalizing, we've replaced the expected term with an unconstrained + // inference variable. This means that we dropped information which could + // have been important. We handle this by instead returning the nested goals + // to the caller, where they are then handled. We only do so if we do not + // need to recompute the `NormalizesTo` goal afterwards to avoid repeatedly + // uplifting its nested goals. This is the case if the `shallow_certainty` is + // `Certainty::Yes`. + (CurrentGoalKind::NormalizesTo, Certainty::Yes) => { + let goals = std::mem::take(&mut self.nested_goals); + // As we return all ambiguous nested goals, we can ignore the certainty + // returned by `self.try_evaluate_added_goals()`. + if goals.is_empty() { + assert!(matches!(goals_certainty, Certainty::Yes)); + } + (Certainty::Yes, NestedNormalizationGoals(goals)) } - (certainty, NestedNormalizationGoals(goals)) - } - CurrentGoalKind::Misc | CurrentGoalKind::CoinductiveTrait => { - let certainty = certainty.unify_with(goals_certainty); - (certainty, NestedNormalizationGoals::empty()) - } - }; + _ => { + let certainty = shallow_certainty.unify_with(goals_certainty); + (certainty, NestedNormalizationGoals::empty()) + } + }; if let Certainty::Maybe(cause @ MaybeCause::Overflow { .. }) = certainty { // If we have overflow, it's probable that we're substituting a type diff --git a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs index 27ca8787db5..6dd554299a6 100644 --- a/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/eval_ctxt/mod.rs @@ -16,6 +16,7 @@ use rustc_type_ir::{ }; use tracing::{instrument, trace}; +use super::has_only_region_constraints; use crate::coherence; use crate::delegate::SolverDelegate; use crate::solve::inspect::{self, ProofTreeBuilder}; @@ -476,13 +477,8 @@ where Ok(response) => response, }; - let has_changed = if !response.value.var_values.is_identity_modulo_regions() - || !response.value.external_constraints.opaque_types.is_empty() - { - HasChanged::Yes - } else { - HasChanged::No - }; + let has_changed = + if !has_only_region_constraints(response) { HasChanged::Yes } else { HasChanged::No }; let (normalization_nested_goals, certainty) = self.instantiate_and_apply_query_response(goal.param_env, orig_values, response); @@ -533,8 +529,8 @@ where ty::PredicateKind::DynCompatible(trait_def_id) => { self.compute_dyn_compatible_goal(trait_def_id) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.compute_well_formed_goal(Goal { param_env, predicate: arg }) + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.compute_well_formed_goal(Goal { param_env, predicate: term }) } ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(ct)) => { self.compute_const_evaluatable_goal(Goal { param_env, predicate: ct }) @@ -1012,9 +1008,9 @@ where pub(super) fn well_formed_goals( &self, param_env: I::ParamEnv, - arg: I::GenericArg, + term: I::Term, ) -> Option<Vec<Goal<I, I::Predicate>>> { - self.delegate.well_formed_goals(param_env, arg) + self.delegate.well_formed_goals(param_env, term) } pub(super) fn trait_ref_is_knowable( diff --git a/compiler/rustc_next_trait_solver/src/solve/mod.rs b/compiler/rustc_next_trait_solver/src/solve/mod.rs index 0695c5acdca..c9f4fc649b5 100644 --- a/compiler/rustc_next_trait_solver/src/solve/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/mod.rs @@ -70,6 +70,17 @@ fn has_no_inference_or_external_constraints<I: Interner>( && normalization_nested_goals.is_empty() } +fn has_only_region_constraints<I: Interner>(response: ty::Canonical<I, Response<I>>) -> bool { + let ExternalConstraintsData { + region_constraints: _, + ref opaque_types, + ref normalization_nested_goals, + } = *response.value.external_constraints; + response.value.var_values.is_identity_modulo_regions() + && opaque_types.is_empty() + && normalization_nested_goals.is_empty() +} + impl<'a, D, I> EvalCtxt<'a, D> where D: SolverDelegate<Interner = I>, @@ -126,7 +137,7 @@ where } #[instrument(level = "trace", skip(self))] - fn compute_well_formed_goal(&mut self, goal: Goal<I, I::GenericArg>) -> QueryResult<I> { + fn compute_well_formed_goal(&mut self, goal: Goal<I, I::Term>) -> QueryResult<I> { match self.well_formed_goals(goal.param_env, goal.predicate) { Some(goals) => { self.add_goals(GoalSource::Misc, goals); diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs index 9c24d4336e2..b030af48381 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/mod.rs @@ -6,7 +6,7 @@ mod opaque_types; use rustc_type_ir::fast_reject::DeepRejectCtxt; use rustc_type_ir::inherent::*; use rustc_type_ir::lang_items::TraitSolverLangItem; -use rustc_type_ir::{self as ty, Interner, NormalizesTo, Upcast as _}; +use rustc_type_ir::{self as ty, Interner, NormalizesTo, PredicateKind, Upcast as _}; use tracing::instrument; use crate::delegate::SolverDelegate; @@ -194,6 +194,12 @@ where .map(|pred| goal.with(cx, pred)); ecx.add_goals(GoalSource::ImplWhereBound, where_clause_bounds); + // Bail if the nested goals don't hold here. This is to avoid unnecessarily + // computing the `type_of` query for associated types that never apply, as + // this may result in query cycles in the case of RPITITs. + // See <https://github.com/rust-lang/trait-system-refactor-initiative/issues/185>. + ecx.try_evaluate_added_goals()?; + // Add GAT where clauses from the trait's definition. // FIXME: We don't need these, since these are the type's own WF obligations. ecx.add_goals( @@ -213,9 +219,6 @@ where ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) }; - // In case the associated item is hidden due to specialization, we have to - // return ambiguity this would otherwise be incomplete, resulting in - // unsoundness during coherence (#105782). let target_item_def_id = match ecx.fetch_eligible_assoc_item( goal_trait_ref, goal.predicate.def_id(), @@ -223,8 +226,36 @@ where ) { Ok(Some(target_item_def_id)) => target_item_def_id, Ok(None) => { - return ecx - .evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS); + match ecx.typing_mode() { + // In case the associated item is hidden due to specialization, + // normalizing this associated item is always ambiguous. Treating + // the associated item as rigid would be incomplete and allow for + // overlapping impls, see #105782. + // + // As this ambiguity is unavoidable we emit a nested ambiguous + // goal instead of using `Certainty::AMBIGUOUS`. This allows us to + // return the nested goals to the parent `AliasRelate` goal. This + // would be relevant if any of the nested goals refer to the `term`. + // This is not the case here and we only prefer adding an ambiguous + // nested goal for consistency. + ty::TypingMode::Coherence => { + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + // Outside of coherence, we treat the associated item as rigid instead. + ty::TypingMode::Analysis { .. } + | ty::TypingMode::Borrowck { .. } + | ty::TypingMode::PostBorrowckAnalysis { .. } + | ty::TypingMode::PostAnalysis => { + ecx.structurally_instantiate_normalizes_to_term( + goal, + goal.predicate.alias, + ); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); + } + }; } Err(guar) => return error_response(ecx, guar), }; @@ -237,10 +268,20 @@ where // treat it as rigid. if cx.impl_self_is_guaranteed_unsized(impl_def_id) { match ecx.typing_mode() { + // Trying to normalize such associated items is always ambiguous + // during coherence to avoid cyclic reasoning. See the example in + // tests/ui/traits/trivial-unsized-projection-in-coherence.rs. + // + // As this ambiguity is unavoidable we emit a nested ambiguous + // goal instead of using `Certainty::AMBIGUOUS`. This allows us to + // return the nested goals to the parent `AliasRelate` goal. This + // would be relevant if any of the nested goals refer to the `term`. + // This is not the case here and we only prefer adding an ambiguous + // nested goal for consistency. ty::TypingMode::Coherence => { - return ecx.evaluate_added_goals_and_make_canonical_response( - Certainty::AMBIGUOUS, - ); + ecx.add_goal(GoalSource::Misc, goal.with(cx, PredicateKind::Ambiguous)); + return ecx + .evaluate_added_goals_and_make_canonical_response(Certainty::Yes); } ty::TypingMode::Analysis { .. } | ty::TypingMode::Borrowck { .. } @@ -870,66 +911,6 @@ where }) } - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - let self_ty = goal.predicate.self_ty(); - let async_destructor_ty = match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(..) - | ty::Array(..) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Infer(ty::IntVar(..) | ty::FloatVar(..)) - | ty::Never - | ty::Adt(_, _) - | ty::Str - | ty::Slice(_) - | ty::Tuple(_) - | ty::Error(_) => self_ty.async_destructor_ty(ecx.cx()), - - ty::UnsafeBinder(_) => { - // FIXME(unsafe_binders): Instantiate the binder with placeholders I guess. - todo!() - } - - // Given an alias, parameter, or placeholder we add an impl candidate normalizing to a rigid - // alias. In case there's a where-bound further constraining this alias it is preferred over - // this impl candidate anyways. It's still a bit scuffed. - ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => { - return ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.structurally_instantiate_normalizes_to_term(goal, goal.predicate.alias); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }); - } - - ty::Infer(ty::TyVar(_) | ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) - | ty::Foreign(..) - | ty::Bound(..) => panic!( - "unexpected self ty `{:?}` when normalizing `<T as AsyncDestruct>::AsyncDestructor`", - goal.predicate.self_ty() - ), - - ty::Pat(..) | ty::Dynamic(..) | ty::Coroutine(..) | ty::CoroutineWitness(..) => panic!( - "`consider_builtin_async_destruct_candidate` is not yet implemented for type: {self_ty:?}" - ), - }; - - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc).enter(|ecx| { - ecx.eq(goal.param_env, goal.predicate.term, async_destructor_ty.into()) - .expect("expected goal term to be fully unconstrained"); - ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) - }) - } - fn consider_builtin_destruct_candidate( _ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, diff --git a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs index ee439f1b3d0..df3ad1e468b 100644 --- a/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs +++ b/compiler/rustc_next_trait_solver/src/solve/normalizes_to/opaque_types.rs @@ -3,6 +3,7 @@ use rustc_index::bit_set::GrowableBitSet; use rustc_type_ir::inherent::*; +use rustc_type_ir::solve::GoalSource; use rustc_type_ir::{self as ty, Interner, TypingMode, fold_regions}; use crate::delegate::SolverDelegate; @@ -31,7 +32,12 @@ where goal.param_env, expected, ); - self.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS) + // Trying to normalize an opaque type during coherence is always ambiguous. + // We add a nested ambiguous goal here instead of using `Certainty::AMBIGUOUS`. + // This allows us to return the nested goals to the parent `AliasRelate` goal. + // This can then allow nested goals to fail after we've constrained the `term`. + self.add_goal(GoalSource::Misc, goal.with(cx, ty::PredicateKind::Ambiguous)); + self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes) } TypingMode::Analysis { defining_opaque_types_and_generators } => { let Some(def_id) = opaque_ty diff --git a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs index ac8fc1d89d6..d19249df387 100644 --- a/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs +++ b/compiler/rustc_next_trait_solver/src/solve/trait_goals.rs @@ -13,7 +13,7 @@ use tracing::{instrument, trace}; use crate::delegate::SolverDelegate; use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes}; -use crate::solve::assembly::{self, AssembleCandidatesFrom, Candidate}; +use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidatesFrom, Candidate}; use crate::solve::inspect::ProbeKind; use crate::solve::{ BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause, @@ -599,19 +599,6 @@ where .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) } - fn consider_builtin_async_destruct_candidate( - ecx: &mut EvalCtxt<'_, D>, - goal: Goal<I, Self>, - ) -> Result<Candidate<I>, NoSolution> { - if goal.predicate.polarity != ty::PredicatePolarity::Positive { - return Err(NoSolution); - } - - // `AsyncDestruct` is automatically implemented for every type. - ecx.probe_builtin_trait_candidate(BuiltinImplSource::Misc) - .enter(|ecx| ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)) - } - fn consider_builtin_destruct_candidate( ecx: &mut EvalCtxt<'_, D>, goal: Goal<I, Self>, @@ -1338,6 +1325,8 @@ where }; } + self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates); + // If there are *only* global where bounds, then make sure to return that this // is still reported as being proven-via the param-env so that rigid projections // operate correctly. Otherwise, drop all global where-bounds before merging the diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index ae9143486ee..ac4f7ed64e2 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -675,6 +675,8 @@ parse_note_pattern_alternatives_use_single_vert = alternatives in or-patterns ar parse_nul_in_c_str = null characters in C string literals are not supported +parse_or_in_let_chain = `||` operators are not supported in let chain conditions + parse_or_pattern_not_allowed_in_fn_parameters = top-level or-patterns are not allowed in function parameters parse_or_pattern_not_allowed_in_let_binding = top-level or-patterns are not allowed in `let` bindings parse_out_of_range_hex_escape = out of range hex escape diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 35cf4c1b00d..6a6fb0eb9b5 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -478,6 +478,13 @@ pub(crate) struct ExpectedExpressionFoundLet { pub comparison: Option<MaybeComparison>, } +#[derive(Diagnostic)] +#[diag(parse_or_in_let_chain)] +pub(crate) struct OrInLetChain { + #[primary_span] + pub span: Span, +} + #[derive(Subdiagnostic, Clone, Copy)] #[multipart_suggestion( parse_maybe_missing_let, diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 370eb3f402d..f3b53971b29 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -4073,14 +4073,18 @@ impl MutVisitor for CondChecker<'_> { match e.kind { ExprKind::Let(_, _, _, ref mut recovered @ Recovered::No) => { if let Some(reason) = self.forbid_let_reason { - *recovered = Recovered::Yes(self.parser.dcx().emit_err( - errors::ExpectedExpressionFoundLet { + let error = match reason { + NotSupportedOr(or_span) => { + self.parser.dcx().emit_err(errors::OrInLetChain { span: or_span }) + } + _ => self.parser.dcx().emit_err(errors::ExpectedExpressionFoundLet { span, reason, missing_let: self.missing_let, comparison: self.comparison, - }, - )); + }), + }; + *recovered = Recovered::Yes(error); } else if self.depth > 1 { // Top level `let` is always allowed; only gate chains match self.let_chains_policy { diff --git a/compiler/rustc_passes/src/lib.rs b/compiler/rustc_passes/src/lib.rs index c7bb00df796..424bce9d4d4 100644 --- a/compiler/rustc_passes/src/lib.rs +++ b/compiler/rustc_passes/src/lib.rs @@ -9,6 +9,7 @@ #![cfg_attr(bootstrap, feature(let_chains))] #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")] #![doc(rust_logo)] +#![feature(box_patterns)] #![feature(map_try_insert)] #![feature(rustdoc_internals)] #![feature(try_blocks)] diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index e57d12dce51..a3b479fdb7a 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -157,7 +157,7 @@ where ty.visit_with(self) } ty::ClauseKind::ConstEvaluatable(ct) => ct.visit_with(self), - ty::ClauseKind::WellFormed(arg) => arg.visit_with(self), + ty::ClauseKind::WellFormed(term) => term.visit_with(self), } } diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 43955cc23a9..e2d36f6a4e2 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2323,14 +2323,13 @@ pub fn parse_externs( let ExternOpt { crate_name: name, path, options } = split_extern_opt(early_dcx, unstable_opts, &arg).unwrap_or_else(|e| e.emit()); - let path = path.map(|p| CanonicalizedPath::new(p.as_path())); - let entry = externs.entry(name.to_owned()); use std::collections::btree_map::Entry; let entry = if let Some(path) = path { // --extern prelude_name=some_file.rlib + let path = CanonicalizedPath::new(path); match entry { Entry::Vacant(vacant) => { let files = BTreeSet::from_iter(iter::once(path)); diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs index 2243e831b66..e9ddd66b5e8 100644 --- a/compiler/rustc_session/src/utils.rs +++ b/compiler/rustc_session/src/utils.rs @@ -1,4 +1,4 @@ -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::OnceLock; use rustc_data_structures::profiling::VerboseTimingGuard; @@ -104,8 +104,8 @@ pub struct CanonicalizedPath { } impl CanonicalizedPath { - pub fn new(path: &Path) -> Self { - Self { original: path.to_owned(), canonicalized: try_canonicalize(path).ok() } + pub fn new(path: PathBuf) -> Self { + Self { canonicalized: try_canonicalize(&path).ok(), original: path } } pub fn canonicalized(&self) -> &PathBuf { diff --git a/compiler/rustc_smir/src/rustc_smir/context.rs b/compiler/rustc_smir/src/rustc_smir/context.rs index 9a4cac243bd..bac5c9066f1 100644 --- a/compiler/rustc_smir/src/rustc_smir/context.rs +++ b/compiler/rustc_smir/src/rustc_smir/context.rs @@ -695,13 +695,6 @@ impl<'tcx> SmirCtxt<'tcx> { matches!(instance.def, ty::InstanceKind::DropGlue(_, None)) } - /// Check if this is an empty AsyncDropGlueCtor shim. - pub fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { - let tables = self.0.borrow_mut(); - let instance = tables.instances[def]; - matches!(instance.def, ty::InstanceKind::AsyncDropGlueCtorShim(_, None)) - } - /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. pub fn mono_instance(&self, def_id: stable_mir::DefId) -> stable_mir::mir::mono::Instance { diff --git a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs index 61b769bce08..42b3e59b73a 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/mir.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/mir.rs @@ -494,6 +494,9 @@ impl<'tcx> Stable<'tcx> for mir::AssertMessage<'tcx> { AssertKind::ResumedAfterPanic(coroutine) => { stable_mir::mir::AssertMessage::ResumedAfterPanic(coroutine.stable(tables)) } + AssertKind::ResumedAfterDrop(coroutine) => { + stable_mir::mir::AssertMessage::ResumedAfterDrop(coroutine.stable(tables)) + } AssertKind::MisalignedPointerDereference { required, found } => { stable_mir::mir::AssertMessage::MisalignedPointerDereference { required: required.stable(tables), @@ -648,13 +651,18 @@ impl<'tcx> Stable<'tcx> for mir::TerminatorKind<'tcx> { mir::TerminatorKind::UnwindTerminate(_) => TerminatorKind::Abort, mir::TerminatorKind::Return => TerminatorKind::Return, mir::TerminatorKind::Unreachable => TerminatorKind::Unreachable, - mir::TerminatorKind::Drop { place, target, unwind, replace: _ } => { - TerminatorKind::Drop { - place: place.stable(tables), - target: target.as_usize(), - unwind: unwind.stable(tables), - } - } + mir::TerminatorKind::Drop { + place, + target, + unwind, + replace: _, + drop: _, + async_fut: _, + } => TerminatorKind::Drop { + place: place.stable(tables), + target: target.as_usize(), + unwind: unwind.stable(tables), + }, mir::TerminatorKind::Call { func, args, diff --git a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs index cc88df78470..76489dd0913 100644 --- a/compiler/rustc_smir/src/rustc_smir/convert/ty.rs +++ b/compiler/rustc_smir/src/rustc_smir/convert/ty.rs @@ -638,8 +638,8 @@ impl<'tcx> Stable<'tcx> for ty::ClauseKind<'tcx> { const_.stable(tables), ty.stable(tables), ), - ClauseKind::WellFormed(generic_arg) => { - stable_mir::ty::ClauseKind::WellFormed(generic_arg.unpack().stable(tables)) + ClauseKind::WellFormed(term) => { + stable_mir::ty::ClauseKind::WellFormed(term.unpack().stable(tables)) } ClauseKind::ConstEvaluatable(const_) => { stable_mir::ty::ClauseKind::ConstEvaluatable(const_.stable(tables)) @@ -813,6 +813,8 @@ impl<'tcx> Stable<'tcx> for ty::Instance<'tcx> { | ty::InstanceKind::DropGlue(..) | ty::InstanceKind::CloneShim(..) | ty::InstanceKind::FnPtrShim(..) + | ty::InstanceKind::FutureDropPollShim(..) + | ty::InstanceKind::AsyncDropGlue(..) | ty::InstanceKind::AsyncDropGlueCtorShim(..) => { stable_mir::mir::mono::InstanceKind::Shim } diff --git a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs index cd61907dc6b..bb35e23a728 100644 --- a/compiler/rustc_smir/src/stable_mir/compiler_interface.rs +++ b/compiler/rustc_smir/src/stable_mir/compiler_interface.rs @@ -354,11 +354,6 @@ impl<'tcx> SmirInterface<'tcx> { self.cx.is_empty_drop_shim(def) } - /// Check if this is an empty AsyncDropGlueCtor shim. - pub(crate) fn is_empty_async_drop_ctor_shim(&self, def: InstanceDef) -> bool { - self.cx.is_empty_async_drop_ctor_shim(def) - } - /// Convert a non-generic crate item into an instance. /// This function will panic if the item is generic. pub(crate) fn mono_instance(&self, def_id: DefId) -> Instance { diff --git a/compiler/rustc_smir/src/stable_mir/mir/body.rs b/compiler/rustc_smir/src/stable_mir/mir/body.rs index b2360545424..660cd7db080 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/body.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/body.rs @@ -267,6 +267,7 @@ pub enum AssertMessage { RemainderByZero(Operand), ResumedAfterReturn(CoroutineKind), ResumedAfterPanic(CoroutineKind), + ResumedAfterDrop(CoroutineKind), MisalignedPointerDereference { required: Operand, found: Operand }, NullPointerDereference, } @@ -320,6 +321,22 @@ impl AssertMessage { _, )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after panicking"), + AssertMessage::ResumedAfterDrop(CoroutineKind::Coroutine(_)) => { + Ok("coroutine resumed after async drop") + } + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::Async, + _, + )) => Ok("`async fn` resumed after async drop"), + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::Gen, + _, + )) => Ok("`async gen fn` resumed after async drop"), + AssertMessage::ResumedAfterDrop(CoroutineKind::Desugared( + CoroutineDesugaring::AsyncGen, + _, + )) => Ok("`gen fn` should just keep returning `AssertMessage::None` after async drop"), + AssertMessage::BoundsCheck { .. } => Ok("index out of bounds"), AssertMessage::MisalignedPointerDereference { .. } => { Ok("misaligned pointer dereference") diff --git a/compiler/rustc_smir/src/stable_mir/mir/mono.rs b/compiler/rustc_smir/src/stable_mir/mir/mono.rs index 0c3e4fb7c90..f5239cccae1 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/mono.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/mono.rs @@ -163,10 +163,7 @@ impl Instance { /// When generating code for a Drop terminator, users can ignore an empty drop glue. /// These shims are only needed to generate a valid Drop call done via VTable. pub fn is_empty_shim(&self) -> bool { - self.kind == InstanceKind::Shim - && with(|cx| { - cx.is_empty_drop_shim(self.def) || cx.is_empty_async_drop_ctor_shim(self.def) - }) + self.kind == InstanceKind::Shim && with(|cx| cx.is_empty_drop_shim(self.def)) } /// Try to constant evaluate the instance into a constant with the given type. diff --git a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs index 8a6be0cd37a..ba20651f993 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/pretty.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/pretty.rs @@ -313,7 +313,9 @@ fn pretty_assert_message<W: Write>(writer: &mut W, msg: &AssertMessage) -> io::R AssertMessage::NullPointerDereference => { write!(writer, "\"null pointer dereference occurred\"") } - AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) => { + AssertMessage::ResumedAfterReturn(_) + | AssertMessage::ResumedAfterPanic(_) + | AssertMessage::ResumedAfterDrop(_) => { write!(writer, "{}", msg.description().unwrap()) } } diff --git a/compiler/rustc_smir/src/stable_mir/mir/visit.rs b/compiler/rustc_smir/src/stable_mir/mir/visit.rs index 786693ea98d..e21dc11eea9 100644 --- a/compiler/rustc_smir/src/stable_mir/mir/visit.rs +++ b/compiler/rustc_smir/src/stable_mir/mir/visit.rs @@ -372,7 +372,8 @@ macro_rules! make_mir_visitor { } AssertMessage::ResumedAfterReturn(_) | AssertMessage::ResumedAfterPanic(_) - | AssertMessage::NullPointerDereference => { + | AssertMessage::NullPointerDereference + | AssertMessage::ResumedAfterDrop(_) => { //nothing to visit } AssertMessage::MisalignedPointerDereference { required, found } => { diff --git a/compiler/rustc_smir/src/stable_mir/ty.rs b/compiler/rustc_smir/src/stable_mir/ty.rs index 215519fa7fc..e331e593471 100644 --- a/compiler/rustc_smir/src/stable_mir/ty.rs +++ b/compiler/rustc_smir/src/stable_mir/ty.rs @@ -1462,7 +1462,7 @@ pub enum ClauseKind { TypeOutlives(TypeOutlivesPredicate), Projection(ProjectionPredicate), ConstArgHasType(TyConst, Ty), - WellFormed(GenericArgKind), + WellFormed(TermKind), ConstEvaluatable(TyConst), } diff --git a/compiler/rustc_span/src/hygiene.rs b/compiler/rustc_span/src/hygiene.rs index 53908914965..ab0a802dc7f 100644 --- a/compiler/rustc_span/src/hygiene.rs +++ b/compiler/rustc_span/src/hygiene.rs @@ -72,21 +72,6 @@ struct SyntaxContextData { } impl SyntaxContextData { - fn new( - (parent, outer_expn, outer_transparency): SyntaxContextKey, - opaque: SyntaxContext, - opaque_and_semiopaque: SyntaxContext, - ) -> SyntaxContextData { - SyntaxContextData { - outer_expn, - outer_transparency, - parent, - opaque, - opaque_and_semiopaque, - dollar_crate_name: kw::DollarCrate, - } - } - fn root() -> SyntaxContextData { SyntaxContextData { outer_expn: ExpnId::root(), @@ -98,14 +83,6 @@ impl SyntaxContextData { } } - fn decode_placeholder() -> SyntaxContextData { - SyntaxContextData { dollar_crate_name: kw::Empty, ..SyntaxContextData::root() } - } - - fn is_decode_placeholder(&self) -> bool { - self.dollar_crate_name == kw::Empty - } - fn key(&self) -> SyntaxContextKey { (self.parent, self.outer_expn, self.outer_transparency) } @@ -148,7 +125,7 @@ impl !PartialOrd for LocalExpnId {} /// with a non-default mode. With this check in place, we can avoid the need /// to maintain separate versions of `ExpnData` hashes for each permutation /// of `HashingControls` settings. -fn assert_default_hashing_controls<CTX: HashStableContext>(ctx: &CTX, msg: &str) { +fn assert_default_hashing_controls(ctx: &impl HashStableContext, msg: &str) { match ctx.hashing_controls() { // Note that we require that `hash_spans` be set according to the global // `-Z incremental-ignore-spans` option. Normally, this option is disabled, @@ -416,7 +393,7 @@ impl HygieneData { } } - fn with<T, F: FnOnce(&mut HygieneData) -> T>(f: F) -> T { + fn with<R>(f: impl FnOnce(&mut HygieneData) -> R) -> R { with_session_globals(|session_globals| f(&mut session_globals.hygiene_data.borrow_mut())) } @@ -460,28 +437,23 @@ impl HygieneData { } fn normalize_to_macros_2_0(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].opaque } fn normalize_to_macro_rules(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].opaque_and_semiopaque } fn outer_expn(&self, ctxt: SyntaxContext) -> ExpnId { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].outer_expn } fn outer_mark(&self, ctxt: SyntaxContext) -> (ExpnId, Transparency) { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); let data = &self.syntax_context_data[ctxt.0 as usize]; (data.outer_expn, data.outer_transparency) } fn parent_ctxt(&self, ctxt: SyntaxContext) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[ctxt.0 as usize].is_decode_placeholder()); self.syntax_context_data[ctxt.0 as usize].parent } @@ -592,8 +564,6 @@ impl HygieneData { expn_id: ExpnId, transparency: Transparency, ) -> SyntaxContext { - debug_assert!(!self.syntax_context_data[parent.0 as usize].is_decode_placeholder()); - // Look into the cache first. let key = (parent, expn_id, transparency); if let Some(ctxt) = self.syntax_context_map.get(&key) { @@ -601,16 +571,20 @@ impl HygieneData { } // Reserve a new syntax context. + // The inserted dummy data can only be potentially accessed by nested `alloc_ctxt` calls, + // the assert below ensures that it doesn't happen. let ctxt = SyntaxContext::from_usize(self.syntax_context_data.len()); - self.syntax_context_data.push(SyntaxContextData::decode_placeholder()); + self.syntax_context_data + .push(SyntaxContextData { dollar_crate_name: sym::dummy, ..SyntaxContextData::root() }); self.syntax_context_map.insert(key, ctxt); // Opaque and semi-opaque versions of the parent. Note that they may be equal to the // parent itself. E.g. `parent_opaque` == `parent` if the expn chain contains only opaques, // and `parent_opaque_and_semiopaque` == `parent` if the expn contains only (semi-)opaques. - let parent_opaque = self.syntax_context_data[parent.0 as usize].opaque; - let parent_opaque_and_semiopaque = - self.syntax_context_data[parent.0 as usize].opaque_and_semiopaque; + let parent_data = &self.syntax_context_data[parent.0 as usize]; + assert_ne!(parent_data.dollar_crate_name, sym::dummy); + let parent_opaque = parent_data.opaque; + let parent_opaque_and_semiopaque = parent_data.opaque_and_semiopaque; // Evaluate opaque and semi-opaque versions of the new syntax context. let (opaque, opaque_and_semiopaque) = match transparency { @@ -629,8 +603,14 @@ impl HygieneData { }; // Fill the full data, now that we have it. - self.syntax_context_data[ctxt.as_u32() as usize] = - SyntaxContextData::new(key, opaque, opaque_and_semiopaque); + self.syntax_context_data[ctxt.as_u32() as usize] = SyntaxContextData { + outer_expn: expn_id, + outer_transparency: transparency, + parent, + opaque, + opaque_and_semiopaque, + dollar_crate_name: kw::DollarCrate, + }; ctxt } } @@ -650,13 +630,12 @@ pub fn walk_chain_collapsed(span: Span, to: Span) -> Span { pub fn update_dollar_crate_names(mut get_name: impl FnMut(SyntaxContext) -> Symbol) { // The new contexts that need updating are at the end of the list and have `$crate` as a name. - // Also decoding placeholders can be encountered among both old and new contexts. let mut to_update = vec![]; HygieneData::with(|data| { for (idx, scdata) in data.syntax_context_data.iter().enumerate().rev() { if scdata.dollar_crate_name == kw::DollarCrate { to_update.push((idx, kw::DollarCrate)); - } else if !scdata.is_decode_placeholder() { + } else { break; } } @@ -922,10 +901,7 @@ impl SyntaxContext { } pub(crate) fn dollar_crate_name(self) -> Symbol { - HygieneData::with(|data| { - debug_assert!(!data.syntax_context_data[self.0 as usize].is_decode_placeholder()); - data.syntax_context_data[self.0 as usize].dollar_crate_name - }) + HygieneData::with(|data| data.syntax_context_data[self.0 as usize].dollar_crate_name) } pub fn edition(self) -> Edition { @@ -1293,49 +1269,47 @@ impl HygieneEncodeContext { self.latest_ctxts ); - // Consume the current round of SyntaxContexts. - // Drop the lock() temporary early - let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }; - - // It's fine to iterate over a HashMap, because the serialization - // of the table that we insert data into doesn't depend on insertion - // order + // Consume the current round of syntax contexts. + // Drop the lock() temporary early. + // It's fine to iterate over a HashMap, because the serialization of the table + // that we insert data into doesn't depend on insertion order. #[allow(rustc::potential_query_instability)] - for_all_ctxts_in(latest_ctxts.into_iter(), |index, ctxt, data| { + let latest_ctxts = { mem::take(&mut *self.latest_ctxts.lock()) }.into_iter(); + let all_ctxt_data: Vec<_> = HygieneData::with(|data| { + latest_ctxts + .map(|ctxt| (ctxt, data.syntax_context_data[ctxt.0 as usize].key())) + .collect() + }); + for (ctxt, ctxt_key) in all_ctxt_data { if self.serialized_ctxts.lock().insert(ctxt) { - encode_ctxt(encoder, index, data); + encode_ctxt(encoder, ctxt.0, &ctxt_key); } - }); - - let latest_expns = { mem::take(&mut *self.latest_expns.lock()) }; + } - // Same as above, this is fine as we are inserting into a order-independent hashset + // Same as above, but for expansions instead of syntax contexts. #[allow(rustc::potential_query_instability)] - for_all_expns_in(latest_expns.into_iter(), |expn, data, hash| { + let latest_expns = { mem::take(&mut *self.latest_expns.lock()) }.into_iter(); + let all_expn_data: Vec<_> = HygieneData::with(|data| { + latest_expns + .map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))) + .collect() + }); + for (expn, expn_data, expn_hash) in all_expn_data { if self.serialized_expns.lock().insert(expn) { - encode_expn(encoder, expn, data, hash); + encode_expn(encoder, expn, &expn_data, expn_hash); } - }); + } } debug!("encode_hygiene: Done serializing SyntaxContextData"); } } -#[derive(Default)] /// Additional information used to assist in decoding hygiene data -struct HygieneDecodeContextInner { - // Maps serialized `SyntaxContext` ids to a `SyntaxContext` in the current - // global `HygieneData`. When we deserialize a `SyntaxContext`, we need to create - // a new id in the global `HygieneData`. This map tracks the ID we end up picking, - // so that multiple occurrences of the same serialized id are decoded to the same - // `SyntaxContext`. This only stores `SyntaxContext`s which are completely decoded. - remapped_ctxts: Vec<Option<SyntaxContext>>, -} - #[derive(Default)] -/// Additional information used to assist in decoding hygiene data pub struct HygieneDecodeContext { - inner: Lock<HygieneDecodeContextInner>, + // A cache mapping raw serialized per-crate syntax context ids to corresponding decoded + // `SyntaxContext`s in the current global `HygieneData`. + remapped_ctxts: Lock<IndexVec<u32, Option<SyntaxContext>>>, } /// Register an expansion which has been decoded from the on-disk-cache for the local crate. @@ -1406,10 +1380,10 @@ pub fn decode_expn_id( // to track which `SyntaxContext`s we have already decoded. // The provided closure will be invoked to deserialize a `SyntaxContextData` // if we haven't already seen the id of the `SyntaxContext` we are deserializing. -pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContextKey>( +pub fn decode_syntax_context<D: Decoder>( d: &mut D, context: &HygieneDecodeContext, - decode_data: F, + decode_data: impl FnOnce(&mut D, u32) -> SyntaxContextKey, ) -> SyntaxContext { let raw_id: u32 = Decodable::decode(d); if raw_id == 0 { @@ -1418,12 +1392,11 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext return SyntaxContext::root(); } + // Look into the cache first. // Reminder: `HygieneDecodeContext` is per-crate, so there are no collisions between // raw ids from different crate metadatas. - if let Some(ctxt) = context.inner.lock().remapped_ctxts.get(raw_id as usize).copied().flatten() - { - // This has already been decoded. - return ctxt; + if let Some(Some(ctxt)) = context.remapped_ctxts.lock().get(raw_id) { + return *ctxt; } // Don't try to decode data while holding the lock, since we need to @@ -1432,48 +1405,11 @@ pub fn decode_syntax_context<D: Decoder, F: FnOnce(&mut D, u32) -> SyntaxContext let ctxt = HygieneData::with(|hygiene_data| hygiene_data.alloc_ctxt(parent, expn_id, transparency)); - let mut inner = context.inner.lock(); - let new_len = raw_id as usize + 1; - if inner.remapped_ctxts.len() < new_len { - inner.remapped_ctxts.resize(new_len, None); - } - inner.remapped_ctxts[raw_id as usize] = Some(ctxt); + context.remapped_ctxts.lock().insert(raw_id, ctxt); ctxt } -fn for_all_ctxts_in<F: FnMut(u32, SyntaxContext, &SyntaxContextKey)>( - ctxts: impl Iterator<Item = SyntaxContext>, - mut f: F, -) { - let all_data: Vec<_> = HygieneData::with(|data| { - ctxts - .map(|ctxt| { - (ctxt, { - let item = data.syntax_context_data[ctxt.0 as usize]; - debug_assert!(!item.is_decode_placeholder()); - item.key() - }) - }) - .collect() - }); - for (ctxt, data) in all_data.into_iter() { - f(ctxt.0, ctxt, &data); - } -} - -fn for_all_expns_in( - expns: impl Iterator<Item = ExpnId>, - mut f: impl FnMut(ExpnId, &ExpnData, ExpnHash), -) { - let all_data: Vec<_> = HygieneData::with(|data| { - expns.map(|expn| (expn, data.expn_data(expn).clone(), data.expn_hash(expn))).collect() - }); - for (expn, data, hash) in all_data.into_iter() { - f(expn, &data, hash); - } -} - impl<E: SpanEncoder> Encodable<E> for LocalExpnId { fn encode(&self, e: &mut E) { self.to_expn_id().encode(e); @@ -1486,10 +1422,10 @@ impl<D: SpanDecoder> Decodable<D> for LocalExpnId { } } -pub fn raw_encode_syntax_context<E: Encoder>( +pub fn raw_encode_syntax_context( ctxt: SyntaxContext, context: &HygieneEncodeContext, - e: &mut E, + e: &mut impl Encoder, ) { if !context.serialized_ctxts.lock().contains(&ctxt) { context.latest_ctxts.lock().insert(ctxt); diff --git a/compiler/rustc_span/src/lib.rs b/compiler/rustc_span/src/lib.rs index fccdaed21a2..9722031f209 100644 --- a/compiler/rustc_span/src/lib.rs +++ b/compiler/rustc_span/src/lib.rs @@ -31,7 +31,6 @@ #![feature(round_char_boundary)] #![feature(rustc_attrs)] #![feature(rustdoc_internals)] -#![feature(slice_as_chunks)] // tidy-alphabetical-end // The code produced by the `Encodable`/`Decodable` derive macros refer to diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 31b85677997..ba3e6d7ca82 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -502,17 +502,8 @@ symbols! { async_call_mut, async_call_once, async_closure, - async_destruct, async_drop, - async_drop_chain, - async_drop_defer, - async_drop_deferred_drop_in_place, - async_drop_either, - async_drop_fuse, async_drop_in_place, - async_drop_noop, - async_drop_slice, - async_drop_surface_drop_in_place, async_fn, async_fn_in_dyn_trait, async_fn_in_trait, @@ -984,7 +975,6 @@ symbols! { fadd_fast, fake_variadic, fallback, - fallback_surface_drop, fdiv_algebraic, fdiv_fast, feature, @@ -1065,6 +1055,7 @@ symbols! { fundamental, fused_iterator, future, + future_drop_poll, future_output, future_trait, gdb_script_file, @@ -1523,14 +1514,18 @@ symbols! { panic_cannot_unwind, panic_const_add_overflow, panic_const_async_fn_resumed, + panic_const_async_fn_resumed_drop, panic_const_async_fn_resumed_panic, panic_const_async_gen_fn_resumed, + panic_const_async_gen_fn_resumed_drop, panic_const_async_gen_fn_resumed_panic, panic_const_coroutine_resumed, + panic_const_coroutine_resumed_drop, panic_const_coroutine_resumed_panic, panic_const_div_by_zero, panic_const_div_overflow, panic_const_gen_fn_none, + panic_const_gen_fn_none_drop, panic_const_gen_fn_none_panic, panic_const_mul_overflow, panic_const_neg_overflow, @@ -2059,7 +2054,6 @@ symbols! { suggestion, super_let, supertrait_item_shadowing, - surface_async_drop_in_place, sym, sync, synthetic, diff --git a/compiler/rustc_symbol_mangling/src/legacy.rs b/compiler/rustc_symbol_mangling/src/legacy.rs index 2802e891807..db102abda7f 100644 --- a/compiler/rustc_symbol_mangling/src/legacy.rs +++ b/compiler/rustc_symbol_mangling/src/legacy.rs @@ -63,10 +63,17 @@ pub(super) fn mangle<'tcx>( .print_def_path( def_id, if let ty::InstanceKind::DropGlue(_, _) - | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) = instance.def + | ty::InstanceKind::AsyncDropGlueCtorShim(_, _) + | ty::InstanceKind::FutureDropPollShim(_, _, _) = instance.def { // Add the name of the dropped type to the symbol name &*instance.args + } else if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + let ty::Coroutine(_, cor_args) = ty.kind() else { + bug!(); + }; + let drop_ty = cor_args.first().unwrap().expect_ty(); + tcx.mk_args(&[GenericArg::from(drop_ty)]) } else { &[] }, @@ -99,6 +106,10 @@ pub(super) fn mangle<'tcx>( _ => {} } + if let ty::InstanceKind::FutureDropPollShim(..) = instance.def { + let _ = printer.write_str("{{drop-shim}}"); + } + printer.path.finish(hash) } diff --git a/compiler/rustc_symbol_mangling/src/v0.rs b/compiler/rustc_symbol_mangling/src/v0.rs index f310aa65500..9f80f8443cb 100644 --- a/compiler/rustc_symbol_mangling/src/v0.rs +++ b/compiler/rustc_symbol_mangling/src/v0.rs @@ -58,11 +58,17 @@ pub(super) fn mangle<'tcx>( ty::InstanceKind::ConstructCoroutineInClosureShim { receiver_by_ref: false, .. } => { Some("by_ref") } - + ty::InstanceKind::FutureDropPollShim(_, _, _) => Some("drop"), _ => None, }; - if let Some(shim_kind) = shim_kind { + if let ty::InstanceKind::AsyncDropGlue(_, ty) = instance.def { + let ty::Coroutine(_, cor_args) = ty.kind() else { + bug!(); + }; + let drop_ty = cor_args.first().unwrap().expect_ty(); + cx.print_def_path(def_id, tcx.mk_args(&[GenericArg::from(drop_ty)])).unwrap() + } else if let Some(shim_kind) = shim_kind { cx.path_append_ns(|cx| cx.print_def_path(def_id, args), 'S', 0, shim_kind).unwrap() } else { cx.print_def_path(def_id, args).unwrap() diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs index f45e3904212..eba195cb99c 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/infer/need_type_info.rs @@ -14,8 +14,8 @@ use rustc_middle::hir::nested_filter; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter, Print, Printer}; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Ty, TyCtxt, - TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, + self, GenericArg, GenericArgKind, GenericArgsRef, InferConst, IsSuggestable, Term, TermKind, + Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeckResults, }; use rustc_span::{BytePos, DUMMY_SP, FileName, Ident, Span, sym}; use rustc_type_ir::TypeVisitableExt; @@ -344,12 +344,12 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { /// which were stuck during inference. pub fn extract_inference_diagnostics_data( &self, - arg: GenericArg<'tcx>, + term: Term<'tcx>, highlight: ty::print::RegionHighlightMode<'tcx>, ) -> InferenceDiagnosticsData { let tcx = self.tcx; - match arg.unpack() { - GenericArgKind::Type(ty) => { + match term.unpack() { + TermKind::Ty(ty) => { if let ty::Infer(ty::TyVar(ty_vid)) = *ty.kind() { let var_origin = self.infcx.type_var_origin(ty_vid); if let Some(def_id) = var_origin.param_def_id @@ -375,7 +375,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { parent: None, } } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() { let origin = self.const_var_origin(vid).expect("expected unresolved const var"); if let Some(def_id) = origin.param_def_id { @@ -411,7 +411,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } } } - GenericArgKind::Lifetime(_) => bug!("unexpected lifetime"), } } @@ -472,13 +471,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { &self, body_def_id: LocalDefId, failure_span: Span, - arg: GenericArg<'tcx>, + term: Term<'tcx>, error_code: TypeAnnotationNeeded, should_label_span: bool, ) -> Diag<'a> { - let arg = self.resolve_vars_if_possible(arg); - let arg_data = - self.extract_inference_diagnostics_data(arg, ty::print::RegionHighlightMode::default()); + let term = self.resolve_vars_if_possible(term); + let arg_data = self + .extract_inference_diagnostics_data(term, ty::print::RegionHighlightMode::default()); let Some(typeck_results) = &self.typeck_results else { // If we don't have any typeck results we're outside @@ -487,7 +486,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { return self.bad_inference_failure_err(failure_span, arg_data, error_code); }; - let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, arg); + let mut local_visitor = FindInferSourceVisitor::new(self, typeck_results, term); if let Some(body) = self.tcx.hir_maybe_body_owned_by( self.tcx.typeck_root_def_id(body_def_id.to_def_id()).expect_local(), ) { @@ -542,7 +541,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { have_turbofish, } => { let generics = self.tcx.generics_of(generics_def_id); - let is_type = matches!(arg.unpack(), GenericArgKind::Type(_)); + let is_type = term.as_type().is_some(); let (parent_exists, parent_prefix, parent_name) = InferenceDiagnosticsParentData::for_parent_def_id(self.tcx, generics_def_id) @@ -811,7 +810,7 @@ struct FindInferSourceVisitor<'a, 'tcx> { tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, attempt: usize, infer_source_cost: usize, @@ -822,7 +821,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { fn new( tecx: &'a TypeErrCtxt<'a, 'tcx>, typeck_results: &'a TypeckResults<'tcx>, - target: GenericArg<'tcx>, + target: Term<'tcx>, ) -> Self { FindInferSourceVisitor { tecx, @@ -938,12 +937,12 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { // Check whether this generic argument is the inference variable we // are looking for. fn generic_arg_is_target(&self, arg: GenericArg<'tcx>) -> bool { - if arg == self.target { + if arg == self.target.into() { return true; } match (arg.unpack(), self.target.unpack()) { - (GenericArgKind::Type(inner_ty), GenericArgKind::Type(target_ty)) => { + (GenericArgKind::Type(inner_ty), TermKind::Ty(target_ty)) => { use ty::{Infer, TyVar}; match (inner_ty.kind(), target_ty.kind()) { (&Infer(TyVar(a_vid)), &Infer(TyVar(b_vid))) => { @@ -952,7 +951,7 @@ impl<'a, 'tcx> FindInferSourceVisitor<'a, 'tcx> { _ => false, } } - (GenericArgKind::Const(inner_ct), GenericArgKind::Const(target_ct)) => { + (GenericArgKind::Const(inner_ct), TermKind::Const(target_ct)) => { use ty::InferConst::*; match (inner_ct.kind(), target_ct.kind()) { (ty::ConstKind::Infer(Var(a_vid)), ty::ConstKind::Infer(Var(b_vid))) => { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs index 54b50851b74..275b580d794 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/ambiguity.rs @@ -228,13 +228,18 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Pick the first generic parameter that still contains inference variables as the one // we're going to emit an error for. If there are none (see above), fall back to // a more general error. - let arg = data.trait_ref.args.iter().find(|s| s.has_non_region_infer()); + let term = data + .trait_ref + .args + .iter() + .filter_map(ty::GenericArg::as_term) + .find(|s| s.has_non_region_infer()); - let mut err = if let Some(arg) = arg { + let mut err = if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0283, true, ) @@ -276,7 +281,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { } if ambiguities.len() > 1 && ambiguities.len() < 10 && has_non_region_infer { if let Some(e) = self.tainted_by_errors() - && arg.is_none() + && term.is_none() { // If `arg.is_none()`, then this is probably two param-env // candidates or impl candidates that are equal modulo lifetimes. @@ -313,7 +318,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.suggest_fully_qualified_path(&mut err, def_id, span, trait_pred.def_id()); } - if let Some(ty::GenericArgKind::Type(_)) = arg.map(|arg| arg.unpack()) + if term.is_some_and(|term| term.as_type().is_some()) && let Some(body) = self.tcx.hir_maybe_body_owned_by(obligation.cause.body_id) { let mut expr_finder = FindExprBySpan::new(span, self.tcx); @@ -464,11 +469,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { err } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // Same hacky approach as above to avoid deluging user // with error messages. - if let Err(e) = arg.error_reported() { + if let Err(e) = term.error_reported() { return e; } if let Some(e) = self.tainted_by_errors() { @@ -478,7 +483,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0282, false, ) @@ -519,18 +524,19 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // other `Foo` impls are incoherent. return guar; } - let arg = data + let term = data .projection_term .args .iter() - .chain(Some(data.term.into_arg())) + .filter_map(ty::GenericArg::as_term) + .chain([data.term]) .find(|g| g.has_non_region_infer()); let predicate = self.tcx.short_string(predicate, &mut file); - if let Some(arg) = arg { + if let Some(term) = term { self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ) @@ -554,12 +560,13 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let Some(e) = self.tainted_by_errors() { return e; } - let arg = data.walk().find(|g| g.is_non_region_infer()); - if let Some(arg) = arg { + let term = + data.walk().filter_map(ty::GenericArg::as_term).find(|term| term.is_infer()); + if let Some(term) = term { let err = self.emit_inference_failure_err( obligation.cause.body_id, span, - arg, + term, TypeAnnotationNeeded::E0284, true, ); diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 595dd12d6f6..ab2aa0ae469 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -813,38 +813,17 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { obligation: &PredicateObligation<'tcx>, mut trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> Option<ErrorGuaranteed> { - // If `AsyncFnKindHelper` is not implemented, that means that the closure kind - // doesn't extend the goal kind. This is worth reporting, but we can only do so - // if we actually know which closure this goal comes from, so look at the cause - // to see if we can extract that information. - if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) - && let Some(found_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(0).to_opt_closure_kind() - && let Some(expected_kind) = - trait_pred.skip_binder().trait_ref.args.type_at(1).to_opt_closure_kind() - && !found_kind.extends(expected_kind) - { - if let Some((_, Some(parent))) = obligation.cause.code().parent_with_predicate() { - // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + // If we end up on an `AsyncFnKindHelper` goal, try to unwrap the parent + // `AsyncFn*` goal. + if self.tcx.is_lang_item(trait_pred.def_id(), LangItem::AsyncFnKindHelper) { + let mut code = obligation.cause.code(); + // Unwrap a `FunctionArg` cause, which has been refined from a derived obligation. + if let ObligationCauseCode::FunctionArg { parent_code, .. } = code { + code = &**parent_code; + } + // If we have a derived obligation, then the parent will be a `AsyncFn*` goal. + if let Some((_, Some(parent))) = code.parent_with_predicate() { trait_pred = parent; - } else if let &ObligationCauseCode::FunctionArg { arg_hir_id, .. } = - obligation.cause.code() - && let Some(typeck_results) = &self.typeck_results - && let ty::Closure(closure_def_id, _) | ty::CoroutineClosure(closure_def_id, _) = - *typeck_results.node_type(arg_hir_id).kind() - { - // Otherwise, extract the closure kind from the obligation, - // but only if we actually have an argument to deduce the - // closure type from... - let mut err = self.report_closure_error( - &obligation, - closure_def_id, - found_kind, - expected_kind, - "Async", - ); - self.note_obligation_cause(&mut err, &obligation); - return Some(err.emit()); } } diff --git a/compiler/rustc_trait_selection/src/solve/delegate.rs b/compiler/rustc_trait_selection/src/solve/delegate.rs index e0b425fa739..a87c5ad6db9 100644 --- a/compiler/rustc_trait_selection/src/solve/delegate.rs +++ b/compiler/rustc_trait_selection/src/solve/delegate.rs @@ -92,12 +92,16 @@ impl<'tcx> rustc_next_trait_solver::delegate::SolverDelegate for SolverDelegate< fn well_formed_goals( &self, param_env: ty::ParamEnv<'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> Option<Vec<Goal<'tcx, ty::Predicate<'tcx>>>> { - crate::traits::wf::unnormalized_obligations(&self.0, param_env, arg, DUMMY_SP, CRATE_DEF_ID) - .map(|obligations| { - obligations.into_iter().map(|obligation| obligation.as_goal()).collect() - }) + crate::traits::wf::unnormalized_obligations( + &self.0, + param_env, + term, + DUMMY_SP, + CRATE_DEF_ID, + ) + .map(|obligations| obligations.into_iter().map(|obligation| obligation.as_goal()).collect()) } fn clone_opaque_types_for_query_response(&self) -> Vec<(ty::OpaqueTypeKey<'tcx>, Ty<'tcx>)> { diff --git a/compiler/rustc_trait_selection/src/solve/fulfill.rs b/compiler/rustc_trait_selection/src/solve/fulfill.rs index 848d0646d00..1f4fa5aac10 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill.rs @@ -163,15 +163,15 @@ where fn select_where_possible(&mut self, infcx: &InferCtxt<'tcx>) -> Vec<E> { assert_eq!(self.usable_in_snapshot, infcx.num_open_snapshots()); let mut errors = Vec::new(); - for i in 0.. { - if !infcx.tcx.recursion_limit().value_within_limit(i) { - self.obligations.on_fulfillment_overflow(infcx); - // Only return true errors that we have accumulated while processing. - return errors; - } - + loop { let mut has_changed = false; - for obligation in self.obligations.drain_pending(|_| true) { + for mut obligation in self.obligations.drain_pending(|_| true) { + if !infcx.tcx.recursion_limit().value_within_limit(obligation.recursion_depth) { + self.obligations.on_fulfillment_overflow(infcx); + // Only return true errors that we have accumulated while processing. + return errors; + } + let goal = obligation.as_goal(); let result = <&SolverDelegate<'tcx>>::from(infcx) .evaluate_root_goal(goal, GenerateProofTree::No, obligation.cause.span) @@ -189,6 +189,13 @@ where }; if changed == HasChanged::Yes { + // We increment the recursion depth here to track the number of times + // this goal has resulted in inference progress. This doesn't precisely + // model the way that we track recursion depth in the old solver due + // to the fact that we only process root obligations, but it is a good + // approximation and should only result in fulfillment overflow in + // pathological cases. + obligation.recursion_depth += 1; has_changed = true; } diff --git a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs index f6c650c68c0..a024f432450 100644 --- a/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs +++ b/compiler/rustc_trait_selection/src/solve/fulfill/derive_errors.rs @@ -241,13 +241,13 @@ impl<'tcx> BestObligation<'tcx> { fn visit_well_formed_goal( &mut self, candidate: &inspect::InspectCandidate<'_, 'tcx>, - arg: ty::GenericArg<'tcx>, + term: ty::Term<'tcx>, ) -> ControlFlow<PredicateObligation<'tcx>> { let infcx = candidate.goal().infcx(); let param_env = candidate.goal().goal().param_env; let body_id = self.obligation.cause.body_id; - for obligation in wf::unnormalized_obligations(infcx, param_env, arg, self.span(), body_id) + for obligation in wf::unnormalized_obligations(infcx, param_env, term, self.span(), body_id) .into_iter() .flatten() { @@ -443,8 +443,8 @@ impl<'tcx> ProofTreeVisitor<'tcx> for BestObligation<'tcx> { polarity: ty::PredicatePolarity::Positive, })) } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - return self.visit_well_formed_goal(candidate, arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + return self.visit_well_formed_goal(candidate, term); } _ => ChildMode::PassThrough, }; diff --git a/compiler/rustc_trait_selection/src/traits/fulfill.rs b/compiler/rustc_trait_selection/src/traits/fulfill.rs index a11f8d3a9ec..34c3c905bd9 100644 --- a/compiler/rustc_trait_selection/src/traits/fulfill.rs +++ b/compiler/rustc_trait_selection/src/traits/fulfill.rs @@ -540,18 +540,18 @@ impl<'a, 'tcx> ObligationProcessor for FulfillProcessor<'a, 'tcx> { self.selcx.infcx.err_ctxt().report_overflow_obligation(&obligation, false); } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { match wf::obligations( self.selcx.infcx, obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { None => { pending_obligation.stalled_on = - vec![TyOrConstInferVar::maybe_from_generic_arg(arg).unwrap()]; + vec![TyOrConstInferVar::maybe_from_term(term).unwrap()]; ProcessResult::Unchanged } Some(os) => ProcessResult::Changed(mk_pending(obligation, os)), diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs index de4979cc1ff..dd868c9d40e 100644 --- a/compiler/rustc_trait_selection/src/traits/project.rs +++ b/compiler/rustc_trait_selection/src/traits/project.rs @@ -1034,42 +1034,6 @@ fn assemble_candidates_from_impls<'cx, 'tcx>( | ty::Infer(..) | ty::Error(_) => false, }, - Some(LangItem::AsyncDestruct) => match self_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(..) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(..) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::UnsafeBinder(_) - | ty::Dynamic(..) - | ty::Closure(..) - | ty::CoroutineClosure(..) - | ty::Coroutine(..) - | ty::CoroutineWitness(..) - | ty::Pat(..) - | ty::Never - | ty::Tuple(..) - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true, - - // type parameters, opaques, and unnormalized projections don't have - // a known async destructor and may need to be normalized further or rely - // on param env for async destructor projections - ty::Param(_) - | ty::Foreign(_) - | ty::Alias(..) - | ty::Bound(..) - | ty::Placeholder(..) - | ty::Infer(_) - | ty::Error(_) => false, - }, Some(LangItem::PointeeTrait) => { let tail = selcx.tcx().struct_tail_raw( self_ty, @@ -1528,11 +1492,6 @@ fn confirm_builtin_candidate<'cx, 'tcx>( assert_eq!(discriminant_def_id, item_def_id); (self_ty.discriminant_ty(tcx).into(), PredicateObligations::new()) - } else if tcx.is_lang_item(trait_def_id, LangItem::AsyncDestruct) { - let destructor_def_id = tcx.associated_item_def_ids(trait_def_id)[0]; - assert_eq!(destructor_def_id, item_def_id); - - (self_ty.async_destructor_ty(tcx).into(), PredicateObligations::new()) } else if tcx.is_lang_item(trait_def_id, LangItem::PointeeTrait) { let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None); assert_eq!(metadata_def_id, item_def_id); diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs index f059bd00768..81b5a131a32 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/ascribe_user_type.rs @@ -131,12 +131,12 @@ fn relate_mir_and_user_args<'tcx>( // const CONST: () = { /* arbitrary code that depends on T being WF */ }; // } // ``` - for arg in args { + for term in args.iter().filter_map(ty::GenericArg::as_term) { ocx.register_obligation(Obligation::new( tcx, cause.clone(), param_env, - ty::ClauseKind::WellFormed(arg), + ty::ClauseKind::WellFormed(term), )); } diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs index f98529860ff..1df69932c64 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/implied_outlives_bounds.rs @@ -113,8 +113,8 @@ pub fn compute_implied_outlives_bounds_inner<'tcx>( | ty::PredicateKind::AliasRelate(..) => {} // We need to search through *all* WellFormed predicates - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - wf_args.push(arg); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + wf_args.push(term); } // We need to register region relationships diff --git a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs index 87fc532adaf..18971c47831 100644 --- a/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs +++ b/compiler/rustc_trait_selection/src/traits/query/type_op/prove_predicate.rs @@ -19,10 +19,10 @@ impl<'tcx> super::QueryTypeOp<'tcx> for ProvePredicate<'tcx> { return Some(()); } - if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) = + if let ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) = key.value.predicate.kind().skip_binder() { - match arg.as_type()?.kind() { + match term.as_type()?.kind() { ty::Param(_) | ty::Bool | ty::Char diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index e9984da0033..5d0b9dd41b2 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -79,9 +79,6 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } else if tcx.is_lang_item(def_id, LangItem::DiscriminantKind) { // `DiscriminantKind` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); - } else if tcx.is_lang_item(def_id, LangItem::AsyncDestruct) { - // `AsyncDestruct` is automatically implemented for every type. - candidates.vec.push(BuiltinCandidate { has_nested: false }); } else if tcx.is_lang_item(def_id, LangItem::PointeeTrait) { // `Pointee` is automatically implemented for every type. candidates.vec.push(BuiltinCandidate { has_nested: false }); diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 5309c05e73d..df02a67c2c9 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -658,7 +658,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { } } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { // So, there is a bit going on here. First, `WellFormed` predicates // are coinductive, like trait predicates with auto traits. // This means that we need to detect if we have recursively @@ -682,11 +682,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { let cache = previous_stack.cache; let dfn = cache.next_dfn(); - for stack_arg in previous_stack.cache.wf_args.borrow().iter().rev() { - if stack_arg.0 != arg { + for stack_term in previous_stack.cache.wf_args.borrow().iter().rev() { + if stack_term.0 != term { continue; } - debug!("WellFormed({:?}) on stack", arg); + debug!("WellFormed({:?}) on stack", term); if let Some(stack) = previous_stack.head { // Okay, let's imagine we have two different stacks: // `T: NonAutoTrait -> WF(T) -> T: NonAutoTrait` @@ -702,11 +702,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // would contain `(T, 1)`. We want to check all // trait predicates greater than `1`. The previous // stack would be `T: Auto`. - let cycle = stack.iter().take_while(|s| s.depth > stack_arg.1); + let cycle = stack.iter().take_while(|s| s.depth > stack_term.1); let tcx = self.tcx(); let cycle = cycle.map(|stack| stack.obligation.predicate.upcast(tcx)); if self.coinductive_match(cycle) { - stack.update_reached_depth(stack_arg.1); + stack.update_reached_depth(stack_term.1); return Ok(EvaluatedToOk); } else { return Ok(EvaluatedToAmbigStackDependent); @@ -720,11 +720,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.param_env, obligation.cause.body_id, obligation.recursion_depth + 1, - arg, + term, obligation.cause.span, ) { Some(obligations) => { - cache.wf_args.borrow_mut().push((arg, previous_stack.depth())); + cache.wf_args.borrow_mut().push((term, previous_stack.depth())); let result = self.evaluate_predicates_recursively(previous_stack, obligations); cache.wf_args.borrow_mut().pop(); @@ -2969,14 +2969,14 @@ struct ProvisionalEvaluationCache<'tcx> { /// means the cached value for `F`. map: RefCell<FxIndexMap<ty::PolyTraitPredicate<'tcx>, ProvisionalEvaluation>>, - /// The stack of args that we assume to be true because a `WF(arg)` predicate + /// The stack of terms that we assume to be well-formed because a `WF(term)` predicate /// is on the stack above (and because of wellformedness is coinductive). /// In an "ideal" world, this would share a stack with trait predicates in /// `TraitObligationStack`. However, trait predicates are *much* hotter than /// `WellFormed` predicates, and it's very likely that the additional matches /// will have a perf effect. The value here is the well-formed `GenericArg` /// and the depth of the trait predicate *above* that well-formed predicate. - wf_args: RefCell<Vec<(ty::GenericArg<'tcx>, usize)>>, + wf_args: RefCell<Vec<(ty::Term<'tcx>, usize)>>, } /// A cache value for the provisional cache: contains the depth-first diff --git a/compiler/rustc_trait_selection/src/traits/wf.rs b/compiler/rustc_trait_selection/src/traits/wf.rs index f82257ab977..00a4a58a6d8 100644 --- a/compiler/rustc_trait_selection/src/traits/wf.rs +++ b/compiler/rustc_trait_selection/src/traits/wf.rs @@ -10,8 +10,8 @@ use rustc_hir::lang_items::LangItem; use rustc_infer::traits::{ObligationCauseCode, PredicateObligations}; use rustc_middle::bug; use rustc_middle::ty::{ - self, GenericArg, GenericArgKind, GenericArgsRef, Ty, TyCtxt, TypeSuperVisitable, - TypeVisitable, TypeVisitableExt, TypeVisitor, + self, GenericArgsRef, Term, TermKind, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, + TypeVisitableExt, TypeVisitor, }; use rustc_session::parse::feature_err; use rustc_span::def_id::{DefId, LocalDefId}; @@ -32,12 +32,12 @@ pub fn obligations<'tcx>( param_env: ty::ParamEnv<'tcx>, body_id: LocalDefId, recursion_depth: usize, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, ) -> Option<PredicateObligations<'tcx>> { // Handle the "cycle" case (see comment above) by bailing out if necessary. - let arg = match arg.unpack() { - GenericArgKind::Type(ty) => { + let term = match term.unpack() { + TermKind::Ty(ty) => { match ty.kind() { ty::Infer(ty::TyVar(_)) => { let resolved_ty = infcx.shallow_resolve(ty); @@ -52,7 +52,7 @@ pub fn obligations<'tcx>( } .into() } - GenericArgKind::Const(ct) => { + TermKind::Const(ct) => { match ct.kind() { ty::ConstKind::Infer(_) => { let resolved = infcx.shallow_resolve_const(ct); @@ -67,8 +67,6 @@ pub fn obligations<'tcx>( } .into() } - // There is nothing we have to do for lifetimes. - GenericArgKind::Lifetime(..) => return Some(PredicateObligations::new()), }; let mut wf = WfPredicates { @@ -80,11 +78,11 @@ pub fn obligations<'tcx>( recursion_depth, item: None, }; - wf.add_wf_preds_for_generic_arg(arg); - debug!("wf::obligations({:?}, body_id={:?}) = {:?}", arg, body_id, wf.out); + wf.add_wf_preds_for_term(term); + debug!("wf::obligations({:?}, body_id={:?}) = {:?}", term, body_id, wf.out); let result = wf.normalize(infcx); - debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", arg, body_id, result); + debug!("wf::obligations({:?}, body_id={:?}) ~~> {:?}", term, body_id, result); Some(result) } @@ -95,23 +93,19 @@ pub fn obligations<'tcx>( pub fn unnormalized_obligations<'tcx>( infcx: &InferCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>, - arg: GenericArg<'tcx>, + term: Term<'tcx>, span: Span, body_id: LocalDefId, ) -> Option<PredicateObligations<'tcx>> { - debug_assert_eq!(arg, infcx.resolve_vars_if_possible(arg)); + debug_assert_eq!(term, infcx.resolve_vars_if_possible(term)); // However, if `arg` IS an unresolved inference variable, returns `None`, // because we are not able to make any progress at all. This is to prevent // cycles where we say "?0 is WF if ?0 is WF". - if arg.is_non_region_infer() { + if term.is_infer() { return None; } - if let ty::GenericArgKind::Lifetime(..) = arg.unpack() { - return Some(PredicateObligations::new()); - } - let mut wf = WfPredicates { infcx, param_env, @@ -121,7 +115,7 @@ pub fn unnormalized_obligations<'tcx>( recursion_depth: 0, item: None, }; - wf.add_wf_preds_for_generic_arg(arg); + wf.add_wf_preds_for_term(term); Some(wf.out) } @@ -185,22 +179,22 @@ pub fn clause_obligations<'tcx>( } ty::ClauseKind::RegionOutlives(..) => {} ty::ClauseKind::TypeOutlives(ty::OutlivesPredicate(ty, _reg)) => { - wf.add_wf_preds_for_generic_arg(ty.into()); + wf.add_wf_preds_for_term(ty.into()); } ty::ClauseKind::Projection(t) => { wf.add_wf_preds_for_alias_term(t.projection_term); - wf.add_wf_preds_for_generic_arg(t.term.into_arg()); + wf.add_wf_preds_for_term(t.term); } ty::ClauseKind::ConstArgHasType(ct, ty) => { - wf.add_wf_preds_for_generic_arg(ct.into()); - wf.add_wf_preds_for_generic_arg(ty.into()); + wf.add_wf_preds_for_term(ct.into()); + wf.add_wf_preds_for_term(ty.into()); } - ty::ClauseKind::WellFormed(arg) => { - wf.add_wf_preds_for_generic_arg(arg); + ty::ClauseKind::WellFormed(term) => { + wf.add_wf_preds_for_term(term); } ty::ClauseKind::ConstEvaluatable(ct) => { - wf.add_wf_preds_for_generic_arg(ct.into()); + wf.add_wf_preds_for_term(ct.into()); } } @@ -426,11 +420,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { .args .iter() .enumerate() - .filter(|(_, arg)| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|(_, arg)| !arg.has_escaping_bound_vars()) - .map(|(i, arg)| { + .filter_map(|(i, arg)| arg.as_term().map(|t| (i, t))) + .filter(|(_, term)| !term.has_escaping_bound_vars()) + .map(|(i, term)| { let mut cause = traits::ObligationCause::misc(self.span, self.body_id); // The first arg is the self ty - use the correct span for it. if i == 0 { @@ -445,9 +437,7 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { cause, depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -457,7 +447,9 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { // given that it is a *negative* trait predicate. fn add_wf_preds_for_negative_trait_pred(&mut self, trait_ref: ty::TraitRef<'tcx>) { for arg in trait_ref.args { - self.add_wf_preds_for_generic_arg(arg); + if let Some(term) = arg.as_term() { + self.add_wf_preds_for_term(term); + } } } @@ -529,19 +521,15 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { self.out.extend( args.iter() - .filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) - .filter(|arg| !arg.has_escaping_bound_vars()) - .map(|arg| { + .filter_map(|arg| arg.as_term()) + .filter(|term| !term.has_escaping_bound_vars()) + .map(|term| { traits::Obligation::with_depth( tcx, cause.clone(), depth, param_env, - ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed( - arg, - ))), + ty::ClauseKind::WellFormed(term), ) }), ); @@ -565,10 +553,10 @@ impl<'a, 'tcx> WfPredicates<'a, 'tcx> { } } - /// Pushes all the predicates needed to validate that `ty` is WF into `out`. + /// Pushes all the predicates needed to validate that `term` is WF into `out`. #[instrument(level = "debug", skip(self))] - fn add_wf_preds_for_generic_arg(&mut self, arg: GenericArg<'tcx>) { - arg.visit_with(self); + fn add_wf_preds_for_term(&mut self, term: Term<'tcx>) { + term.visit_with(self); debug!(?self.out); } diff --git a/compiler/rustc_transmute/src/layout/mod.rs b/compiler/rustc_transmute/src/layout/mod.rs index d555ea702a9..4d5f630ae22 100644 --- a/compiler/rustc_transmute/src/layout/mod.rs +++ b/compiler/rustc_transmute/src/layout/mod.rs @@ -65,7 +65,12 @@ impl fmt::Debug for Byte { } } -#[cfg(test)] +impl From<RangeInclusive<u8>> for Byte { + fn from(src: RangeInclusive<u8>) -> Self { + Self::new(src) + } +} + impl From<u8> for Byte { fn from(src: u8) -> Self { Self::from_val(src) diff --git a/compiler/rustc_transmute/src/layout/tree.rs b/compiler/rustc_transmute/src/layout/tree.rs index 6a09be18ef9..7cf712ce9e9 100644 --- a/compiler/rustc_transmute/src/layout/tree.rs +++ b/compiler/rustc_transmute/src/layout/tree.rs @@ -1,4 +1,4 @@ -use std::ops::ControlFlow; +use std::ops::{ControlFlow, RangeInclusive}; use super::{Byte, Def, Ref}; @@ -32,6 +32,22 @@ where Byte(Byte), } +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub(crate) enum Endian { + Little, + Big, +} + +#[cfg(feature = "rustc")] +impl From<rustc_abi::Endian> for Endian { + fn from(order: rustc_abi::Endian) -> Endian { + match order { + rustc_abi::Endian::Little => Endian::Little, + rustc_abi::Endian::Big => Endian::Big, + } + } +} + impl<D, R> Tree<D, R> where D: Def, @@ -59,22 +75,60 @@ where /// A `Tree` representing the layout of `bool`. pub(crate) fn bool() -> Self { - Self::Byte(Byte::new(0x00..=0x01)) + Self::byte(0x00..=0x01) } /// A `Tree` whose layout matches that of a `u8`. pub(crate) fn u8() -> Self { - Self::Byte(Byte::new(0x00..=0xFF)) + Self::byte(0x00..=0xFF) + } + + /// A `Tree` whose layout matches that of a `char`. + pub(crate) fn char(order: Endian) -> Self { + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // All other `char` values are illegal. We can thus represent a `char` + // as a union of three possible layouts: + // - 00 00 [00, D7] XX + // - 00 00 [E0, FF] XX + // - 00 [01, 10] XX XX + + const _0: RangeInclusive<u8> = 0..=0; + const BYTE: RangeInclusive<u8> = 0x00..=0xFF; + let x = Self::from_big_endian(order, [_0, _0, 0x00..=0xD7, BYTE]); + let y = Self::from_big_endian(order, [_0, _0, 0xE0..=0xFF, BYTE]); + let z = Self::from_big_endian(order, [_0, 0x01..=0x10, BYTE, BYTE]); + Self::alt([x, y, z]) } - /// A `Tree` whose layout accepts exactly the given bit pattern. - pub(crate) fn from_bits(bits: u8) -> Self { - Self::Byte(Byte::from_val(bits)) + /// A `Tree` whose layout matches `std::num::NonZeroXxx`. + #[allow(dead_code)] + pub(crate) fn nonzero(width_in_bytes: u64) -> Self { + const BYTE: RangeInclusive<u8> = 0x00..=0xFF; + const NONZERO: RangeInclusive<u8> = 0x01..=0xFF; + + (0..width_in_bytes) + .map(|nz_idx| { + (0..width_in_bytes) + .map(|pos| Self::byte(if pos == nz_idx { NONZERO } else { BYTE })) + .fold(Self::unit(), Self::then) + }) + .fold(Self::uninhabited(), Self::or) + } + + pub(crate) fn bytes<const N: usize, B: Into<Byte>>(bytes: [B; N]) -> Self { + Self::seq(bytes.map(B::into).map(Self::Byte)) + } + + pub(crate) fn byte(byte: impl Into<Byte>) -> Self { + Self::Byte(byte.into()) } /// A `Tree` whose layout is a number of the given width. - pub(crate) fn number(width_in_bytes: usize) -> Self { - Self::Seq(vec![Self::u8(); width_in_bytes]) + pub(crate) fn number(width_in_bytes: u64) -> Self { + Self::Seq(vec![Self::u8(); width_in_bytes.try_into().unwrap()]) } /// A `Tree` whose layout is entirely padding of the given width. @@ -125,13 +179,35 @@ where Self::Byte(..) | Self::Ref(..) | Self::Def(..) => true, } } -} -impl<D, R> Tree<D, R> -where - D: Def, - R: Ref, -{ + /// Produces a `Tree` which represents a sequence of bytes stored in + /// `order`. + /// + /// `bytes` is taken to be in big-endian byte order, and its order will be + /// swapped if `order == Endian::Little`. + pub(crate) fn from_big_endian<const N: usize, B: Into<Byte>>( + order: Endian, + mut bytes: [B; N], + ) -> Self { + if order == Endian::Little { + (&mut bytes[..]).reverse(); + } + + Self::bytes(bytes) + } + + /// Produces a `Tree` where each of the trees in `trees` are sequenced one + /// after another. + pub(crate) fn seq<const N: usize>(trees: [Tree<D, R>; N]) -> Self { + trees.into_iter().fold(Tree::unit(), Self::then) + } + + /// Produces a `Tree` where each of the trees in `trees` are accepted as + /// alternative layouts. + pub(crate) fn alt<const N: usize>(trees: [Tree<D, R>; N]) -> Self { + trees.into_iter().fold(Tree::uninhabited(), Self::or) + } + /// Produces a new `Tree` where `other` is sequenced after `self`. pub(crate) fn then(self, other: Self) -> Self { match (self, other) { @@ -222,17 +298,17 @@ pub(crate) mod rustc { ty::Float(nty) => { let width = nty.bit_width() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Int(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Uint(nty) => { let width = nty.normalize(pointer_size.bits() as _).bit_width().unwrap() / 8; - Ok(Self::number(width as _)) + Ok(Self::number(width.try_into().unwrap())) } ty::Tuple(members) => Self::from_tuple((ty, layout), members, cx), @@ -249,11 +325,33 @@ pub(crate) mod rustc { .fold(Tree::unit(), |tree, elt| tree.then(elt))) } - ty::Adt(adt_def, _args_ref) if !ty.is_box() => match adt_def.adt_kind() { - AdtKind::Struct => Self::from_struct((ty, layout), *adt_def, cx), - AdtKind::Enum => Self::from_enum((ty, layout), *adt_def, cx), - AdtKind::Union => Self::from_union((ty, layout), *adt_def, cx), - }, + ty::Adt(adt_def, _args_ref) if !ty.is_box() => { + let (lo, hi) = cx.tcx().layout_scalar_valid_range(adt_def.did()); + + use core::ops::Bound::*; + let is_transparent = adt_def.repr().transparent(); + match (adt_def.adt_kind(), lo, hi) { + (AdtKind::Struct, Unbounded, Unbounded) => { + Self::from_struct((ty, layout), *adt_def, cx) + } + (AdtKind::Struct, Included(1), Included(_hi)) if is_transparent => { + // FIXME(@joshlf): Support `NonZero` types: + // - Check to make sure that the first field is + // numerical + // - Check to make sure that the upper bound is the + // maximum value for the field's type + // - Construct `Self::nonzero` + Err(Err::NotYetSupported) + } + (AdtKind::Enum, Unbounded, Unbounded) => { + Self::from_enum((ty, layout), *adt_def, cx) + } + (AdtKind::Union, Unbounded, Unbounded) => { + Self::from_union((ty, layout), *adt_def, cx) + } + _ => Err(Err::NotYetSupported), + } + } ty::Ref(lifetime, ty, mutability) => { let layout = layout_of(cx, *ty)?; @@ -268,6 +366,8 @@ pub(crate) mod rustc { })) } + ty::Char => Ok(Self::char(cx.tcx().data_layout.endian.into())), + _ => Err(Err::NotYetSupported), } } @@ -450,7 +550,7 @@ pub(crate) mod rustc { &bytes[bytes.len() - size.bytes_usize()..] } }; - Self::Seq(bytes.iter().map(|&b| Self::from_bits(b)).collect()) + Self::Seq(bytes.iter().map(|&b| Self::byte(b)).collect()) } /// Constructs a `Tree` from a union. diff --git a/compiler/rustc_transmute/src/layout/tree/tests.rs b/compiler/rustc_transmute/src/layout/tree/tests.rs index 44f50a25c93..8c3dbbe37ab 100644 --- a/compiler/rustc_transmute/src/layout/tree/tests.rs +++ b/compiler/rustc_transmute/src/layout/tree/tests.rs @@ -20,23 +20,18 @@ mod prune { #[test] fn seq_1() { - let layout: Tree<Def, !> = - Tree::def(Def::NoSafetyInvariants).then(Tree::from_bits(0x00)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants).then(Tree::byte(0x00)); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } #[test] fn seq_2() { - let layout: Tree<Def, !> = Tree::from_bits(0x00) - .then(Tree::def(Def::NoSafetyInvariants)) - .then(Tree::from_bits(0x01)); + let layout: Tree<Def, !> = + Tree::byte(0x00).then(Tree::def(Def::NoSafetyInvariants)).then(Tree::byte(0x01)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00).then(Tree::from_bits(0x01)) + Tree::byte(0x00).then(Tree::byte(0x01)) ); } } @@ -66,7 +61,7 @@ mod prune { #[test] fn invisible_def_in_seq_len_3() { let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::HasSafetyInvariants)); assert_eq!( layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), @@ -94,12 +89,9 @@ mod prune { #[test] fn visible_def_in_seq_len_3() { let layout: Tree<Def, !> = Tree::def(Def::NoSafetyInvariants) - .then(Tree::from_bits(0x00)) + .then(Tree::byte(0x00)) .then(Tree::def(Def::NoSafetyInvariants)); - assert_eq!( - layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), - Tree::from_bits(0x00) - ); + assert_eq!(layout.prune(&|d| matches!(d, Def::HasSafetyInvariants)), Tree::byte(0x00)); } } } diff --git a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs index 24e2a1acadd..992fcb7cc4c 100644 --- a/compiler/rustc_transmute/src/maybe_transmutable/tests.rs +++ b/compiler/rustc_transmute/src/maybe_transmutable/tests.rs @@ -177,9 +177,9 @@ mod bool { #[test] fn should_permit_validity_expansion_and_reject_contraction() { - let b0 = layout::Tree::<Def, !>::from_bits(0); - let b1 = layout::Tree::<Def, !>::from_bits(1); - let b2 = layout::Tree::<Def, !>::from_bits(2); + let b0 = layout::Tree::<Def, !>::byte(0); + let b1 = layout::Tree::<Def, !>::byte(1); + let b2 = layout::Tree::<Def, !>::byte(2); let alts = [b0, b1, b2]; @@ -279,8 +279,8 @@ mod alt { fn should_permit_identity_transmutation() { type Tree = layout::Tree<Def, !>; - let x = Tree::Seq(vec![Tree::from_bits(0), Tree::from_bits(0)]); - let y = Tree::Seq(vec![Tree::bool(), Tree::from_bits(1)]); + let x = Tree::Seq(vec![Tree::byte(0), Tree::byte(0)]); + let y = Tree::Seq(vec![Tree::bool(), Tree::byte(1)]); let layout = Tree::Alt(vec![x, y]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( @@ -323,6 +323,76 @@ mod union { } } +mod char { + use super::*; + use crate::layout::tree::Endian; + + #[test] + fn should_permit_valid_transmutation() { + for order in [Endian::Big, Endian::Little] { + use Answer::*; + let char_layout = layout::Tree::<Def, !>::char(order); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // This loop synthesizes a singleton-validity type for the extremes + // of each range, and for one past the end of the extremes of each + // range. + let no = No(Reason::DstIsBitIncompatible); + for (src, answer) in [ + (0u32, Yes), + (0xD7FF, Yes), + (0xD800, no.clone()), + (0xDFFF, no.clone()), + (0xE000, Yes), + (0x10FFFF, Yes), + (0x110000, no.clone()), + (0xFFFF0000, no.clone()), + (0xFFFFFFFF, no), + ] { + let src_layout = + layout::tree::Tree::<Def, !>::from_big_endian(order, src.to_be_bytes()); + + let a = is_transmutable(&src_layout, &char_layout, Assume::default()); + assert_eq!(a, answer, "endian:{order:?},\nsrc:{src:x}"); + } + } + } +} + +mod nonzero { + use super::*; + use crate::{Answer, Reason}; + + const NONZERO_BYTE_WIDTHS: [u64; 5] = [1, 2, 4, 8, 16]; + + #[test] + fn should_permit_identity_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + let layout = layout::Tree::<Def, !>::nonzero(width); + assert_eq!(is_transmutable(&layout, &layout, Assume::default()), Answer::Yes); + } + } + + #[test] + fn should_permit_valid_transmutation() { + for width in NONZERO_BYTE_WIDTHS { + use Answer::*; + + let num = layout::Tree::<Def, !>::number(width); + let nz = layout::Tree::<Def, !>::nonzero(width); + + let a = is_transmutable(&num, &nz, Assume::default()); + assert_eq!(a, No(Reason::DstIsBitIncompatible), "width:{width}"); + + let a = is_transmutable(&nz, &num, Assume::default()); + assert_eq!(a, Yes, "width:{width}"); + } + } +} + mod r#ref { use super::*; @@ -330,7 +400,7 @@ mod r#ref { fn should_permit_identity_transmutation() { type Tree = crate::layout::Tree<Def, [(); 1]>; - let layout = Tree::Seq(vec![Tree::from_bits(0), Tree::Ref([()])]); + let layout = Tree::Seq(vec![Tree::byte(0x00), Tree::Ref([()])]); let answer = crate::maybe_transmutable::MaybeTransmutableQuery::new( layout.clone(), diff --git a/compiler/rustc_ty_utils/src/abi.rs b/compiler/rustc_ty_utils/src/abi.rs index 63ea035bd0e..2b49d7ac8b5 100644 --- a/compiler/rustc_ty_utils/src/abi.rs +++ b/compiler/rustc_ty_utils/src/abi.rs @@ -551,8 +551,10 @@ fn fn_abi_new_uncached<'tcx>( extra_args }; - let is_drop_in_place = - determined_fn_def_id.is_some_and(|def_id| tcx.is_lang_item(def_id, LangItem::DropInPlace)); + let is_drop_in_place = determined_fn_def_id.is_some_and(|def_id| { + tcx.is_lang_item(def_id, LangItem::DropInPlace) + || tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) + }); let arg_of = |ty: Ty<'tcx>, arg_idx: Option<usize>| -> Result<_, &'tcx FnAbiError<'tcx>> { let span = tracing::debug_span!("arg_of"); diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs index 20646cf9a82..bb2c4172b08 100644 --- a/compiler/rustc_ty_utils/src/common_traits.rs +++ b/compiler/rustc_ty_utils/src/common_traits.rs @@ -29,6 +29,13 @@ fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, T is_item_raw(tcx, query, LangItem::Unpin) } +fn is_async_drop_raw<'tcx>( + tcx: TyCtxt<'tcx>, + query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, +) -> bool { + is_item_raw(tcx, query, LangItem::AsyncDrop) +} + fn is_item_raw<'tcx>( tcx: TyCtxt<'tcx>, query: ty::PseudoCanonicalInput<'tcx, Ty<'tcx>>, @@ -46,6 +53,7 @@ pub(crate) fn provide(providers: &mut Providers) { is_sized_raw, is_freeze_raw, is_unpin_raw, + is_async_drop_raw, ..*providers }; } diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs index 66c18bed5e7..da83566dad0 100644 --- a/compiler/rustc_ty_utils/src/instance.rs +++ b/compiler/rustc_ty_utils/src/instance.rs @@ -5,7 +5,6 @@ use rustc_infer::infer::TyCtxtInferExt; use rustc_middle::bug; use rustc_middle::query::Providers; use rustc_middle::traits::{BuiltinImplSource, CodegenObligationError}; -use rustc_middle::ty::util::AsyncDropGlueMorphology; use rustc_middle::ty::{ self, ClosureKind, GenericArgsRef, Instance, PseudoCanonicalInput, TyCtxt, TypeVisitableExt, }; @@ -41,20 +40,26 @@ fn resolve_instance_raw<'tcx>( if ty.needs_drop(tcx, typing_env) { debug!(" => nontrivial drop glue"); match *ty.kind() { + ty::Coroutine(coroutine_def_id, ..) => { + // FIXME: sync drop of coroutine with async drop (generate both versions?) + // Currently just ignored + if tcx.optimized_mir(coroutine_def_id).coroutine_drop_async().is_some() { + ty::InstanceKind::DropGlue(def_id, None) + } else { + ty::InstanceKind::DropGlue(def_id, Some(ty)) + } + } ty::Closure(..) | ty::CoroutineClosure(..) - | ty::Coroutine(..) | ty::Tuple(..) | ty::Adt(..) | ty::Dynamic(..) | ty::Array(..) | ty::Slice(..) - | ty::UnsafeBinder(..) => {} + | ty::UnsafeBinder(..) => ty::InstanceKind::DropGlue(def_id, Some(ty)), // Drop shims can only be built from ADTs. _ => return Ok(None), } - - ty::InstanceKind::DropGlue(def_id, Some(ty)) } else { debug!(" => trivial drop glue"); ty::InstanceKind::DropGlue(def_id, None) @@ -62,7 +67,7 @@ fn resolve_instance_raw<'tcx>( } else if tcx.is_lang_item(def_id, LangItem::AsyncDropInPlace) { let ty = args.type_at(0); - if ty.async_drop_glue_morphology(tcx) != AsyncDropGlueMorphology::Noop { + if ty.needs_async_drop(tcx, typing_env) { match *ty.kind() { ty::Closure(..) | ty::CoroutineClosure(..) @@ -76,11 +81,14 @@ fn resolve_instance_raw<'tcx>( _ => return Ok(None), } debug!(" => nontrivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, Some(ty)) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) } else { debug!(" => trivial async drop glue ctor"); - ty::InstanceKind::AsyncDropGlueCtorShim(def_id, None) + ty::InstanceKind::AsyncDropGlueCtorShim(def_id, ty) } + } else if tcx.is_async_drop_in_place_coroutine(def_id) { + let ty = args.type_at(0); + ty::InstanceKind::AsyncDropGlue(def_id, ty) } else { debug!(" => free item"); ty::InstanceKind::Item(def_id) diff --git a/compiler/rustc_ty_utils/src/layout.rs b/compiler/rustc_ty_utils/src/layout.rs index 1915ba623cb..16336ed530a 100644 --- a/compiler/rustc_ty_utils/src/layout.rs +++ b/compiler/rustc_ty_utils/src/layout.rs @@ -184,6 +184,10 @@ fn layout_of_uncached<'tcx>( } let tcx = cx.tcx(); + + // layout of `async_drop_in_place<T>::{closure}` in case, + // when T is a coroutine, contains this internal coroutine's ref + let dl = cx.data_layout(); let map_layout = |result: Result<_, _>| match result { Ok(layout) => Ok(tcx.mk_layout(layout)), @@ -406,7 +410,7 @@ fn layout_of_uncached<'tcx>( ty::Coroutine(def_id, args) => { use rustc_middle::ty::layout::PrimitiveExt as _; - let Some(info) = tcx.coroutine_layout(def_id, args.as_coroutine().kind_ty()) else { + let Some(info) = tcx.coroutine_layout(def_id, args) else { return Err(error(cx, LayoutError::Unknown(ty))); }; @@ -770,7 +774,7 @@ fn variant_info_for_coroutine<'tcx>( return (vec![], None); }; - let coroutine = cx.tcx().coroutine_layout(def_id, args.as_coroutine().kind_ty()).unwrap(); + let coroutine = cx.tcx().coroutine_layout(def_id, args).unwrap(); let upvar_names = cx.tcx().closure_saved_names_of_captured_variables(def_id); let mut upvars_size = Size::ZERO; diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs index 52955ec59a4..c3b04c20f4b 100644 --- a/compiler/rustc_ty_utils/src/needs_drop.rs +++ b/compiler/rustc_ty_utils/src/needs_drop.rs @@ -42,11 +42,11 @@ fn needs_async_drop_raw<'tcx>( let adt_has_async_dtor = |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); let res = drop_tys_helper(tcx, query.value, query.typing_env, adt_has_async_dtor, false, false) - .filter(filter_array_elements(tcx, query.typing_env)) + .filter(filter_array_elements_async(tcx, query.typing_env)) .next() .is_some(); - debug!("needs_drop_raw({:?}) = {:?}", query, res); + debug!("needs_async_drop_raw({:?}) = {:?}", query, res); res } @@ -66,6 +66,18 @@ fn filter_array_elements<'tcx>( Err(AlwaysRequiresDrop) => true, } } +fn filter_array_elements_async<'tcx>( + tcx: TyCtxt<'tcx>, + typing_env: ty::TypingEnv<'tcx>, +) -> impl Fn(&Result<Ty<'tcx>, AlwaysRequiresDrop>) -> bool { + move |ty| match ty { + Ok(ty) => match *ty.kind() { + ty::Array(elem, _) => tcx.needs_async_drop_raw(typing_env.as_query_input(elem)), + _ => true, + }, + Err(AlwaysRequiresDrop) => true, + } +} fn has_significant_drop_raw<'tcx>( tcx: TyCtxt<'tcx>, @@ -414,6 +426,27 @@ fn adt_drop_tys<'tcx>( .collect::<Result<Vec<_>, _>>() .map(|components| tcx.mk_type_list(&components)) } + +fn adt_async_drop_tys<'tcx>( + tcx: TyCtxt<'tcx>, + def_id: DefId, +) -> Result<&'tcx ty::List<Ty<'tcx>>, AlwaysRequiresDrop> { + // This is for the "adt_async_drop_tys" query, that considers all `AsyncDrop` impls. + let adt_has_dtor = + |adt_def: ty::AdtDef<'tcx>| adt_def.async_destructor(tcx).map(|_| DtorType::Significant); + // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_args)` + drop_tys_helper( + tcx, + tcx.type_of(def_id).instantiate_identity(), + ty::TypingEnv::non_body_analysis(tcx, def_id), + adt_has_dtor, + false, + false, + ) + .collect::<Result<Vec<_>, _>>() + .map(|components| tcx.mk_type_list(&components)) +} + // If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed // a `tcx.make_ty(def, identity_args)` and as such it is legal to instantiate the generic parameters // of the ADT into the outputted `ty`s. @@ -458,6 +491,7 @@ pub(crate) fn provide(providers: &mut Providers) { needs_async_drop_raw, has_significant_drop_raw, adt_drop_tys, + adt_async_drop_tys, adt_significant_drop_tys, list_significant_drop_tys, ..*providers diff --git a/compiler/rustc_type_ir/src/flags.rs b/compiler/rustc_type_ir/src/flags.rs index 4eead83c113..b37347354fa 100644 --- a/compiler/rustc_type_ir/src/flags.rs +++ b/compiler/rustc_type_ir/src/flags.rs @@ -1,5 +1,3 @@ -use std::slice; - use crate::inherent::*; use crate::visit::Flags; use crate::{self as ty, Interner}; @@ -388,8 +386,8 @@ impl<I: Interner> FlagComputation<I> { self.add_alias_term(projection_term); self.add_term(term); } - ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(arg)) => { - self.add_args(slice::from_ref(&arg)); + ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(term)) => { + self.add_term(term); } ty::PredicateKind::DynCompatible(_def_id) => {} ty::PredicateKind::Clause(ty::ClauseKind::ConstEvaluatable(uv)) => { diff --git a/compiler/rustc_type_ir/src/inherent.rs b/compiler/rustc_type_ir/src/inherent.rs index 9b066b6869f..e6e6466766b 100644 --- a/compiler/rustc_type_ir/src/inherent.rs +++ b/compiler/rustc_type_ir/src/inherent.rs @@ -151,7 +151,6 @@ pub trait Ty<I: Interner<Ty = Self>>: fn discriminant_ty(self, interner: I) -> I::Ty; - fn async_destructor_ty(self, interner: I) -> I::Ty; fn is_known_rigid(self) -> bool { self.kind().is_known_rigid() } diff --git a/compiler/rustc_type_ir/src/interner.rs b/compiler/rustc_type_ir/src/interner.rs index ab38556589e..9758cecaf6a 100644 --- a/compiler/rustc_type_ir/src/interner.rs +++ b/compiler/rustc_type_ir/src/interner.rs @@ -286,6 +286,8 @@ pub trait Interner: fn has_item_definition(self, def_id: Self::DefId) -> bool; + fn impl_specializes(self, impl_def_id: Self::DefId, victim_def_id: Self::DefId) -> bool; + fn impl_is_default(self, impl_def_id: Self::DefId) -> bool; fn impl_trait_ref(self, impl_def_id: Self::DefId) -> ty::EarlyBinder<Self, ty::TraitRef<Self>>; diff --git a/compiler/rustc_type_ir/src/lang_items.rs b/compiler/rustc_type_ir/src/lang_items.rs index 65f7cdf8f92..699dd82fb22 100644 --- a/compiler/rustc_type_ir/src/lang_items.rs +++ b/compiler/rustc_type_ir/src/lang_items.rs @@ -2,7 +2,6 @@ /// representation of `LangItem`s used in the underlying compiler implementation. pub enum TraitSolverLangItem { // tidy-alphabetical-start - AsyncDestruct, AsyncFn, AsyncFnKindHelper, AsyncFnKindUpvars, diff --git a/compiler/rustc_type_ir/src/predicate_kind.rs b/compiler/rustc_type_ir/src/predicate_kind.rs index 847dff156fe..4e41fd16ffd 100644 --- a/compiler/rustc_type_ir/src/predicate_kind.rs +++ b/compiler/rustc_type_ir/src/predicate_kind.rs @@ -36,7 +36,7 @@ pub enum ClauseKind<I: Interner> { ConstArgHasType(I::Const, I::Ty), /// No syntax: `T` well-formed. - WellFormed(I::GenericArg), + WellFormed(I::Term), /// Constant initializer must evaluate successfully. ConstEvaluatable(I::Const), diff --git a/library/Cargo.lock b/library/Cargo.lock index f7f09a11f3a..c21dec14986 100644 --- a/library/Cargo.lock +++ b/library/Cargo.lock @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "r-efi" -version = "4.5.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" dependencies = [ "compiler_builtins", "rustc-std-workspace-core", @@ -267,9 +267,9 @@ dependencies = [ [[package]] name = "r-efi-alloc" -version = "1.0.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31d6f09fe2b6ad044bc3d2c34ce4979796581afd2f1ebc185837e02421e02fd7" +checksum = "e43c53ff1a01d423d1cb762fd991de07d32965ff0ca2e4f80444ac7804198203" dependencies = [ "compiler_builtins", "r-efi", diff --git a/library/alloc/src/ffi/c_str.rs b/library/alloc/src/ffi/c_str.rs index d5b19470e31..8b448a18402 100644 --- a/library/alloc/src/ffi/c_str.rs +++ b/library/alloc/src/ffi/c_str.rs @@ -351,9 +351,14 @@ impl CString { /// # Safety /// /// This should only ever be called with a pointer that was earlier - /// obtained by calling [`CString::into_raw`]. Other usage (e.g., trying to take - /// ownership of a string that was allocated by foreign code) is likely to lead - /// to undefined behavior or allocator corruption. + /// obtained by calling [`CString::into_raw`], and the memory it points to must not be accessed + /// through any other pointer during the lifetime of reconstructed `CString`. + /// Other usage (e.g., trying to take ownership of a string that was allocated by foreign code) + /// is likely to lead to undefined behavior or allocator corruption. + /// + /// This function does not validate ownership of the raw pointer's memory. + /// A double-free may occur if the function is called twice on the same raw pointer. + /// Additionally, the caller must ensure the pointer is not dangling. /// /// It should be noted that the length isn't just "recomputed," but that /// the recomputed length must match the original length from the @@ -818,6 +823,7 @@ impl From<Vec<NonZero<u8>>> for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl FromStr for CString { type Err = NulError; @@ -830,6 +836,7 @@ impl FromStr for CString { } } +#[stable(feature = "c_string_from_str", since = "1.85.0")] impl TryFrom<CString> for String { type Error = IntoStringError; diff --git a/library/alloc/src/ffi/mod.rs b/library/alloc/src/ffi/mod.rs index 695d7ad07cf..05a2763a225 100644 --- a/library/alloc/src/ffi/mod.rs +++ b/library/alloc/src/ffi/mod.rs @@ -87,5 +87,5 @@ pub use self::c_str::CString; #[stable(feature = "alloc_c_string", since = "1.64.0")] pub use self::c_str::{FromVecWithNulError, IntoStringError, NulError}; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index e70e6ca0d55..abda5aefab6 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -122,6 +122,7 @@ #![feature(fmt_internals)] #![feature(fn_traits)] #![feature(formatting_options)] +#![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(inplace_iteration)] #![feature(iter_advance_by)] diff --git a/library/alloc/src/str.rs b/library/alloc/src/str.rs index 0664f2c3cf2..24c5d4c92f7 100644 --- a/library/alloc/src/str.rs +++ b/library/alloc/src/str.rs @@ -603,6 +603,10 @@ impl str { /// Converts a boxed slice of bytes to a boxed string slice without checking /// that the string contains valid UTF-8. /// +/// # Safety +/// +/// * The provided bytes must contain a valid UTF-8 sequence. +/// /// # Examples /// /// ``` diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index be581661f4c..17090925cfa 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -26,8 +26,8 @@ use core::pin::{Pin, PinCoerceUnsized}; use core::ptr::{self, NonNull}; #[cfg(not(no_global_oom_handling))] use core::slice::from_raw_parts_mut; -use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use core::sync::atomic::{self, Atomic}; use core::{borrow, fmt, hint}; #[cfg(not(no_global_oom_handling))] @@ -369,12 +369,12 @@ impl<T: ?Sized, A: Allocator> fmt::Debug for Weak<T, A> { // inner types. #[repr(C)] struct ArcInner<T: ?Sized> { - strong: atomic::AtomicUsize, + strong: Atomic<usize>, // the value usize::MAX acts as a sentinel for temporarily "locking" the // ability to upgrade weak pointers or downgrade strong ones; this is used // to avoid races in `make_mut` and `get_mut`. - weak: atomic::AtomicUsize, + weak: Atomic<usize>, data: T, } @@ -2446,7 +2446,7 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { #[inline] #[stable(feature = "arc_unique", since = "1.4.0")] pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { + if Self::is_unique(this) { // This unsafety is ok because we're guaranteed that the pointer // returned is the *only* pointer that will ever be returned to T. Our // reference count is guaranteed to be 1 at this point, and we required @@ -2526,11 +2526,64 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { unsafe { &mut (*this.ptr.as_ptr()).data } } - /// Determine whether this is the unique reference (including weak refs) to - /// the underlying data. + /// Determine whether this is the unique reference to the underlying data. /// - /// Note that this requires locking the weak ref count. - fn is_unique(&mut self) -> bool { + /// Returns `true` if there are no other `Arc` or [`Weak`] pointers to the same allocation; + /// returns `false` otherwise. + /// + /// If this function returns `true`, then is guaranteed to be safe to call [`get_mut_unchecked`] + /// on this `Arc`, so long as no clones occur in between. + /// + /// # Examples + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert!(Arc::is_unique(&x)); + /// + /// let y = Arc::clone(&x); + /// assert!(!Arc::is_unique(&x)); + /// drop(y); + /// + /// // Weak references also count, because they could be upgraded at any time. + /// let z = Arc::downgrade(&x); + /// assert!(!Arc::is_unique(&x)); + /// ``` + /// + /// # Pointer invalidation + /// + /// This function will always return the same value as `Arc::get_mut(arc).is_some()`. However, + /// unlike that operation it does not produce any mutable references to the underlying data, + /// meaning no pointers to the data inside the `Arc` are invalidated by the call. Thus, the + /// following code is valid, even though it would be UB if it used `Arc::get_mut`: + /// + /// ``` + /// #![feature(arc_is_unique)] + /// + /// use std::sync::Arc; + /// + /// let arc = Arc::new(5); + /// let pointer: *const i32 = &*arc; + /// assert!(Arc::is_unique(&arc)); + /// assert_eq!(unsafe { *pointer }, 5); + /// ``` + /// + /// # Atomic orderings + /// + /// Concurrent drops to other `Arc` pointers to the same allocation will synchronize with this + /// call - that is, this call performs an `Acquire` operation on the underlying strong and weak + /// ref counts. This ensures that calling `get_mut_unchecked` is safe. + /// + /// Note that this operation requires locking the weak ref count, so concurrent calls to + /// `downgrade` may spin-loop for a short period of time. + /// + /// [`get_mut_unchecked`]: Self::get_mut_unchecked + #[inline] + #[unstable(feature = "arc_is_unique", issue = "138938")] + pub fn is_unique(this: &Self) -> bool { // lock the weak pointer count if we appear to be the sole weak pointer // holder. // @@ -2538,16 +2591,16 @@ impl<T: ?Sized, A: Allocator> Arc<T, A> { // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. - if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + if this.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { // This needs to be an `Acquire` to synchronize with the decrement of the `strong` // counter in `drop` -- the only access that happens when any but the last reference // is being dropped. - let unique = self.inner().strong.load(Acquire) == 1; + let unique = this.inner().strong.load(Acquire) == 1; // The release write here synchronizes with a read in `downgrade`, // effectively preventing the above read of `strong` from happening // after the write. - self.inner().weak.store(1, Release); // release the lock + this.inner().weak.store(1, Release); // release the lock unique } else { false @@ -2760,8 +2813,8 @@ impl<T, A: Allocator> Weak<T, A> { /// Helper type to allow accessing the reference counts without /// making any assertions about the data field. struct WeakInner<'a> { - weak: &'a atomic::AtomicUsize, - strong: &'a atomic::AtomicUsize, + weak: &'a Atomic<usize>, + strong: &'a Atomic<usize>, } impl<T: ?Sized> Weak<T> { diff --git a/library/alloc/src/vec/into_iter.rs b/library/alloc/src/vec/into_iter.rs index 3eee988b6c9..1af110691ba 100644 --- a/library/alloc/src/vec/into_iter.rs +++ b/library/alloc/src/vec/into_iter.rs @@ -168,7 +168,7 @@ impl<T, A: Allocator> IntoIter<T, A> { // SAFETY: This allocation originally came from a `Vec`, so it passes // all those checks. We have `this.buf` ≤ `this.ptr` ≤ `this.end`, - // so the `sub_ptr`s below cannot wrap, and will produce a well-formed + // so the `offset_from_unsigned`s below cannot wrap, and will produce a well-formed // range. `end` ≤ `buf + cap`, so the range will be in-bounds. // Taking `alloc` is ok because nothing else is going to look at it, // since our `Drop` impl isn't going to run so there's no more code. diff --git a/library/alloc/src/vec/splice.rs b/library/alloc/src/vec/splice.rs index ca5cb17f8bf..ed1a0dda76d 100644 --- a/library/alloc/src/vec/splice.rs +++ b/library/alloc/src/vec/splice.rs @@ -59,7 +59,7 @@ impl<I: Iterator, A: Allocator> Drop for Splice<'_, I, A> { // and moving things into the final place. // Which means we can replace the slice::Iter with pointers that won't point to deallocated // memory, so that Drain::drop is still allowed to call iter.len(), otherwise it would break - // the ptr.sub_ptr contract. + // the ptr.offset_from_unsigned contract. self.drain.iter = (&[]).iter(); unsafe { diff --git a/library/core/src/ffi/c_str.rs b/library/core/src/ffi/c_str.rs index 85e87445a1b..ac07c645c01 100644 --- a/library/core/src/ffi/c_str.rs +++ b/library/core/src/ffi/c_str.rs @@ -79,8 +79,9 @@ use crate::{fmt, ops, slice, str}; /// /// fn my_string_safe() -> String { /// let cstr = unsafe { CStr::from_ptr(my_string()) }; -/// // Get copy-on-write Cow<'_, str>, then guarantee a freshly-owned String allocation -/// String::from_utf8_lossy(cstr.to_bytes()).to_string() +/// // Get a copy-on-write Cow<'_, str>, then extract the +/// // allocated String (or allocate a fresh one if needed). +/// cstr.to_string_lossy().into_owned() /// } /// /// println!("string: {}", my_string_safe()); diff --git a/library/core/src/ffi/mod.rs b/library/core/src/ffi/mod.rs index 9bae5fd466a..c9c73a25d89 100644 --- a/library/core/src/ffi/mod.rs +++ b/library/core/src/ffi/mod.rs @@ -20,7 +20,7 @@ pub use self::c_str::FromBytesUntilNulError; pub use self::c_str::FromBytesWithNulError; use crate::fmt; -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; #[unstable( diff --git a/library/core/src/future/async_drop.rs b/library/core/src/future/async_drop.rs index f1778a4d782..fc4f95a98b4 100644 --- a/library/core/src/future/async_drop.rs +++ b/library/core/src/future/async_drop.rs @@ -1,284 +1,51 @@ #![unstable(feature = "async_drop", issue = "126482")] -use crate::fmt; -use crate::future::{Future, IntoFuture}; -use crate::intrinsics::discriminant_value; -use crate::marker::{DiscriminantKind, PhantomPinned}; -use crate::mem::MaybeUninit; -use crate::pin::Pin; -use crate::task::{Context, Poll, ready}; - -/// Asynchronously drops a value by running `AsyncDrop::async_drop` -/// on a value and its fields recursively. -#[unstable(feature = "async_drop", issue = "126482")] -pub fn async_drop<T>(value: T) -> AsyncDropOwning<T> { - AsyncDropOwning { value: MaybeUninit::new(value), dtor: None, _pinned: PhantomPinned } -} - -/// A future returned by the [`async_drop`]. -#[unstable(feature = "async_drop", issue = "126482")] -pub struct AsyncDropOwning<T> { - value: MaybeUninit<T>, - dtor: Option<AsyncDropInPlace<T>>, - _pinned: PhantomPinned, -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl<T> fmt::Debug for AsyncDropOwning<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AsyncDropOwning").finish_non_exhaustive() - } -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl<T> Future for AsyncDropOwning<T> { - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { - // SAFETY: Self is pinned thus it is ok to store references to self - unsafe { - let this = self.get_unchecked_mut(); - let dtor = Pin::new_unchecked( - this.dtor.get_or_insert_with(|| async_drop_in_place(this.value.as_mut_ptr())), - ); - // AsyncDestuctors are idempotent so Self gets idempotency as well - dtor.poll(cx) - } - } -} +#[allow(unused_imports)] +use core::future::Future; -#[lang = "async_drop_in_place"] -#[allow(unconditional_recursion)] -// FIXME: Consider if `#[rustc_diagnostic_item = "ptr_drop_in_place"]` is needed? -unsafe fn async_drop_in_place_raw<T: ?Sized>( - to_drop: *mut T, -) -> <T as AsyncDestruct>::AsyncDestructor { - // Code here does not matter - this is replaced by the - // real async drop glue constructor by the compiler. - - // SAFETY: see comment above - unsafe { async_drop_in_place_raw(to_drop) } -} +#[allow(unused_imports)] +use crate::pin::Pin; +#[allow(unused_imports)] +use crate::task::{Context, Poll}; -/// Creates the asynchronous destructor of the pointed-to value. -/// -/// # Safety -/// -/// Behavior is undefined if any of the following conditions are violated: -/// -/// * `to_drop` must be [valid](crate::ptr#safety) for both reads and writes. -/// -/// * `to_drop` must be properly aligned, even if `T` has size 0. +/// Async version of Drop trait. /// -/// * `to_drop` must be nonnull, even if `T` has size 0. +/// When a value is no longer needed, Rust will run a "destructor" on that value. +/// The most common way that a value is no longer needed is when it goes out of +/// scope. Destructors may still run in other circumstances, but we're going to +/// focus on scope for the examples here. To learn about some of those other cases, +/// please see [the reference] section on destructors. /// -/// * The value `to_drop` points to must be valid for async dropping, -/// which may mean it must uphold additional invariants. These -/// invariants depend on the type of the value being dropped. For -/// instance, when dropping a Box, the box's pointer to the heap must -/// be valid. +/// [the reference]: https://doc.rust-lang.org/reference/destructors.html /// -/// * While `async_drop_in_place` is executing or the returned async -/// destructor is alive, the only way to access parts of `to_drop` -/// is through the `self: Pin<&mut Self>` references supplied to -/// the `AsyncDrop::async_drop` methods that `async_drop_in_place` -/// or `AsyncDropInPlace<T>::poll` invokes. This usually means the -/// returned future stores the `to_drop` pointer and user is required -/// to guarantee that dropped value doesn't move. +/// ## `Copy` and ([`Drop`]|`AsyncDrop`) are exclusive /// -#[unstable(feature = "async_drop", issue = "126482")] -pub unsafe fn async_drop_in_place<T: ?Sized>(to_drop: *mut T) -> AsyncDropInPlace<T> { - // SAFETY: `async_drop_in_place_raw` has the same safety requirements - unsafe { AsyncDropInPlace(async_drop_in_place_raw(to_drop)) } -} - -/// A future returned by the [`async_drop_in_place`]. -#[unstable(feature = "async_drop", issue = "126482")] -pub struct AsyncDropInPlace<T: ?Sized>(<T as AsyncDestruct>::AsyncDestructor); - -#[unstable(feature = "async_drop", issue = "126482")] -impl<T: ?Sized> fmt::Debug for AsyncDropInPlace<T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_struct("AsyncDropInPlace").finish_non_exhaustive() - } -} - -#[unstable(feature = "async_drop", issue = "126482")] -impl<T: ?Sized> Future for AsyncDropInPlace<T> { - type Output = (); - - #[inline(always)] - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { - // SAFETY: This code simply forwards poll call to the inner future - unsafe { Pin::new_unchecked(&mut self.get_unchecked_mut().0) }.poll(cx) - } -} - -// FIXME(zetanumbers): Add same restrictions on AsyncDrop impls as -// with Drop impls -/// Custom code within the asynchronous destructor. +/// You cannot implement both [`Copy`] and ([`Drop`]|`AsyncDrop`) on the same type. Types that +/// are `Copy` get implicitly duplicated by the compiler, making it very +/// hard to predict when, and how often destructors will be executed. As such, +/// these types cannot have destructors. +#[cfg(not(bootstrap))] #[unstable(feature = "async_drop", issue = "126482")] #[lang = "async_drop"] pub trait AsyncDrop { - /// A future returned by the [`AsyncDrop::async_drop`] to be part - /// of the async destructor. - #[unstable(feature = "async_drop", issue = "126482")] - type Dropper<'a>: Future<Output = ()> - where - Self: 'a; - - /// Constructs the asynchronous destructor for this type. - #[unstable(feature = "async_drop", issue = "126482")] - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_>; -} - -#[lang = "async_destruct"] -#[rustc_deny_explicit_impl] -#[rustc_do_not_implement_via_object] -trait AsyncDestruct { - type AsyncDestructor: Future<Output = ()>; -} - -/// Basically calls `AsyncDrop::async_drop` with pointer. Used to simplify -/// generation of the code for `async_drop_in_place_raw` -#[lang = "surface_async_drop_in_place"] -async unsafe fn surface_async_drop_in_place<T: AsyncDrop + ?Sized>(ptr: *mut T) { - // SAFETY: We call this from async drop `async_drop_in_place_raw` - // which has the same safety requirements - unsafe { <T as AsyncDrop>::async_drop(Pin::new_unchecked(&mut *ptr)).await } -} - -/// Basically calls `Drop::drop` with pointer. Used to simplify generation -/// of the code for `async_drop_in_place_raw` -#[allow(drop_bounds)] -#[lang = "async_drop_surface_drop_in_place"] -async unsafe fn surface_drop_in_place<T: Drop + ?Sized>(ptr: *mut T) { - // SAFETY: We call this from async drop `async_drop_in_place_raw` - // which has the same safety requirements - unsafe { crate::ops::fallback_surface_drop(&mut *ptr) } -} - -/// Wraps a future to continue outputting `Poll::Ready(())` once after -/// wrapped future completes by returning `Poll::Ready(())` on poll. This -/// is useful for constructing async destructors to guarantee this -/// "fuse" property -// -// FIXME: Consider optimizing combinators to not have to use fuse in majority -// of cases, perhaps by adding `#[(rustc_)idempotent(_future)]` attribute for -// async functions and blocks with the unit return type. However current layout -// optimizations currently encode `None` case into the async block's discriminant. -struct Fuse<T> { - inner: Option<T>, -} - -#[lang = "async_drop_fuse"] -fn fuse<T>(inner: T) -> Fuse<T> { - Fuse { inner: Some(inner) } -} - -impl<T> Future for Fuse<T> -where - T: Future<Output = ()>, -{ - type Output = (); - - fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> { - // SAFETY: pin projection into `self.inner` - unsafe { - let this = self.get_unchecked_mut(); - if let Some(inner) = &mut this.inner { - ready!(Pin::new_unchecked(inner).poll(cx)); - this.inner = None; - } - } - Poll::Ready(()) - } -} - -/// Async destructor for arrays and slices. -#[lang = "async_drop_slice"] -async unsafe fn slice<T>(s: *mut [T]) { - let len = s.len(); - let ptr = s.as_mut_ptr(); - for i in 0..len { - // SAFETY: we iterate over elements of `s` slice - unsafe { async_drop_in_place_raw(ptr.add(i)).await } - } -} - -/// Constructs a chain of two futures, which awaits them sequentially as -/// a future. -#[lang = "async_drop_chain"] -async fn chain<F, G>(first: F, last: G) -where - F: IntoFuture<Output = ()>, - G: IntoFuture<Output = ()>, -{ - first.await; - last.await; -} - -/// Basically a lazy version of `async_drop_in_place`. Returns a future -/// that would call `AsyncDrop::async_drop` on a first poll. -/// -/// # Safety -/// -/// Same as `async_drop_in_place` except is lazy to avoid creating -/// multiple mutable references. -#[lang = "async_drop_defer"] -async unsafe fn defer<T: ?Sized>(to_drop: *mut T) { - // SAFETY: same safety requirements as `async_drop_in_place` - unsafe { async_drop_in_place(to_drop) }.await -} - -/// If `T`'s discriminant is equal to the stored one then awaits `M` -/// otherwise awaits the `O`. -/// -/// # Safety -/// -/// Users should carefully manage the returned future, since it would -/// try creating an immutable reference from `this` and get pointee's -/// discriminant. -// FIXME(zetanumbers): Send and Sync impls -#[lang = "async_drop_either"] -async unsafe fn either<O: IntoFuture<Output = ()>, M: IntoFuture<Output = ()>, T>( - other: O, - matched: M, - this: *mut T, - discr: <T as DiscriminantKind>::Discriminant, -) { - // SAFETY: Guaranteed by the safety section of this funtion's documentation - if unsafe { discriminant_value(&*this) } == discr { - drop(other); - matched.await - } else { - drop(matched); - other.await - } -} - -#[lang = "async_drop_deferred_drop_in_place"] -async unsafe fn deferred_drop_in_place<T>(to_drop: *mut T) { - // SAFETY: same safety requirements as with drop_in_place (implied by - // function's name) - unsafe { crate::ptr::drop_in_place(to_drop) } -} - -/// Used for noop async destructors. We don't use [`core::future::Ready`] -/// because it panics after its second poll, which could be potentially -/// bad if that would happen during the cleanup. -#[derive(Clone, Copy)] -struct Noop; - -#[lang = "async_drop_noop"] -fn noop() -> Noop { - Noop -} - -impl Future for Noop { - type Output = (); - - fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<Self::Output> { - Poll::Ready(()) - } + /// Executes the async destructor for this type. + /// + /// This method is called implicitly when the value goes out of scope, + /// and cannot be called explicitly. + /// + /// When this method has been called, `self` has not yet been deallocated. + /// That only happens after the method is over. + /// + /// # Panics + #[allow(async_fn_in_trait)] + async fn drop(self: Pin<&mut Self>); +} + +/// Async drop. +#[cfg(not(bootstrap))] +#[unstable(feature = "async_drop", issue = "126482")] +#[lang = "async_drop_in_place"] +pub async unsafe fn async_drop_in_place<T: ?Sized>(_to_drop: *mut T) { + // Code here does not matter - this is replaced by the + // real implementation by the compiler. } diff --git a/library/core/src/future/mod.rs b/library/core/src/future/mod.rs index 65c0171c88d..4b5a2f34d3f 100644 --- a/library/core/src/future/mod.rs +++ b/library/core/src/future/mod.rs @@ -20,8 +20,9 @@ mod pending; mod poll_fn; mod ready; +#[cfg(not(bootstrap))] #[unstable(feature = "async_drop", issue = "126482")] -pub use async_drop::{AsyncDrop, AsyncDropInPlace, async_drop, async_drop_in_place}; +pub use async_drop::{AsyncDrop, async_drop_in_place}; #[stable(feature = "into_future", since = "1.64.0")] pub use into_future::IntoFuture; #[stable(feature = "future_readiness_fns", since = "1.48.0")] diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index a7563f918a2..c5d5bc000f1 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -2996,7 +2996,7 @@ pub unsafe fn nontemporal_store<T>(ptr: *mut T, val: T); #[rustc_intrinsic] pub const unsafe fn ptr_offset_from<T>(ptr: *const T, base: *const T) -> isize; -/// See documentation of `<*const T>::sub_ptr` for details. +/// See documentation of `<*const T>::offset_from_unsigned` for details. #[rustc_nounwind] #[rustc_intrinsic] #[rustc_intrinsic_const_stable_indirect] diff --git a/library/core/src/iter/adapters/peekable.rs b/library/core/src/iter/adapters/peekable.rs index cc12cd9c356..a6522659620 100644 --- a/library/core/src/iter/adapters/peekable.rs +++ b/library/core/src/iter/adapters/peekable.rs @@ -271,7 +271,7 @@ impl<I: Iterator> Peekable<I> { /// assert_eq!(iter.next_if(|&x| x == 0), Some(0)); /// // The next item returned is now 1, so `next_if` will return `None`. /// assert_eq!(iter.next_if(|&x| x == 0), None); - /// // `next_if` saves the value of the next item if it was not equal to `expected`. + /// // `next_if` retains the next item if the predicate evaluates to `false` for it. /// assert_eq!(iter.next(), Some(1)); /// ``` /// @@ -304,9 +304,9 @@ impl<I: Iterator> Peekable<I> { /// let mut iter = (0..5).peekable(); /// // The first item of the iterator is 0; consume it. /// assert_eq!(iter.next_if_eq(&0), Some(0)); - /// // The next item returned is now 1, so `next_if` will return `None`. + /// // The next item returned is now 1, so `next_if_eq` will return `None`. /// assert_eq!(iter.next_if_eq(&0), None); - /// // `next_if_eq` saves the value of the next item if it was not equal to `expected`. + /// // `next_if_eq` retains the next item if it was not equal to `expected`. /// assert_eq!(iter.next(), Some(1)); /// ``` #[stable(feature = "peekable_next_if", since = "1.51.0")] diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 9625475e617..64a7ec8906b 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -118,7 +118,6 @@ #![feature(ptr_metadata)] #![feature(set_ptr_value)] #![feature(slice_as_array)] -#![feature(slice_as_chunks)] #![feature(slice_ptr_get)] #![feature(str_internals)] #![feature(str_split_inclusive_remainder)] diff --git a/library/core/src/ops/drop.rs b/library/core/src/ops/drop.rs index e024b7fb4d3..5d040804a8d 100644 --- a/library/core/src/ops/drop.rs +++ b/library/core/src/ops/drop.rs @@ -240,10 +240,3 @@ pub trait Drop { #[stable(feature = "rust1", since = "1.0.0")] fn drop(&mut self); } - -/// Fallback function to call surface level `Drop::drop` function -#[allow(drop_bounds)] -#[lang = "fallback_surface_drop"] -pub(crate) fn fallback_surface_drop<T: Drop + ?Sized>(x: &mut T) { - <T as Drop>::drop(x) -} diff --git a/library/core/src/ops/mod.rs b/library/core/src/ops/mod.rs index 627a875d9f7..1658f0e5a36 100644 --- a/library/core/src/ops/mod.rs +++ b/library/core/src/ops/mod.rs @@ -176,7 +176,6 @@ pub use self::deref::Receiver; pub use self::deref::{Deref, DerefMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::drop::Drop; -pub(crate) use self::drop::fallback_surface_drop; #[stable(feature = "rust1", since = "1.0.0")] pub use self::function::{Fn, FnMut, FnOnce}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 33ad59916e3..83a45436b30 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -155,30 +155,26 @@ pub const fn panic(expr: &'static str) -> ! { // reducing binary size impact. macro_rules! panic_const { ($($lang:ident = $message:expr,)+) => { - pub mod panic_const { - use super::*; - - $( - /// This is a panic called with a message that's a result of a MIR-produced Assert. - // - // never inline unless panic_immediate_abort to avoid code - // bloat at the call sites as much as possible - #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] - #[cfg_attr(feature = "panic_immediate_abort", inline)] - #[track_caller] - #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable - #[lang = stringify!($lang)] - pub const fn $lang() -> ! { - // Use Arguments::new_const instead of format_args!("{expr}") to potentially - // reduce size overhead. The format_args! macro uses str's Display trait to - // write expr, which calls Formatter::pad, which must accommodate string - // truncation and padding (even though none is used here). Using - // Arguments::new_const may allow the compiler to omit Formatter::pad from the - // output binary, saving up to a few kilobytes. - panic_fmt(fmt::Arguments::new_const(&[$message])); - } - )+ - } + $( + /// This is a panic called with a message that's a result of a MIR-produced Assert. + // + // never inline unless panic_immediate_abort to avoid code + // bloat at the call sites as much as possible + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never), cold)] + #[cfg_attr(feature = "panic_immediate_abort", inline)] + #[track_caller] + #[rustc_const_stable_indirect] // must follow stable const rules since it is exposed to stable + #[lang = stringify!($lang)] + pub const fn $lang() -> ! { + // Use Arguments::new_const instead of format_args!("{expr}") to potentially + // reduce size overhead. The format_args! macro uses str's Display trait to + // write expr, which calls Formatter::pad, which must accommodate string + // truncation and padding (even though none is used here). Using + // Arguments::new_const may allow the compiler to omit Formatter::pad from the + // output binary, saving up to a few kilobytes. + panic_fmt(fmt::Arguments::new_const(&[$message])); + } + )+ } } @@ -186,25 +182,37 @@ macro_rules! panic_const { // slightly different forms. It's not clear if there's a good way to deduplicate without adding // special cases to the compiler (e.g., a const generic function wouldn't have a single definition // shared across crates, which is exactly what we want here). -panic_const! { - panic_const_add_overflow = "attempt to add with overflow", - panic_const_sub_overflow = "attempt to subtract with overflow", - panic_const_mul_overflow = "attempt to multiply with overflow", - panic_const_div_overflow = "attempt to divide with overflow", - panic_const_rem_overflow = "attempt to calculate the remainder with overflow", - panic_const_neg_overflow = "attempt to negate with overflow", - panic_const_shr_overflow = "attempt to shift right with overflow", - panic_const_shl_overflow = "attempt to shift left with overflow", - panic_const_div_by_zero = "attempt to divide by zero", - panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", - panic_const_coroutine_resumed = "coroutine resumed after completion", - panic_const_async_fn_resumed = "`async fn` resumed after completion", - panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", - panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", - panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", - panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", - panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", - panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", +pub mod panic_const { + use super::*; + panic_const! { + panic_const_add_overflow = "attempt to add with overflow", + panic_const_sub_overflow = "attempt to subtract with overflow", + panic_const_mul_overflow = "attempt to multiply with overflow", + panic_const_div_overflow = "attempt to divide with overflow", + panic_const_rem_overflow = "attempt to calculate the remainder with overflow", + panic_const_neg_overflow = "attempt to negate with overflow", + panic_const_shr_overflow = "attempt to shift right with overflow", + panic_const_shl_overflow = "attempt to shift left with overflow", + panic_const_div_by_zero = "attempt to divide by zero", + panic_const_rem_by_zero = "attempt to calculate the remainder with a divisor of zero", + panic_const_coroutine_resumed = "coroutine resumed after completion", + panic_const_async_fn_resumed = "`async fn` resumed after completion", + panic_const_async_gen_fn_resumed = "`async gen fn` resumed after completion", + panic_const_gen_fn_none = "`gen fn` should just keep returning `None` after completion", + panic_const_coroutine_resumed_panic = "coroutine resumed after panicking", + panic_const_async_fn_resumed_panic = "`async fn` resumed after panicking", + panic_const_async_gen_fn_resumed_panic = "`async gen fn` resumed after panicking", + panic_const_gen_fn_none_panic = "`gen fn` should just keep returning `None` after panicking", + } + // Separated panic constants list for async drop feature + // (May be joined when the corresponding lang items will be in the bootstrap) + #[cfg(not(bootstrap))] + panic_const! { + panic_const_coroutine_resumed_drop = "coroutine resumed after async drop", + panic_const_async_fn_resumed_drop = "`async fn` resumed after async drop", + panic_const_async_gen_fn_resumed_drop = "`async gen fn` resumed after async drop", + panic_const_gen_fn_none_drop = "`gen fn` resumed after async drop", + } } /// Like `panic`, but without unwinding and track_caller to reduce the impact on codesize on the caller. diff --git a/library/core/src/pin.rs b/library/core/src/pin.rs index 9e6acf04bf7..dd1c2f2c285 100644 --- a/library/core/src/pin.rs +++ b/library/core/src/pin.rs @@ -676,7 +676,7 @@ //! let data_ptr = unpinned_src.data.as_ptr() as *const u8; //! let slice_ptr = unpinned_src.slice.as_ptr() as *const u8; //! let offset = slice_ptr.offset_from(data_ptr) as usize; -//! let len = (*unpinned_src.slice.as_ptr()).len(); +//! let len = unpinned_src.slice.as_ptr().len(); //! //! unpinned_self.slice = NonNull::from(&mut unpinned_self.data[offset..offset+len]); //! } diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 2d869958b85..bcf34781c8a 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -804,8 +804,8 @@ impl<T: ?Sized> *const T { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][pointer::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][pointer::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. @@ -814,7 +814,7 @@ impl<T: ?Sized> *const T { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from_unsigned<U: ?Sized>(self, origin: *const U) -> usize { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { self.cast::<u8>().offset_from_unsigned(origin.cast::<u8>()) } } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index df49eedb350..6d1a50d8436 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -937,6 +937,7 @@ impl<T: ?Sized> *mut T { /// /// // This would be incorrect, as the pointers are not correctly ordered: /// // ptr1.offset_from(ptr2) + /// ``` #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] #[inline] @@ -945,7 +946,7 @@ impl<T: ?Sized> *mut T { where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { (self as *const T).offset_from_unsigned(origin) } } @@ -954,8 +955,8 @@ impl<T: ?Sized> *mut T { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][pointer::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][pointer::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. @@ -964,7 +965,7 @@ impl<T: ?Sized> *mut T { #[inline] #[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces pub const unsafe fn byte_offset_from_unsigned<U: ?Sized>(self, origin: *mut U) -> usize { - // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `byte_offset_from_unsigned`. unsafe { (self as *const T).byte_offset_from_unsigned(origin) } } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 68b8f0c3e4e..0864cc457b6 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -906,7 +906,7 @@ impl<T: ?Sized> NonNull<T> { where T: Sized, { - // SAFETY: the caller must uphold the safety contract for `sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `offset_from_unsigned`. unsafe { self.as_ptr().offset_from_unsigned(subtracted.as_ptr()) } } @@ -915,8 +915,8 @@ impl<T: ?Sized> NonNull<T> { /// units of **bytes**. /// /// This is purely a convenience for casting to a `u8` pointer and - /// using [`sub_ptr`][NonNull::offset_from_unsigned] on it. See that method for - /// documentation and safety requirements. + /// using [`offset_from_unsigned`][NonNull::offset_from_unsigned] on it. + /// See that method for documentation and safety requirements. /// /// For non-`Sized` pointees this operation considers only the data pointers, /// ignoring the metadata. @@ -925,7 +925,7 @@ impl<T: ?Sized> NonNull<T> { #[stable(feature = "ptr_sub_ptr", since = "1.87.0")] #[rustc_const_stable(feature = "const_ptr_sub_ptr", since = "1.87.0")] pub const unsafe fn byte_offset_from_unsigned<U: ?Sized>(self, origin: NonNull<U>) -> usize { - // SAFETY: the caller must uphold the safety contract for `byte_sub_ptr`. + // SAFETY: the caller must uphold the safety contract for `byte_offset_from_unsigned`. unsafe { self.as_ptr().byte_offset_from_unsigned(origin.as_ptr()) } } diff --git a/library/core/src/slice/iter.rs b/library/core/src/slice/iter.rs index f507ee563ac..85a5e89a49e 100644 --- a/library/core/src/slice/iter.rs +++ b/library/core/src/slice/iter.rs @@ -2335,7 +2335,6 @@ pub struct ArrayChunks<'a, T: 'a, const N: usize> { impl<'a, T, const N: usize> ArrayChunks<'a, T, N> { #[rustc_const_unstable(feature = "const_slice_make_iter", issue = "137737")] - // #[rustc_const_unstable(feature = "slice_as_chunks", issue = "74985")] #[inline] pub(super) const fn new(slice: &'a [T]) -> Self { let (array_slice, rem) = slice.as_chunks(); diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index fbfa4d87415..81fe0166fd7 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -1268,6 +1268,18 @@ impl<T> [T] { /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// + /// This is the inverse operation to [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// + /// As this is `unsafe`, consider whether you could use [`as_chunks`] or + /// [`as_rchunks`] instead, perhaps via something like + /// `if let (chunks, []) = slice.as_chunks()` or + /// `let (chunks, []) = slice.as_chunks() else { unreachable!() };`. + /// + /// [`as_chunks`]: slice::as_chunks + /// [`as_rchunks`]: slice::as_rchunks + /// /// # Safety /// /// This may only be called when @@ -1277,7 +1289,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice: &[char] = &['l', 'o', 'r', 'e', 'm', '!']; /// let chunks: &[[char; 1]] = /// // SAFETY: 1-element chunks never have remainder @@ -1292,7 +1303,8 @@ impl<T> [T] { /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked() // Zero-length chunks are never allowed /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked<const N: usize>(&self) -> &[[T; N]] { @@ -1312,15 +1324,27 @@ impl<T> [T] { /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (chunks, remainder) = slice.as_chunks()`, then: + /// - `chunks.len()` equals `slice.len() / N`, + /// - `remainder.len()` equals `slice.len() % N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let (chunks, remainder) = slice.as_chunks(); /// assert_eq!(chunks, &[['l', 'o'], ['r', 'e']]); @@ -1330,14 +1354,14 @@ impl<T> [T] { /// If you expect the slice to be an exact multiple, you can combine /// `let`-`else` with an empty slice pattern: /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['R', 'u', 's', 't']; /// let (chunks, []) = slice.as_chunks::<2>() else { /// panic!("slice didn't have even length") /// }; /// assert_eq!(chunks, &[['R', 'u'], ['s', 't']]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[track_caller] #[must_use] @@ -1357,21 +1381,34 @@ impl<T> [T] { /// starting at the end of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (remainder, chunks) = slice.as_rchunks()`, then: + /// - `remainder.len()` equals `slice.len() % N`, + /// - `chunks.len()` equals `slice.len() / N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened`]. + /// + /// [`as_flattened`]: slice::as_flattened + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice = ['l', 'o', 'r', 'e', 'm']; /// let (remainder, chunks) = slice.as_rchunks(); /// assert_eq!(remainder, &['l']); /// assert_eq!(chunks, &[['o', 'r'], ['e', 'm']]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[track_caller] #[must_use] @@ -1424,6 +1461,18 @@ impl<T> [T] { /// Splits the slice into a slice of `N`-element arrays, /// assuming that there's no remainder. /// + /// This is the inverse operation to [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// + /// As this is `unsafe`, consider whether you could use [`as_chunks_mut`] or + /// [`as_rchunks_mut`] instead, perhaps via something like + /// `if let (chunks, []) = slice.as_chunks_mut()` or + /// `let (chunks, []) = slice.as_chunks_mut() else { unreachable!() };`. + /// + /// [`as_chunks_mut`]: slice::as_chunks_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut + /// /// # Safety /// /// This may only be called when @@ -1433,7 +1482,6 @@ impl<T> [T] { /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let slice: &mut [char] = &mut ['l', 'o', 'r', 'e', 'm', '!']; /// let chunks: &mut [[char; 1]] = /// // SAFETY: 1-element chunks never have remainder @@ -1450,7 +1498,8 @@ impl<T> [T] { /// // let chunks: &[[_; 5]] = slice.as_chunks_unchecked_mut() // The slice length is not a multiple of 5 /// // let chunks: &[[_; 0]] = slice.as_chunks_unchecked_mut() // Zero-length chunks are never allowed /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[must_use] pub const unsafe fn as_chunks_unchecked_mut<const N: usize>(&mut self) -> &mut [[T; N]] { @@ -1470,15 +1519,27 @@ impl<T> [T] { /// starting at the beginning of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (chunks, remainder) = slice.as_chunks_mut()`, then: + /// - `chunks.len()` equals `slice.len() / N`, + /// - `remainder.len()` equals `slice.len() % N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// @@ -1490,7 +1551,8 @@ impl<T> [T] { /// } /// assert_eq!(v, &[1, 1, 2, 2, 9]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[track_caller] #[must_use] @@ -1510,15 +1572,27 @@ impl<T> [T] { /// starting at the end of the slice, /// and a remainder slice with length strictly less than `N`. /// + /// The remainder is meaningful in the division sense. Given + /// `let (remainder, chunks) = slice.as_rchunks_mut()`, then: + /// - `remainder.len()` equals `slice.len() % N`, + /// - `chunks.len()` equals `slice.len() / N`, and + /// - `slice.len()` equals `chunks.len() * N + remainder.len()`. + /// + /// You can flatten the chunks back into a slice-of-`T` with [`as_flattened_mut`]. + /// + /// [`as_flattened_mut`]: slice::as_flattened_mut + /// /// # Panics /// - /// Panics if `N` is zero. This check will most probably get changed to a compile time - /// error before this method gets stabilized. + /// Panics if `N` is zero. + /// + /// Note that this check is against a const generic parameter, not a runtime + /// value, and thus a particular monomorphization will either always panic + /// or it will never panic. /// /// # Examples /// /// ``` - /// #![feature(slice_as_chunks)] /// let v = &mut [0, 0, 0, 0, 0]; /// let mut count = 1; /// @@ -1530,7 +1604,8 @@ impl<T> [T] { /// } /// assert_eq!(v, &[9, 1, 1, 2, 2]); /// ``` - #[unstable(feature = "slice_as_chunks", issue = "74985")] + #[stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] + #[rustc_const_stable(feature = "slice_as_chunks", since = "CURRENT_RUSTC_VERSION")] #[inline] #[track_caller] #[must_use] @@ -4816,6 +4891,11 @@ impl<T> [MaybeUninit<T>] { impl<T, const N: usize> [[T; N]] { /// Takes a `&[[T; N]]`, and flattens it to a `&[T]`. /// + /// For the opposite operation, see [`as_chunks`] and [`as_rchunks`]. + /// + /// [`as_chunks`]: slice::as_chunks + /// [`as_rchunks`]: slice::as_rchunks + /// /// # Panics /// /// This panics if the length of the resulting slice would overflow a `usize`. @@ -4856,6 +4936,11 @@ impl<T, const N: usize> [[T; N]] { /// Takes a `&mut [[T; N]]`, and flattens it to a `&mut [T]`. /// + /// For the opposite operation, see [`as_chunks_mut`] and [`as_rchunks_mut`]. + /// + /// [`as_chunks_mut`]: slice::as_chunks_mut + /// [`as_rchunks_mut`]: slice::as_rchunks_mut + /// /// # Panics /// /// This panics if the length of the resulting slice would overflow a `usize`. diff --git a/library/core/src/str/converts.rs b/library/core/src/str/converts.rs index 37854a4da64..058628797ea 100644 --- a/library/core/src/str/converts.rs +++ b/library/core/src/str/converts.rs @@ -178,7 +178,7 @@ pub const unsafe fn from_utf8_unchecked(v: &[u8]) -> &str { /// Converts a slice of bytes to a string slice without checking /// that the string contains valid UTF-8; mutable version. /// -/// See the immutable version, [`from_utf8_unchecked()`] for more information. +/// See the immutable version, [`from_utf8_unchecked()`] for documentation and safety requirements. /// /// # Examples /// diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 2099327d2f1..dafabba645c 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -308,7 +308,7 @@ impl str { /// Converts a slice of bytes to a string slice without checking /// that the string contains valid UTF-8; mutable version. /// - /// See the immutable version, [`from_utf8_unchecked()`] for more information. + /// See the immutable version, [`from_utf8_unchecked()`] for documentation and safety requirements. /// /// # Examples /// @@ -2117,7 +2117,7 @@ impl str { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_diagnostic_item = "str_trim"] pub fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) + self.trim_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. @@ -2156,7 +2156,7 @@ impl str { #[stable(feature = "trim_direction", since = "1.30.0")] #[rustc_diagnostic_item = "str_trim_start"] pub fn trim_start(&self) -> &str { - self.trim_start_matches(|c: char| c.is_whitespace()) + self.trim_start_matches(char::is_whitespace) } /// Returns a string slice with trailing whitespace removed. @@ -2195,7 +2195,7 @@ impl str { #[stable(feature = "trim_direction", since = "1.30.0")] #[rustc_diagnostic_item = "str_trim_end"] pub fn trim_end(&self) -> &str { - self.trim_end_matches(|c: char| c.is_whitespace()) + self.trim_end_matches(char::is_whitespace) } /// Returns a string slice with leading whitespace removed. diff --git a/library/core/src/sync/atomic.rs b/library/core/src/sync/atomic.rs index 9b1b13e7129..84c7f1aafe1 100644 --- a/library/core/src/sync/atomic.rs +++ b/library/core/src/sync/atomic.rs @@ -247,6 +247,100 @@ use crate::cell::UnsafeCell; use crate::hint::spin_loop; use crate::{fmt, intrinsics}; +trait Sealed {} + +/// A marker trait for primitive types which can be modified atomically. +/// +/// This is an implementation detail for <code>[Atomic]\<T></code> which may disappear or be replaced at any time. +/// +/// # Safety +/// +/// Types implementing this trait must be primitives that can be modified atomically. +/// +/// The associated `Self::AtomicInner` type must have the same size and bit validity as `Self`, +/// but may have a higher alignment requirement, so the following `transmute`s are sound: +/// +/// - `&mut Self::AtomicInner` as `&mut Self` +/// - `Self` as `Self::AtomicInner` or the reverse +#[unstable( + feature = "atomic_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" +)] +#[expect(private_bounds)] +pub unsafe trait AtomicPrimitive: Sized + Copy + Sealed { + /// Temporary implementation detail. + type AtomicInner: Sized; +} + +macro impl_atomic_primitive( + $Atom:ident $(<$T:ident>)? ($Primitive:ty), + size($size:literal), + align($align:literal) $(,)? +) { + impl $(<$T>)? Sealed for $Primitive {} + + #[unstable( + feature = "atomic_internals", + reason = "implementation detail which may disappear or be replaced at any time", + issue = "none" + )] + #[cfg(target_has_atomic_load_store = $size)] + unsafe impl $(<$T>)? AtomicPrimitive for $Primitive { + type AtomicInner = $Atom $(<$T>)?; + } +} + +impl_atomic_primitive!(AtomicBool(bool), size("8"), align(1)); +impl_atomic_primitive!(AtomicI8(i8), size("8"), align(1)); +impl_atomic_primitive!(AtomicU8(u8), size("8"), align(1)); +impl_atomic_primitive!(AtomicI16(i16), size("16"), align(2)); +impl_atomic_primitive!(AtomicU16(u16), size("16"), align(2)); +impl_atomic_primitive!(AtomicI32(i32), size("32"), align(4)); +impl_atomic_primitive!(AtomicU32(u32), size("32"), align(4)); +impl_atomic_primitive!(AtomicI64(i64), size("64"), align(8)); +impl_atomic_primitive!(AtomicU64(u64), size("64"), align(8)); +impl_atomic_primitive!(AtomicI128(i128), size("128"), align(16)); +impl_atomic_primitive!(AtomicU128(u128), size("128"), align(16)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicIsize(isize), size("ptr"), align(8)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicUsize(usize), size("ptr"), align(8)); + +#[cfg(target_pointer_width = "16")] +impl_atomic_primitive!(AtomicPtr<T>(*mut T), size("ptr"), align(2)); +#[cfg(target_pointer_width = "32")] +impl_atomic_primitive!(AtomicPtr<T>(*mut T), size("ptr"), align(4)); +#[cfg(target_pointer_width = "64")] +impl_atomic_primitive!(AtomicPtr<T>(*mut T), size("ptr"), align(8)); + +/// A memory location which can be safely modified from multiple threads. +/// +/// This has the same size and bit validity as the underlying type `T`. However, +/// the alignment of this type is always equal to its size, even on targets where +/// `T` has alignment less than its size. +/// +/// For more about the differences between atomic types and non-atomic types as +/// well as information about the portability of this type, please see the +/// [module-level documentation]. +/// +/// **Note:** This type is only available on platforms that support atomic loads +/// and stores of `T`. +/// +/// [module-level documentation]: crate::sync::atomic +#[unstable(feature = "generic_atomic", issue = "130539")] +pub type Atomic<T> = <T as AtomicPrimitive>::AtomicInner; + // Some architectures don't have byte-sized atomics, which results in LLVM // emulating them using a LL/SC loop. However for AtomicBool we can take // advantage of the fact that it only ever contains 0 or 1 and use atomic OR/AND diff --git a/library/proc_macro/src/lib.rs b/library/proc_macro/src/lib.rs index c46dcebedca..36a1c57b020 100644 --- a/library/proc_macro/src/lib.rs +++ b/library/proc_macro/src/lib.rs @@ -513,13 +513,13 @@ impl Span { } /// Creates an empty span pointing to directly before this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn start(&self) -> Span { Span(self.0.start()) } /// Creates an empty span pointing to directly after this span. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn end(&self) -> Span { Span(self.0.end()) } @@ -527,7 +527,7 @@ impl Span { /// The one-indexed line of the source file where the span starts. /// /// To obtain the line of the span's end, use `span.end().line()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn line(&self) -> usize { self.0.line() } @@ -535,7 +535,7 @@ impl Span { /// The one-indexed column of the source file where the span starts. /// /// To obtain the column of the span's end, use `span.end().column()`. - #[unstable(feature = "proc_macro_span", issue = "54725")] + #[stable(feature = "proc_macro_span_location", since = "CURRENT_RUSTC_VERSION")] pub fn column(&self) -> usize { self.0.column() } diff --git a/library/std/Cargo.toml b/library/std/Cargo.toml index 7b8f2dd46d6..940b671c514 100644 --- a/library/std/Cargo.toml +++ b/library/std/Cargo.toml @@ -83,8 +83,8 @@ wasi = { version = "0.11.0", features = [ ], default-features = false } [target.'cfg(target_os = "uefi")'.dependencies] -r-efi = { version = "4.5.0", features = ['rustc-dep-of-std'] } -r-efi-alloc = { version = "1.0.0", features = ['rustc-dep-of-std'] } +r-efi = { version = "5.2.0", features = ['rustc-dep-of-std'] } +r-efi-alloc = { version = "2.0.0", features = ['rustc-dep-of-std'] } [features] backtrace = [ diff --git a/library/std/src/alloc.rs b/library/std/src/alloc.rs index 75971ac90e7..b574e9f3a25 100644 --- a/library/std/src/alloc.rs +++ b/library/std/src/alloc.rs @@ -57,7 +57,7 @@ #![stable(feature = "alloc_module", since = "1.28.0")] use core::ptr::NonNull; -use core::sync::atomic::{AtomicPtr, Ordering}; +use core::sync::atomic::{Atomic, AtomicPtr, Ordering}; use core::{hint, mem, ptr}; #[stable(feature = "alloc_module", since = "1.28.0")] @@ -287,7 +287,7 @@ unsafe impl Allocator for System { } } -static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); +static HOOK: Atomic<*mut ()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom allocation error hook, replacing any that was previously registered. /// diff --git a/library/std/src/backtrace.rs b/library/std/src/backtrace.rs index 3683485640c..c3fcb0e2e42 100644 --- a/library/std/src/backtrace.rs +++ b/library/std/src/backtrace.rs @@ -92,8 +92,8 @@ use crate::backtrace_rs::{self, BytesOrWideString}; use crate::ffi::c_void; use crate::panic::UnwindSafe; use crate::sync::LazyLock; -use crate::sync::atomic::AtomicU8; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicU8}; use crate::sys::backtrace::{lock, output_filename, set_image_base}; use crate::{env, fmt}; @@ -254,7 +254,7 @@ impl Backtrace { // Cache the result of reading the environment variables to make // backtrace captures speedy, because otherwise reading environment // variables every time can be somewhat slow. - static ENABLED: AtomicU8 = AtomicU8::new(0); + static ENABLED: Atomic<u8> = AtomicU8::new(0); match ENABLED.load(Relaxed) { 0 => {} 1 => return false, diff --git a/library/std/src/ffi/mod.rs b/library/std/src/ffi/mod.rs index d34e3ca00b9..bd9446f5aba 100644 --- a/library/std/src/ffi/mod.rs +++ b/library/std/src/ffi/mod.rs @@ -161,7 +161,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -#[unstable(feature = "c_str_module", issue = "112134")] +#[stable(feature = "c_str_module", since = "CURRENT_RUSTC_VERSION")] pub mod c_str; #[stable(feature = "core_c_void", since = "1.30.0")] diff --git a/library/std/src/fs.rs b/library/std/src/fs.rs index 801baf3d990..3340a5dc23d 100644 --- a/library/std/src/fs.rs +++ b/library/std/src/fs.rs @@ -2980,6 +2980,21 @@ pub fn read_dir<P: AsRef<Path>>(path: P) -> io::Result<ReadDir> { /// /// [changes]: io#platform-specific-behavior /// +/// ## Symlinks +/// On UNIX-like systems, this function will update the permission bits +/// of the file pointed to by the symlink. +/// +/// Note that this behavior can lead to privalage escalation vulnerabilites, +/// where the ability to create a symlink in one directory allows you to +/// cause the permissions of another file or directory to be modified. +/// +/// For this reason, using this function with symlinks should be avoided. +/// When possible, permissions should be set at creation time instead. +/// +/// # Rationale +/// POSIX does not specify an `lchown` function, +/// and symlinks can be followed regardless of what permission bits are set. +/// /// # Errors /// /// This function will return an error in the following situations, but is not diff --git a/library/std/src/io/mod.rs b/library/std/src/io/mod.rs index 5242261cf93..96fac4f6bde 100644 --- a/library/std/src/io/mod.rs +++ b/library/std/src/io/mod.rs @@ -2319,9 +2319,9 @@ pub trait BufRead: Read { /// Checks if there is any data left to be `read`. /// /// This function may fill the buffer to check for data, - /// so this functions returns `Result<bool>`, not `bool`. + /// so this function returns `Result<bool>`, not `bool`. /// - /// Default implementation calls `fill_buf` and checks that + /// The default implementation calls `fill_buf` and checks that the /// returned slice is empty (which means that there is no data left, /// since EOF is reached). /// diff --git a/library/std/src/io/stdio.rs b/library/std/src/io/stdio.rs index 8fc16331339..2d80fe49e80 100644 --- a/library/std/src/io/stdio.rs +++ b/library/std/src/io/stdio.rs @@ -11,7 +11,7 @@ use crate::io::{ self, BorrowedCursor, BufReader, IoSlice, IoSliceMut, LineWriter, Lines, SpecReadByte, }; use crate::panic::{RefUnwindSafe, UnwindSafe}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sync::{Arc, Mutex, MutexGuard, OnceLock, ReentrantLock, ReentrantLockGuard}; use crate::sys::stdio; use crate::thread::AccessError; @@ -37,7 +37,7 @@ thread_local! { /// have a consistent order between set_output_capture and print_to *within /// the same thread*. Within the same thread, things always have a perfectly /// consistent order. So Ordering::Relaxed is fine. -static OUTPUT_CAPTURE_USED: AtomicBool = AtomicBool::new(false); +static OUTPUT_CAPTURE_USED: Atomic<bool> = AtomicBool::new(false); /// A handle to a raw instance of the standard input stream of this process. /// diff --git a/library/std/src/keyword_docs.rs b/library/std/src/keyword_docs.rs index 91701576130..c9595b051e2 100644 --- a/library/std/src/keyword_docs.rs +++ b/library/std/src/keyword_docs.rs @@ -388,11 +388,15 @@ mod enum_keyword {} /// lazy_static;`. The other use is in foreign function interfaces (FFI). /// /// `extern` is used in two different contexts within FFI. The first is in the form of external -/// blocks, for declaring function interfaces that Rust code can call foreign code by. +/// blocks, for declaring function interfaces that Rust code can call foreign code by. This use +/// of `extern` is unsafe, since we are asserting to the compiler that all function declarations +/// are correct. If they are not, using these items may lead to undefined behavior. /// /// ```rust ignore +/// // SAFETY: The function declarations given below are in +/// // line with the header files of `my_c_library`. /// #[link(name = "my_c_library")] -/// extern "C" { +/// unsafe extern "C" { /// fn my_c_function(x: i32) -> bool; /// } /// ``` diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f77bf92a806..ba57ad9bae3 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -329,7 +329,6 @@ #![feature(array_chunks)] #![feature(bstr)] #![feature(bstr_internals)] -#![feature(c_str_module)] #![feature(char_internals)] #![feature(clone_to_uninit)] #![feature(core_intrinsics)] @@ -344,6 +343,7 @@ #![feature(float_gamma)] #![feature(float_minimum_maximum)] #![feature(fmt_internals)] +#![feature(generic_atomic)] #![feature(hasher_prefixfree_extras)] #![feature(hashmap_internals)] #![feature(hint_must_use)] @@ -704,8 +704,14 @@ pub use core::cfg_match; reason = "`concat_bytes` is not stable enough for use and is subject to change" )] pub use core::concat_bytes; +#[stable(feature = "matches_macro", since = "1.42.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::matches; #[stable(feature = "core_primitive", since = "1.43.0")] pub use core::primitive; +#[stable(feature = "todo_macro", since = "1.40.0")] +#[allow(deprecated, deprecated_in_future)] +pub use core::todo; // Re-export built-in macros defined through core. #[stable(feature = "builtin_macro_prelude", since = "1.38.0")] #[allow(deprecated)] @@ -719,8 +725,8 @@ pub use core::{ #[stable(feature = "rust1", since = "1.0.0")] #[allow(deprecated, deprecated_in_future)] pub use core::{ - assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, matches, todo, r#try, - unimplemented, unreachable, write, writeln, + assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, r#try, unimplemented, + unreachable, write, writeln, }; // Include a number of private modules that exist solely to provide diff --git a/library/std/src/os/uefi/env.rs b/library/std/src/os/uefi/env.rs index cf8ae697e38..ab5406e605c 100644 --- a/library/std/src/os/uefi/env.rs +++ b/library/std/src/os/uefi/env.rs @@ -4,13 +4,13 @@ use crate::ffi::c_void; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicBool, AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, Ordering}; -static SYSTEM_TABLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut()); -static IMAGE_HANDLE: AtomicPtr<c_void> = AtomicPtr::new(crate::ptr::null_mut()); +static SYSTEM_TABLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); +static IMAGE_HANDLE: Atomic<*mut c_void> = AtomicPtr::new(crate::ptr::null_mut()); // Flag to check if BootServices are still valid. // Start with assuming that they are not available -static BOOT_SERVICES_FLAG: AtomicBool = AtomicBool::new(false); +static BOOT_SERVICES_FLAG: Atomic<bool> = AtomicBool::new(false); /// Initializes the global System Table and Image Handle pointers. /// diff --git a/library/std/src/os/xous/services.rs b/library/std/src/os/xous/services.rs index 93916750c05..0681485ea06 100644 --- a/library/std/src/os/xous/services.rs +++ b/library/std/src/os/xous/services.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -106,7 +106,7 @@ pub fn try_connect(name: &str) -> Option<Connection> { ns::try_connect_with_name(name) } -static NAME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); +static NAME_SERVER_CONNECTION: Atomic<u32> = AtomicU32::new(0); /// Returns a `Connection` to the name server. If the name server has not been started, /// then this call will block until the name server has been started. The `Connection` diff --git a/library/std/src/os/xous/services/dns.rs b/library/std/src/os/xous/services/dns.rs index 02881648393..7641d1f15e4 100644 --- a/library/std/src/os/xous/services/dns.rs +++ b/library/std/src/os/xous/services/dns.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; @@ -17,7 +17,7 @@ impl Into<usize> for DnsLendMut { /// Returns a `Connection` to the DNS lookup server. This server is used for /// querying domain name values. pub(crate) fn dns_server() -> Connection { - static DNS_CONNECTION: AtomicU32 = AtomicU32::new(0); + static DNS_CONNECTION: Atomic<u32> = AtomicU32::new(0); let cid = DNS_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/log.rs b/library/std/src/os/xous/services/log.rs index 095d4f4a3e7..e7717c8515d 100644 --- a/library/std/src/os/xous/services/log.rs +++ b/library/std/src/os/xous/services/log.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -64,7 +64,7 @@ impl Into<usize> for LogLend { /// running. It is safe to call this multiple times, because the address is /// shared among all threads in a process. pub(crate) fn log_server() -> Connection { - static LOG_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static LOG_SERVER_CONNECTION: Atomic<u32> = AtomicU32::new(0); let cid = LOG_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { diff --git a/library/std/src/os/xous/services/net.rs b/library/std/src/os/xous/services/net.rs index 83acc7961b3..c20bf1a7ad5 100644 --- a/library/std/src/os/xous/services/net.rs +++ b/library/std/src/os/xous/services/net.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; use crate::os::xous::services::connect; @@ -84,7 +84,7 @@ impl<'a> Into<[usize; 5]> for NetBlockingScalar { /// Returns a `Connection` to the Network server. This server provides all /// OS-level networking functions. pub(crate) fn net_server() -> Connection { - static NET_CONNECTION: AtomicU32 = AtomicU32::new(0); + static NET_CONNECTION: Atomic<u32> = AtomicU32::new(0); let cid = NET_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/systime.rs b/library/std/src/os/xous/services/systime.rs index de87694b4cd..e54cffdc4c0 100644 --- a/library/std/src/os/xous/services/systime.rs +++ b/library/std/src/os/xous/services/systime.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::{Connection, connect}; @@ -17,7 +17,7 @@ impl Into<[usize; 5]> for SystimeScalar { /// Returns a `Connection` to the systime server. This server is used for reporting the /// realtime clock. pub(crate) fn systime_server() -> Connection { - static SYSTIME_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static SYSTIME_SERVER_CONNECTION: Atomic<u32> = AtomicU32::new(0); let cid = SYSTIME_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/os/xous/services/ticktimer.rs b/library/std/src/os/xous/services/ticktimer.rs index 66ade6da65c..bf51ecde8e5 100644 --- a/library/std/src/os/xous/services/ticktimer.rs +++ b/library/std/src/os/xous/services/ticktimer.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use crate::os::xous::ffi::Connection; @@ -31,7 +31,7 @@ impl Into<[usize; 5]> for TicktimerScalar { /// Returns a `Connection` to the ticktimer server. This server is used for synchronization /// primitives such as sleep, Mutex, and Condvar. pub(crate) fn ticktimer_server() -> Connection { - static TICKTIMER_SERVER_CONNECTION: AtomicU32 = AtomicU32::new(0); + static TICKTIMER_SERVER_CONNECTION: Atomic<u32> = AtomicU32::new(0); let cid = TICKTIMER_SERVER_CONNECTION.load(Ordering::Relaxed); if cid != 0 { return cid.into(); diff --git a/library/std/src/panic.rs b/library/std/src/panic.rs index 22776ae2bc4..f3b26ac64df 100644 --- a/library/std/src/panic.rs +++ b/library/std/src/panic.rs @@ -3,7 +3,7 @@ #![stable(feature = "std_panic", since = "1.9.0")] use crate::any::Any; -use crate::sync::atomic::{AtomicU8, Ordering}; +use crate::sync::atomic::{Atomic, AtomicU8, Ordering}; use crate::sync::{Condvar, Mutex, RwLock}; use crate::thread::Result; use crate::{collections, fmt, panicking}; @@ -469,7 +469,7 @@ impl BacktraceStyle { // that backtrace. // // Internally stores equivalent of an Option<BacktraceStyle>. -static SHOULD_CAPTURE: AtomicU8 = AtomicU8::new(0); +static SHOULD_CAPTURE: Atomic<u8> = AtomicU8::new(0); /// Configures whether the default panic hook will capture and display a /// backtrace. diff --git a/library/std/src/panicking.rs b/library/std/src/panicking.rs index a3950980b5e..4bfedf78366 100644 --- a/library/std/src/panicking.rs +++ b/library/std/src/panicking.rs @@ -21,7 +21,7 @@ use crate::any::Any; use crate::io::try_set_output_capture; use crate::mem::{self, ManuallyDrop}; use crate::panic::{BacktraceStyle, PanicHookInfo}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sync::{PoisonError, RwLock}; use crate::sys::backtrace; use crate::sys::stdio::panic_output; @@ -289,7 +289,7 @@ fn default_hook(info: &PanicHookInfo<'_>) { }; }); - static FIRST_PANIC: AtomicBool = AtomicBool::new(true); + static FIRST_PANIC: Atomic<bool> = AtomicBool::new(true); match backtrace { // SAFETY: we took out a lock just a second ago. @@ -374,7 +374,7 @@ pub mod panic_count { #[unstable(feature = "update_panic_count", issue = "none")] pub mod panic_count { use crate::cell::Cell; - use crate::sync::atomic::{AtomicUsize, Ordering}; + use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; const ALWAYS_ABORT_FLAG: usize = 1 << (usize::BITS - 1); @@ -416,7 +416,7 @@ pub mod panic_count { // // Stealing a bit is fine because it just amounts to assuming that each // panicking thread consumes at least 2 bytes of address space. - static GLOBAL_PANIC_COUNT: AtomicUsize = AtomicUsize::new(0); + static GLOBAL_PANIC_COUNT: Atomic<usize> = AtomicUsize::new(0); // Increases the global and local panic count, and returns whether an // immediate abort is required. diff --git a/library/std/src/sync/mpmc/array.rs b/library/std/src/sync/mpmc/array.rs index a467237fef1..880d8b5f57c 100644 --- a/library/std/src/sync/mpmc/array.rs +++ b/library/std/src/sync/mpmc/array.rs @@ -16,13 +16,13 @@ use super::waker::SyncWaker; use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; use crate::ptr; -use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicUsize, Ordering}; use crate::time::Instant; /// A slot in a channel. struct Slot<T> { /// The current stamp. - stamp: AtomicUsize, + stamp: Atomic<usize>, /// The message in this slot. Either read out in `read` or dropped through /// `discard_all_messages`. @@ -55,7 +55,7 @@ pub(crate) struct Channel<T> { /// represent the lap. The mark bit in the head is always zero. /// /// Messages are popped from the head of the channel. - head: CachePadded<AtomicUsize>, + head: CachePadded<Atomic<usize>>, /// The tail of the channel. /// @@ -64,7 +64,7 @@ pub(crate) struct Channel<T> { /// represent the lap. The mark bit indicates that the channel is disconnected. /// /// Messages are pushed into the tail of the channel. - tail: CachePadded<AtomicUsize>, + tail: CachePadded<Atomic<usize>>, /// The buffer holding slots. buffer: Box<[Slot<T>]>, diff --git a/library/std/src/sync/mpmc/context.rs b/library/std/src/sync/mpmc/context.rs index 51aa7e82e78..6b2f4cb6ffd 100644 --- a/library/std/src/sync/mpmc/context.rs +++ b/library/std/src/sync/mpmc/context.rs @@ -5,7 +5,7 @@ use super::waker::current_thread_id; use crate::cell::Cell; use crate::ptr; use crate::sync::Arc; -use crate::sync::atomic::{AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize, Ordering}; use crate::thread::{self, Thread}; use crate::time::Instant; @@ -19,10 +19,10 @@ pub struct Context { #[derive(Debug)] struct Inner { /// Selected operation. - select: AtomicUsize, + select: Atomic<usize>, /// A slot into which another thread may store a pointer to its `Packet`. - packet: AtomicPtr<()>, + packet: Atomic<*mut ()>, /// Thread handle. thread: Thread, diff --git a/library/std/src/sync/mpmc/counter.rs b/library/std/src/sync/mpmc/counter.rs index d1bfe612f53..efa6af11483 100644 --- a/library/std/src/sync/mpmc/counter.rs +++ b/library/std/src/sync/mpmc/counter.rs @@ -1,16 +1,16 @@ -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; use crate::{ops, process}; /// Reference counter internals. struct Counter<C> { /// The number of senders associated with the channel. - senders: AtomicUsize, + senders: Atomic<usize>, /// The number of receivers associated with the channel. - receivers: AtomicUsize, + receivers: Atomic<usize>, /// Set to `true` if the last sender or the last receiver reference deallocates the channel. - destroy: AtomicBool, + destroy: Atomic<bool>, /// The internal channel. chan: C, diff --git a/library/std/src/sync/mpmc/list.rs b/library/std/src/sync/mpmc/list.rs index 1c6acb29e37..3fcfb85cf2a 100644 --- a/library/std/src/sync/mpmc/list.rs +++ b/library/std/src/sync/mpmc/list.rs @@ -9,7 +9,7 @@ use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::mem::MaybeUninit; use crate::ptr; -use crate::sync::atomic::{self, AtomicPtr, AtomicUsize, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicPtr, AtomicUsize, Ordering}; use crate::time::Instant; // Bits indicating the state of a slot: @@ -37,7 +37,7 @@ struct Slot<T> { msg: UnsafeCell<MaybeUninit<T>>, /// The state of the slot. - state: AtomicUsize, + state: Atomic<usize>, } impl<T> Slot<T> { @@ -55,7 +55,7 @@ impl<T> Slot<T> { /// Each block in the list can hold up to `BLOCK_CAP` messages. struct Block<T> { /// The next block in the linked list. - next: AtomicPtr<Block<T>>, + next: Atomic<*mut Block<T>>, /// Slots for messages. slots: [Slot<T>; BLOCK_CAP], @@ -65,11 +65,11 @@ impl<T> Block<T> { /// Creates an empty block. fn new() -> Box<Block<T>> { // SAFETY: This is safe because: - // [1] `Block::next` (AtomicPtr) may be safely zero initialized. + // [1] `Block::next` (Atomic<*mut _>) may be safely zero initialized. // [2] `Block::slots` (Array) may be safely zero initialized because of [3, 4]. // [3] `Slot::msg` (UnsafeCell) may be safely zero initialized because it // holds a MaybeUninit. - // [4] `Slot::state` (AtomicUsize) may be safely zero initialized. + // [4] `Slot::state` (Atomic<usize>) may be safely zero initialized. unsafe { Box::new_zeroed().assume_init() } } @@ -110,10 +110,10 @@ impl<T> Block<T> { #[derive(Debug)] struct Position<T> { /// The index in the channel. - index: AtomicUsize, + index: Atomic<usize>, /// The block in the linked list. - block: AtomicPtr<Block<T>>, + block: Atomic<*mut Block<T>>, } /// The token type for the list flavor. diff --git a/library/std/src/sync/mpmc/waker.rs b/library/std/src/sync/mpmc/waker.rs index f5e764e69bd..4216fb7ac59 100644 --- a/library/std/src/sync/mpmc/waker.rs +++ b/library/std/src/sync/mpmc/waker.rs @@ -4,7 +4,7 @@ use super::context::Context; use super::select::{Operation, Selected}; use crate::ptr; use crate::sync::Mutex; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; /// Represents a thread blocked on a specific channel operation. pub(crate) struct Entry { @@ -137,7 +137,7 @@ pub(crate) struct SyncWaker { inner: Mutex<Waker>, /// `true` if the waker is empty. - is_empty: AtomicBool, + is_empty: Atomic<bool>, } impl SyncWaker { diff --git a/library/std/src/sync/mpmc/zero.rs b/library/std/src/sync/mpmc/zero.rs index 577997c07a6..f1ecf80fcb9 100644 --- a/library/std/src/sync/mpmc/zero.rs +++ b/library/std/src/sync/mpmc/zero.rs @@ -10,7 +10,7 @@ use super::waker::Waker; use crate::cell::UnsafeCell; use crate::marker::PhantomData; use crate::sync::Mutex; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::time::Instant; use crate::{fmt, ptr}; @@ -35,7 +35,7 @@ struct Packet<T> { on_stack: bool, /// Equals `true` once the packet is ready for reading or writing. - ready: AtomicBool, + ready: Atomic<bool>, /// The message. msg: UnsafeCell<Option<T>>, diff --git a/library/std/src/sync/poison.rs b/library/std/src/sync/poison.rs index 1b8809734b8..cc1d0b30152 100644 --- a/library/std/src/sync/poison.rs +++ b/library/std/src/sync/poison.rs @@ -76,7 +76,7 @@ pub use self::rwlock::{RwLock, RwLockReadGuard, RwLockWriteGuard}; use crate::error::Error; use crate::fmt; #[cfg(panic = "unwind")] -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; #[cfg(panic = "unwind")] use crate::thread; @@ -88,7 +88,7 @@ mod rwlock; pub(crate) struct Flag { #[cfg(panic = "unwind")] - failed: AtomicBool, + failed: Atomic<bool>, } // Note that the Ordering uses to access the `failed` field of `Flag` below is diff --git a/library/std/src/sync/reentrant_lock.rs b/library/std/src/sync/reentrant_lock.rs index e009eb410ef..24539d4e830 100644 --- a/library/std/src/sync/reentrant_lock.rs +++ b/library/std/src/sync/reentrant_lock.rs @@ -89,9 +89,9 @@ pub struct ReentrantLock<T: ?Sized> { cfg_if!( if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::{AtomicU64, Ordering::Relaxed}; + use crate::sync::atomic::{Atomic, AtomicU64, Ordering::Relaxed}; - struct Tid(AtomicU64); + struct Tid(Atomic<u64>); impl Tid { const fn new() -> Self { @@ -120,6 +120,7 @@ cfg_if!( } use crate::sync::atomic::{ + Atomic, AtomicUsize, Ordering, }; @@ -137,7 +138,7 @@ cfg_if!( // the current thread, or by a thread that has terminated before // the current thread was created. In either case, no further // synchronization is needed (as per <https://github.com/rust-lang/miri/issues/3450>) - tls_addr: AtomicUsize, + tls_addr: Atomic<usize>, tid: UnsafeCell<u64>, } diff --git a/library/std/src/sys/alloc/sgx.rs b/library/std/src/sys/alloc/sgx.rs index 7a846e2376b..afdef7a5cb6 100644 --- a/library/std/src/sys/alloc/sgx.rs +++ b/library/std/src/sys/alloc/sgx.rs @@ -1,6 +1,6 @@ use crate::alloc::{GlobalAlloc, Layout, System}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; use crate::sys::pal::abi::mem as sgx_mem; use crate::sys::pal::waitqueue::SpinMutex; @@ -22,7 +22,7 @@ struct Sgx; unsafe impl dlmalloc::Allocator for Sgx { /// Allocs system resources fn alloc(&self, _size: usize) -> (*mut u8, usize, u32) { - static INIT: AtomicBool = AtomicBool::new(false); + static INIT: Atomic<bool> = AtomicBool::new(false); // No ordering requirement since this function is protected by the global lock. if !INIT.swap(true, Ordering::Relaxed) { diff --git a/library/std/src/sys/alloc/wasm.rs b/library/std/src/sys/alloc/wasm.rs index 53fbc9529e5..c8fab992a88 100644 --- a/library/std/src/sys/alloc/wasm.rs +++ b/library/std/src/sys/alloc/wasm.rs @@ -60,10 +60,10 @@ unsafe impl GlobalAlloc for System { #[cfg(target_feature = "atomics")] mod lock { - use crate::sync::atomic::AtomicI32; use crate::sync::atomic::Ordering::{Acquire, Release}; + use crate::sync::atomic::{Atomic, AtomicI32}; - static LOCKED: AtomicI32 = AtomicI32::new(0); + static LOCKED: Atomic<i32> = AtomicI32::new(0); pub struct DropLock; diff --git a/library/std/src/sys/alloc/xous.rs b/library/std/src/sys/alloc/xous.rs index ccaa972c22d..c7f973b8027 100644 --- a/library/std/src/sys/alloc/xous.rs +++ b/library/std/src/sys/alloc/xous.rs @@ -49,10 +49,10 @@ unsafe impl GlobalAlloc for System { } mod lock { - use crate::sync::atomic::AtomicI32; use crate::sync::atomic::Ordering::{Acquire, Release}; + use crate::sync::atomic::{Atomic, AtomicI32}; - static LOCKED: AtomicI32 = AtomicI32::new(0); + static LOCKED: Atomic<i32> = AtomicI32::new(0); pub struct DropLock; diff --git a/library/std/src/sys/args/sgx.rs b/library/std/src/sys/args/sgx.rs index 0185a8a6000..2a4bc76aefb 100644 --- a/library/std/src/sys/args/sgx.rs +++ b/library/std/src/sys/args/sgx.rs @@ -1,7 +1,7 @@ #![allow(fuzzy_provenance_casts)] // FIXME: this module systematically confuses pointers and integers use crate::ffi::OsString; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::os_str::Buf; use crate::sys::pal::abi::usercalls::alloc; use crate::sys::pal::abi::usercalls::raw::ByteBuffer; @@ -11,7 +11,7 @@ use crate::{fmt, slice}; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3sgx4args4ARGSE")] -static ARGS: AtomicUsize = AtomicUsize::new(0); +static ARGS: Atomic<usize> = AtomicUsize::new(0); type ArgsStore = Vec<OsString>; #[cfg_attr(test, allow(dead_code))] diff --git a/library/std/src/sys/args/unix.rs b/library/std/src/sys/args/unix.rs index a7b79ad396e..0dfbd5f03eb 100644 --- a/library/std/src/sys/args/unix.rs +++ b/library/std/src/sys/args/unix.rs @@ -88,7 +88,7 @@ pub fn args() -> Args { mod imp { use crate::ffi::c_char; use crate::ptr; - use crate::sync::atomic::{AtomicIsize, AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicIsize, AtomicPtr, Ordering}; // The system-provided argc and argv, which we store in static memory // here so that we can defer the work of parsing them until its actually @@ -96,8 +96,8 @@ mod imp { // // Note that we never mutate argv/argc, the argv array, or the argv // strings, which allows the code in this file to be very simple. - static ARGC: AtomicIsize = AtomicIsize::new(0); - static ARGV: AtomicPtr<*const u8> = AtomicPtr::new(ptr::null_mut()); + static ARGC: Atomic<isize> = AtomicIsize::new(0); + static ARGV: Atomic<*mut *const u8> = AtomicPtr::new(ptr::null_mut()); unsafe fn really_init(argc: isize, argv: *const *const u8) { // These don't need to be ordered with each other or other stores, diff --git a/library/std/src/sys/env/sgx.rs b/library/std/src/sys/env/sgx.rs index 85be9cd6ad4..09090ec7cf0 100644 --- a/library/std/src/sys/env/sgx.rs +++ b/library/std/src/sys/env/sgx.rs @@ -4,13 +4,13 @@ pub use super::common::Env; use crate::collections::HashMap; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sync::{Mutex, Once}; // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os3ENVE")] -static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV: Atomic<usize> = AtomicUsize::new(0); // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx2os8ENV_INITE")] diff --git a/library/std/src/sys/env/xous.rs b/library/std/src/sys/env/xous.rs index 232a3dafb0b..8f65f30d35f 100644 --- a/library/std/src/sys/env/xous.rs +++ b/library/std/src/sys/env/xous.rs @@ -2,11 +2,11 @@ pub use super::common::Env; use crate::collections::HashMap; use crate::ffi::{OsStr, OsString}; use crate::io; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sync::{Mutex, Once}; use crate::sys::pal::os::{get_application_parameters, params}; -static ENV: AtomicUsize = AtomicUsize::new(0); +static ENV: Atomic<usize> = AtomicUsize::new(0); static ENV_INIT: Once = Once::new(); type EnvStore = Mutex<HashMap<OsString, OsString>>; diff --git a/library/std/src/sys/fs/unix.rs b/library/std/src/sys/fs/unix.rs index 351a9f9413f..5af59a2a914 100644 --- a/library/std/src/sys/fs/unix.rs +++ b/library/std/src/sys/fs/unix.rs @@ -147,14 +147,14 @@ cfg_has_statx! {{ flags: i32, mask: u32, ) -> Option<io::Result<FileAttr>> { - use crate::sync::atomic::{AtomicU8, Ordering}; + use crate::sync::atomic::{Atomic, AtomicU8, Ordering}; // Linux kernel prior to 4.11 or glibc prior to glibc 2.28 don't support `statx`. // We check for it on first failure and remember availability to avoid having to // do it again. #[repr(u8)] enum STATX_STATE{ Unknown = 0, Present, Unavailable } - static STATX_SAVED_STATE: AtomicU8 = AtomicU8::new(STATX_STATE::Unknown as u8); + static STATX_SAVED_STATE: Atomic<u8> = AtomicU8::new(STATX_STATE::Unknown as u8); syscall!( fn statx( diff --git a/library/std/src/sys/fs/windows/remove_dir_all.rs b/library/std/src/sys/fs/windows/remove_dir_all.rs index b213c49bcb0..06734f9e309 100644 --- a/library/std/src/sys/fs/windows/remove_dir_all.rs +++ b/library/std/src/sys/fs/windows/remove_dir_all.rs @@ -29,7 +29,7 @@ //! race but we do make a best effort such that it *should* do so. use core::ptr; -use core::sync::atomic::{AtomicU32, Ordering}; +use core::sync::atomic::{Atomic, AtomicU32, Ordering}; use super::{AsRawHandle, DirBuff, File, FromRawHandle}; use crate::sys::c; @@ -87,7 +87,7 @@ fn open_link_no_reparse( // The `OBJ_DONT_REPARSE` attribute ensures that we haven't been // tricked into following a symlink. However, it may not be available in // earlier versions of Windows. - static ATTRIBUTES: AtomicU32 = AtomicU32::new(c::OBJ_DONT_REPARSE); + static ATTRIBUTES: Atomic<u32> = AtomicU32::new(c::OBJ_DONT_REPARSE); let result = unsafe { let mut path_str = c::UNICODE_STRING::from_ref(path); diff --git a/library/std/src/sys/net/connection/xous/tcplistener.rs b/library/std/src/sys/net/connection/xous/tcplistener.rs index 7f13ca55920..bdf1fcd9302 100644 --- a/library/std/src/sys/net/connection/xous/tcplistener.rs +++ b/library/std/src/sys/net/connection/xous/tcplistener.rs @@ -1,5 +1,5 @@ use core::convert::TryInto; -use core::sync::atomic::{AtomicBool, AtomicU16, AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicBool, AtomicU16, AtomicUsize, Ordering}; use super::*; use crate::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; @@ -18,10 +18,10 @@ macro_rules! unimpl { #[derive(Clone)] pub struct TcpListener { - fd: Arc<AtomicU16>, + fd: Arc<Atomic<u16>>, local: SocketAddr, - handle_count: Arc<AtomicUsize>, - nonblocking: Arc<AtomicBool>, + handle_count: Arc<Atomic<usize>>, + nonblocking: Arc<Atomic<bool>>, } impl TcpListener { diff --git a/library/std/src/sys/net/connection/xous/tcpstream.rs b/library/std/src/sys/net/connection/xous/tcpstream.rs index e8aea8b706a..54524767452 100644 --- a/library/std/src/sys/net/connection/xous/tcpstream.rs +++ b/library/std/src/sys/net/connection/xous/tcpstream.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicBool, AtomicU32, AtomicUsize, Ordering}; use super::*; use crate::fmt; @@ -29,11 +29,11 @@ pub struct TcpStream { remote_port: u16, peer_addr: SocketAddr, // milliseconds - read_timeout: Arc<AtomicU32>, + read_timeout: Arc<Atomic<u32>>, // milliseconds - write_timeout: Arc<AtomicU32>, - handle_count: Arc<AtomicUsize>, - nonblocking: Arc<AtomicBool>, + write_timeout: Arc<Atomic<u32>>, + handle_count: Arc<Atomic<usize>>, + nonblocking: Arc<Atomic<bool>>, } fn sockaddr_to_buf(duration: Duration, addr: &SocketAddr, buf: &mut [u8]) { diff --git a/library/std/src/sys/net/connection/xous/udp.rs b/library/std/src/sys/net/connection/xous/udp.rs index c112c04ce94..2127d3267ed 100644 --- a/library/std/src/sys/net/connection/xous/udp.rs +++ b/library/std/src/sys/net/connection/xous/udp.rs @@ -1,5 +1,5 @@ use core::convert::TryInto; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use super::*; use crate::cell::Cell; @@ -27,7 +27,7 @@ pub struct UdpSocket { read_timeout: Cell<u64>, // in milliseconds. The setting applies only to `send` calls after the timeout is set. write_timeout: Cell<u64>, - handle_count: Arc<AtomicUsize>, + handle_count: Arc<Atomic<usize>>, nonblocking: Cell<bool>, } diff --git a/library/std/src/sys/pal/hermit/futex.rs b/library/std/src/sys/pal/hermit/futex.rs index 670383b45ac..78c86071fdd 100644 --- a/library/std/src/sys/pal/hermit/futex.rs +++ b/library/std/src/sys/pal/hermit/futex.rs @@ -1,19 +1,19 @@ use super::hermit_abi; use crate::ptr::null; -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic<Primitive>; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic<SmallPrimitive>; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { // Calculate the timeout as a relative timespec. // // Overflows are rounded up to an infinite timeout (None). @@ -37,12 +37,12 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - } #[inline] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic<u32>) -> bool { unsafe { hermit_abi::futex_wake(futex.as_ptr(), 1) > 0 } } #[inline] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { unsafe { hermit_abi::futex_wake(futex.as_ptr(), i32::MAX); } diff --git a/library/std/src/sys/pal/itron/spin.rs b/library/std/src/sys/pal/itron/spin.rs index 6a9a7c72deb..bc4f83260bb 100644 --- a/library/std/src/sys/pal/itron/spin.rs +++ b/library/std/src/sys/pal/itron/spin.rs @@ -1,12 +1,12 @@ use super::abi; use crate::cell::UnsafeCell; use crate::mem::MaybeUninit; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; /// A mutex implemented by `dis_dsp` (for intra-core synchronization) and a /// spinlock (for inter-core synchronization). pub struct SpinMutex<T = ()> { - locked: AtomicBool, + locked: Atomic<bool>, data: UnsafeCell<T>, } @@ -19,7 +19,7 @@ impl<T> SpinMutex<T> { /// Acquire a lock. #[inline] pub fn with_locked<R>(&self, f: impl FnOnce(&mut T) -> R) -> R { - struct SpinMutexGuard<'a>(&'a AtomicBool); + struct SpinMutexGuard<'a>(&'a Atomic<bool>); impl Drop for SpinMutexGuard<'_> { #[inline] @@ -50,7 +50,7 @@ impl<T> SpinMutex<T> { /// It's assumed that `0` is not a valid ID, and all kernel /// object IDs fall into range `1..=usize::MAX`. pub struct SpinIdOnceCell<T = ()> { - id: AtomicUsize, + id: Atomic<usize>, spin: SpinMutex<()>, extra: UnsafeCell<MaybeUninit<T>>, } diff --git a/library/std/src/sys/pal/itron/thread.rs b/library/std/src/sys/pal/itron/thread.rs index d1481f827e1..a974f4f17ae 100644 --- a/library/std/src/sys/pal/itron/thread.rs +++ b/library/std/src/sys/pal/itron/thread.rs @@ -9,7 +9,7 @@ use crate::ffi::CStr; use crate::mem::ManuallyDrop; use crate::num::NonZero; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::time::Duration; use crate::{hint, io}; @@ -64,7 +64,7 @@ struct ThreadInner { /// '--> JOIN_FINALIZE ---' /// (-1) /// - lifecycle: AtomicUsize, + lifecycle: Atomic<usize>, } // Safety: The only `!Sync` field, `ThreadInner::start`, is only touched by diff --git a/library/std/src/sys/pal/sgx/abi/mod.rs b/library/std/src/sys/pal/sgx/abi/mod.rs index 2c805a4d0af..57247cffad3 100644 --- a/library/std/src/sys/pal/sgx/abi/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/mod.rs @@ -1,7 +1,7 @@ #![cfg_attr(test, allow(unused))] // RT initialization logic is not compiled for test use core::arch::global_asm; -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::io::Write; @@ -31,7 +31,7 @@ unsafe extern "C" fn tcs_init(secondary: bool) { const BUSY: usize = 1; const DONE: usize = 2; // Three-state spin-lock - static RELOC_STATE: AtomicUsize = AtomicUsize::new(UNINIT); + static RELOC_STATE: Atomic<usize> = AtomicUsize::new(UNINIT); if secondary && RELOC_STATE.load(Ordering::Relaxed) != DONE { rtabort!("Entered secondary TCS before main TCS!") diff --git a/library/std/src/sys/pal/sgx/abi/tls/mod.rs b/library/std/src/sys/pal/sgx/abi/tls/mod.rs index f082d94614b..41e38b69616 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/mod.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/mod.rs @@ -3,7 +3,7 @@ mod sync_bitset; use self::sync_bitset::*; use crate::cell::Cell; use crate::num::NonZero; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::{mem, ptr}; #[cfg(target_pointer_width = "64")] @@ -15,14 +15,10 @@ const TLS_KEYS_BITSET_SIZE: usize = (TLS_KEYS + (USIZE_BITS - 1)) / USIZE_BITS; #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_KEY_IN_USEE")] static TLS_KEY_IN_USE: SyncBitset = SYNC_BITSET_INIT; -macro_rules! dup { - ((* $($exp:tt)*) $($val:tt)*) => (dup!( ($($exp)*) $($val)* $($val)* )); - (() $($val:tt)*) => ([$($val),*]) -} // Specifying linkage/symbol name is solely to ensure a single instance between this crate and its unit tests #[cfg_attr(test, linkage = "available_externally")] #[unsafe(export_name = "_ZN16__rust_internals3std3sys3pal3sgx3abi3tls14TLS_DESTRUCTORE")] -static TLS_DESTRUCTOR: [AtomicUsize; TLS_KEYS] = dup!((* * * * * * *) (AtomicUsize::new(0))); +static TLS_DESTRUCTOR: [Atomic<usize>; TLS_KEYS] = [const { AtomicUsize::new(0) }; TLS_KEYS]; unsafe extern "C" { fn get_tls_ptr() -> *const u8; @@ -84,7 +80,7 @@ impl<'a> Drop for ActiveTls<'a> { impl Tls { pub fn new() -> Tls { - Tls { data: dup!((* * * * * * *) (Cell::new(ptr::null_mut()))) } + Tls { data: [const { Cell::new(ptr::null_mut()) }; TLS_KEYS] } } pub unsafe fn activate(&self) -> ActiveTls<'_> { diff --git a/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs index 4eeff8f6ef7..9087168589a 100644 --- a/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs +++ b/library/std/src/sys/pal/sgx/abi/tls/sync_bitset.rs @@ -4,10 +4,10 @@ mod tests; use super::{TLS_KEYS_BITSET_SIZE, USIZE_BITS}; use crate::iter::{Enumerate, Peekable}; use crate::slice::Iter; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; /// A bitset that can be used synchronously. -pub(super) struct SyncBitset([AtomicUsize; TLS_KEYS_BITSET_SIZE]); +pub(super) struct SyncBitset([Atomic<usize>; TLS_KEYS_BITSET_SIZE]); pub(super) const SYNC_BITSET_INIT: SyncBitset = SyncBitset([AtomicUsize::new(0), AtomicUsize::new(0)]); @@ -58,7 +58,7 @@ impl SyncBitset { } pub(super) struct SyncBitsetIter<'a> { - iter: Peekable<Enumerate<Iter<'a, AtomicUsize>>>, + iter: Peekable<Enumerate<Iter<'a, Atomic<usize>>>>, elem_idx: usize, } diff --git a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs index 3fe6dee3d6f..a60b83213fd 100644 --- a/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs +++ b/library/std/src/sys/pal/sgx/abi/usercalls/alloc.rs @@ -675,7 +675,7 @@ where /// Obtain the number of elements in this user slice. pub fn len(&self) -> usize { - unsafe { (*self.0.get()).len() } + unsafe { self.0.get().len() } } /// Copies the value from user memory and appends it to `dest`. diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs index 99735947e2c..3932f64c0ef 100644 --- a/library/std/src/sys/pal/sgx/mod.rs +++ b/library/std/src/sys/pal/sgx/mod.rs @@ -6,7 +6,7 @@ #![allow(fuzzy_provenance_casts)] // FIXME: this entire module systematically confuses pointers and integers use crate::io::ErrorKind; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; pub mod abi; mod libunwind_integration; @@ -46,7 +46,7 @@ pub fn unsupported_err() -> crate::io::Error { /// what happens when `SGX_INEFFECTIVE_ERROR` is set to `true`. If it is /// `false`, the behavior is the same as `unsupported`. pub fn sgx_ineffective<T>(v: T) -> crate::io::Result<T> { - static SGX_INEFFECTIVE_ERROR: AtomicBool = AtomicBool::new(false); + static SGX_INEFFECTIVE_ERROR: Atomic<bool> = AtomicBool::new(false); if SGX_INEFFECTIVE_ERROR.load(Ordering::Relaxed) { Err(crate::io::const_error!( ErrorKind::Uncategorized, diff --git a/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs index f6e851ccadd..73c7a101d60 100644 --- a/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs +++ b/library/std/src/sys/pal/sgx/waitqueue/spin_mutex.rs @@ -7,12 +7,12 @@ mod tests; use crate::cell::UnsafeCell; use crate::hint; use crate::ops::{Deref, DerefMut}; -use crate::sync::atomic::{AtomicBool, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, Ordering}; #[derive(Default)] pub struct SpinMutex<T> { value: UnsafeCell<T>, - lock: AtomicBool, + lock: Atomic<bool>, } unsafe impl<T: Send> Send for SpinMutex<T> {} diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs index 309022bcccf..6ee3e0a8b66 100644 --- a/library/std/src/sys/pal/uefi/helpers.rs +++ b/library/std/src/sys/pal/uefi/helpers.rs @@ -22,7 +22,7 @@ use crate::os::uefi::{self}; use crate::path::Path; use crate::ptr::NonNull; use crate::slice; -use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys_common::wstr::WStrUnits; type BootInstallMultipleProtocolInterfaces = @@ -157,7 +157,7 @@ pub(crate) fn device_path_to_text(path: NonNull<device_path::Protocol>) -> io::R Ok(path) } - static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { @@ -269,7 +269,7 @@ impl OwnedDevicePath { .ok_or_else(|| const_error!(io::ErrorKind::InvalidFilename, "invalid Device Path")) } - static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { @@ -606,7 +606,7 @@ pub(crate) fn os_string_to_raw(s: &OsStr) -> Option<Box<[r_efi::efi::Char16]>> { } pub(crate) fn open_shell() -> Option<NonNull<shell::Protocol>> { - static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs index bd6a36021f4..78fcfcb3b77 100644 --- a/library/std/src/sys/pal/uefi/mod.rs +++ b/library/std/src/sys/pal/uefi/mod.rs @@ -28,9 +28,9 @@ pub type RawOsError = usize; use crate::io as std_io; use crate::os::uefi; use crate::ptr::NonNull; -use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; -static EXIT_BOOT_SERVICE_EVENT: AtomicPtr<crate::ffi::c_void> = +static EXIT_BOOT_SERVICE_EVENT: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); /// # SAFETY diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs index c4ff3015ac6..eeb2c35ffbb 100644 --- a/library/std/src/sys/pal/uefi/time.rs +++ b/library/std/src/sys/pal/uefi/time.rs @@ -121,7 +121,7 @@ pub(crate) mod instant_internal { use super::*; use crate::mem::MaybeUninit; use crate::ptr::NonNull; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys_common::mul_div_u64; const NS_PER_SEC: u64 = 1_000_000_000; @@ -142,7 +142,7 @@ pub(crate) mod instant_internal { Some(mul_div_u64(ts, NS_PER_SEC, freq)) } - static LAST_VALID_HANDLE: AtomicPtr<crate::ffi::c_void> = + static LAST_VALID_HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(crate::ptr::null_mut()); if let Some(handle) = NonNull::new(LAST_VALID_HANDLE.load(Ordering::Acquire)) { diff --git a/library/std/src/sys/pal/unix/futex.rs b/library/std/src/sys/pal/unix/futex.rs index 87ba13ca932..8d89163c42c 100644 --- a/library/std/src/sys/pal/unix/futex.rs +++ b/library/std/src/sys/pal/unix/futex.rs @@ -8,16 +8,16 @@ target_os = "fuchsia", ))] -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic<Primitive>; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic<SmallPrimitive>; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; @@ -27,7 +27,7 @@ pub type SmallPrimitive = u32; /// /// Returns false on timeout, and true in all other cases. #[cfg(any(target_os = "linux", target_os = "android", target_os = "freebsd"))] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { use super::time::Timespec; use crate::ptr::null; use crate::sync::atomic::Ordering::Relaxed; @@ -60,7 +60,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - let umtx_timeout_ptr = umtx_timeout.as_ref().map_or(null(), |t| t as *const _); let umtx_timeout_size = umtx_timeout.as_ref().map_or(0, |t| size_of_val(t)); libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic<u32> as *mut _, libc::UMTX_OP_WAIT_UINT_PRIVATE, expected as libc::c_ulong, crate::ptr::without_provenance_mut(umtx_timeout_size), @@ -71,7 +71,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - // absolute time rather than a relative time. libc::syscall( libc::SYS_futex, - futex as *const AtomicU32, + futex as *const Atomic<u32>, libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG, expected, timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), @@ -99,16 +99,16 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - /// /// On some platforms, this always returns false. #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn futex_wake(futex: &AtomicU32) -> bool { - let ptr = futex as *const AtomicU32; +pub fn futex_wake(futex: &Atomic<u32>) -> bool { + let ptr = futex as *const Atomic<u32>; let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 } } /// Wakes up all threads that are waiting on `futex_wait` on this futex. #[cfg(any(target_os = "linux", target_os = "android"))] -pub fn futex_wake_all(futex: &AtomicU32) { - let ptr = futex as *const AtomicU32; +pub fn futex_wake_all(futex: &Atomic<u32>) { + let ptr = futex as *const Atomic<u32>; let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG; unsafe { libc::syscall(libc::SYS_futex, ptr, op, i32::MAX); @@ -117,11 +117,11 @@ pub fn futex_wake_all(futex: &AtomicU32) { // FreeBSD doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "freebsd")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic<u32>) -> bool { use crate::ptr::null_mut; unsafe { libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic<u32> as *mut _, libc::UMTX_OP_WAKE_PRIVATE, 1, null_mut(), @@ -132,11 +132,11 @@ pub fn futex_wake(futex: &AtomicU32) -> bool { } #[cfg(target_os = "freebsd")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { use crate::ptr::null_mut; unsafe { libc::_umtx_op( - futex as *const AtomicU32 as *mut _, + futex as *const Atomic<u32> as *mut _, libc::UMTX_OP_WAKE_PRIVATE, i32::MAX as libc::c_ulong, null_mut(), @@ -146,7 +146,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "openbsd")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { use super::time::Timespec; use crate::ptr::{null, null_mut}; @@ -157,7 +157,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - let r = unsafe { libc::futex( - futex as *const AtomicU32 as *mut u32, + futex as *const Atomic<u32> as *mut u32, libc::FUTEX_WAIT, expected as i32, timespec.as_ref().map_or(null(), |t| t as *const libc::timespec), @@ -169,20 +169,25 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - } #[cfg(target_os = "openbsd")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic<u32>) -> bool { use crate::ptr::{null, null_mut}; unsafe { - libc::futex(futex as *const AtomicU32 as *mut u32, libc::FUTEX_WAKE, 1, null(), null_mut()) - > 0 + libc::futex( + futex as *const Atomic<u32> as *mut u32, + libc::FUTEX_WAKE, + 1, + null(), + null_mut(), + ) > 0 } } #[cfg(target_os = "openbsd")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { use crate::ptr::{null, null_mut}; unsafe { libc::futex( - futex as *const AtomicU32 as *mut u32, + futex as *const Atomic<u32> as *mut u32, libc::FUTEX_WAKE, i32::MAX, null(), @@ -192,7 +197,7 @@ pub fn futex_wake_all(futex: &AtomicU32) { } #[cfg(target_os = "dragonfly")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { // A timeout of 0 means infinite. // We round smaller timeouts up to 1 millisecond. // Overflows are rounded up to an infinite timeout. @@ -200,7 +205,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - timeout.and_then(|d| Some(i32::try_from(d.as_millis()).ok()?.max(1))).unwrap_or(0); let r = unsafe { - libc::umtx_sleep(futex as *const AtomicU32 as *const i32, expected as i32, timeout_ms) + libc::umtx_sleep(futex as *const Atomic<u32> as *const i32, expected as i32, timeout_ms) }; r == 0 || super::os::errno() != libc::ETIMEDOUT @@ -208,28 +213,28 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - // DragonflyBSD doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "dragonfly")] -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, 1) }; +pub fn futex_wake(futex: &Atomic<u32>) -> bool { + unsafe { libc::umtx_wakeup(futex as *const Atomic<u32> as *const i32, 1) }; false } #[cfg(target_os = "dragonfly")] -pub fn futex_wake_all(futex: &AtomicU32) { - unsafe { libc::umtx_wakeup(futex as *const AtomicU32 as *const i32, i32::MAX) }; +pub fn futex_wake_all(futex: &Atomic<u32>) { + unsafe { libc::umtx_wakeup(futex as *const Atomic<u32> as *const i32, i32::MAX) }; } #[cfg(target_os = "emscripten")] unsafe extern "C" { - fn emscripten_futex_wake(addr: *const AtomicU32, count: libc::c_int) -> libc::c_int; + fn emscripten_futex_wake(addr: *const Atomic<u32>, count: libc::c_int) -> libc::c_int; fn emscripten_futex_wait( - addr: *const AtomicU32, + addr: *const Atomic<u32>, val: libc::c_uint, max_wait_ms: libc::c_double, ) -> libc::c_int; } #[cfg(target_os = "emscripten")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { unsafe { emscripten_futex_wait( futex, @@ -240,18 +245,18 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - } #[cfg(target_os = "emscripten")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic<u32>) -> bool { unsafe { emscripten_futex_wake(futex, 1) > 0 } } #[cfg(target_os = "emscripten")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { unsafe { emscripten_futex_wake(futex, i32::MAX) }; } #[cfg(target_os = "fuchsia")] pub mod zircon { - pub type zx_futex_t = crate::sync::atomic::AtomicU32; + pub type zx_futex_t = crate::sync::atomic::Atomic<u32>; pub type zx_handle_t = u32; pub type zx_status_t = i32; pub type zx_time_t = i64; @@ -282,7 +287,7 @@ pub mod zircon { } #[cfg(target_os = "fuchsia")] -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { // Sleep forever if the timeout is longer than fits in a i64. let deadline = timeout .and_then(|d| { @@ -293,19 +298,23 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - .unwrap_or(zircon::ZX_TIME_INFINITE); unsafe { - zircon::zx_futex_wait(futex, AtomicU32::new(expected), zircon::ZX_HANDLE_INVALID, deadline) - != zircon::ZX_ERR_TIMED_OUT + zircon::zx_futex_wait( + futex, + core::sync::atomic::AtomicU32::new(expected), + zircon::ZX_HANDLE_INVALID, + deadline, + ) != zircon::ZX_ERR_TIMED_OUT } } // Fuchsia doesn't tell us how many threads are woken up, so this always returns false. #[cfg(target_os = "fuchsia")] -pub fn futex_wake(futex: &AtomicU32) -> bool { +pub fn futex_wake(futex: &Atomic<u32>) -> bool { unsafe { zircon::zx_futex_wake(futex, 1) }; false } #[cfg(target_os = "fuchsia")] -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { unsafe { zircon::zx_futex_wake(futex, u32::MAX) }; } diff --git a/library/std/src/sys/pal/unix/kernel_copy.rs b/library/std/src/sys/pal/unix/kernel_copy.rs index d42a7e2a7fc..b984afa149d 100644 --- a/library/std/src/sys/pal/unix/kernel_copy.rs +++ b/library/std/src/sys/pal/unix/kernel_copy.rs @@ -62,7 +62,7 @@ use crate::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use crate::os::unix::net::UnixStream; use crate::process::{ChildStderr, ChildStdin, ChildStdout}; use crate::ptr; -use crate::sync::atomic::{AtomicBool, AtomicU8, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicU8, Ordering}; use crate::sys::cvt; use crate::sys::fs::CachedFileMetadata; use crate::sys::weak::syscall; @@ -596,7 +596,7 @@ pub(super) fn copy_regular_files(reader: RawFd, writer: RawFd, max_len: u64) -> // Kernel prior to 4.5 don't have copy_file_range // We store the availability in a global to avoid unnecessary syscalls - static HAS_COPY_FILE_RANGE: AtomicU8 = AtomicU8::new(NOT_PROBED); + static HAS_COPY_FILE_RANGE: Atomic<u8> = AtomicU8::new(NOT_PROBED); let mut have_probed = match HAS_COPY_FILE_RANGE.load(Ordering::Relaxed) { NOT_PROBED => false, @@ -721,8 +721,8 @@ enum SpliceMode { /// performs splice or sendfile between file descriptors /// Does _not_ fall back to a generic copy loop. fn sendfile_splice(mode: SpliceMode, reader: RawFd, writer: RawFd, len: u64) -> CopyResult { - static HAS_SENDFILE: AtomicBool = AtomicBool::new(true); - static HAS_SPLICE: AtomicBool = AtomicBool::new(true); + static HAS_SENDFILE: Atomic<bool> = AtomicBool::new(true); + static HAS_SPLICE: Atomic<bool> = AtomicBool::new(true); // Android builds use feature level 14, but the libc wrapper for splice is // gated on feature level 21+, so we have to invoke the syscall directly. diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs index a4702ae1b18..ba9e14b8009 100644 --- a/library/std/src/sys/pal/unix/mod.rs +++ b/library/std/src/sys/pal/unix/mod.rs @@ -204,7 +204,7 @@ pub unsafe fn init(argc: isize, argv: *const *const u8, sigpipe: u8) { target_os = "vxworks", target_os = "vita", )))] -static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::AtomicBool = +static ON_BROKEN_PIPE_FLAG_USED: crate::sync::atomic::Atomic<bool> = crate::sync::atomic::AtomicBool::new(false); #[cfg(not(any( diff --git a/library/std/src/sys/pal/unix/stack_overflow.rs b/library/std/src/sys/pal/unix/stack_overflow.rs index 34b3948e3f6..8bf6d833515 100644 --- a/library/std/src/sys/pal/unix/stack_overflow.rs +++ b/library/std/src/sys/pal/unix/stack_overflow.rs @@ -49,7 +49,7 @@ mod imp { use crate::cell::Cell; use crate::ops::Range; use crate::sync::OnceLock; - use crate::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering}; + use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering}; use crate::sys::pal::unix::os; use crate::{io, mem, ptr, thread}; @@ -118,9 +118,9 @@ mod imp { } } - static PAGE_SIZE: AtomicUsize = AtomicUsize::new(0); - static MAIN_ALTSTACK: AtomicPtr<libc::c_void> = AtomicPtr::new(ptr::null_mut()); - static NEED_ALTSTACK: AtomicBool = AtomicBool::new(false); + static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0); + static MAIN_ALTSTACK: Atomic<*mut libc::c_void> = AtomicPtr::new(ptr::null_mut()); + static NEED_ALTSTACK: Atomic<bool> = AtomicBool::new(false); /// # Safety /// Must be called only once diff --git a/library/std/src/sys/pal/unix/weak.rs b/library/std/src/sys/pal/unix/weak.rs index a034995e652..c8cf75b876c 100644 --- a/library/std/src/sys/pal/unix/weak.rs +++ b/library/std/src/sys/pal/unix/weak.rs @@ -24,7 +24,7 @@ use crate::ffi::CStr; use crate::marker::PhantomData; -use crate::sync::atomic::{self, AtomicPtr, Ordering}; +use crate::sync::atomic::{self, Atomic, AtomicPtr, Ordering}; use crate::{mem, ptr}; // We can use true weak linkage on ELF targets. @@ -80,7 +80,7 @@ pub(crate) macro dlsym { } pub(crate) struct DlsymWeak<F> { name: &'static str, - func: AtomicPtr<libc::c_void>, + func: Atomic<*mut libc::c_void>, _marker: PhantomData<F>, } diff --git a/library/std/src/sys/pal/wasm/atomics/futex.rs b/library/std/src/sys/pal/wasm/atomics/futex.rs index bdad0da73f0..6676aa7e8e3 100644 --- a/library/std/src/sys/pal/wasm/atomics/futex.rs +++ b/library/std/src/sys/pal/wasm/atomics/futex.rs @@ -3,16 +3,16 @@ use core::arch::wasm32 as wasm; #[cfg(target_arch = "wasm64")] use core::arch::wasm64 as wasm; -use crate::sync::atomic::AtomicU32; +use crate::sync::atomic::Atomic; use crate::time::Duration; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic<Primitive>; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU32; +pub type SmallFutex = Atomic<SmallPrimitive>; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u32; @@ -21,11 +21,14 @@ pub type SmallPrimitive = u32; /// Returns directly if the futex doesn't hold the expected value. /// /// Returns false on timeout, and true in all other cases. -pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -> bool { +pub fn futex_wait(futex: &Atomic<u32>, expected: u32, timeout: Option<Duration>) -> bool { let timeout = timeout.and_then(|t| t.as_nanos().try_into().ok()).unwrap_or(-1); unsafe { - wasm::memory_atomic_wait32(futex as *const AtomicU32 as *mut i32, expected as i32, timeout) - < 2 + wasm::memory_atomic_wait32( + futex as *const Atomic<u32> as *mut i32, + expected as i32, + timeout, + ) < 2 } } @@ -33,13 +36,13 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) - /// /// Returns true if this actually woke up such a thread, /// or false if no thread was waiting on this futex. -pub fn futex_wake(futex: &AtomicU32) -> bool { - unsafe { wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, 1) > 0 } +pub fn futex_wake(futex: &Atomic<u32>) -> bool { + unsafe { wasm::memory_atomic_notify(futex as *const Atomic<u32> as *mut i32, 1) > 0 } } /// Wakes up all threads that are waiting on `futex_wait` on this futex. -pub fn futex_wake_all(futex: &AtomicU32) { +pub fn futex_wake_all(futex: &Atomic<u32>) { unsafe { - wasm::memory_atomic_notify(futex as *const AtomicU32 as *mut i32, i32::MAX as u32); + wasm::memory_atomic_notify(futex as *const Atomic<u32> as *mut i32, i32::MAX as u32); } } diff --git a/library/std/src/sys/pal/windows/compat.rs b/library/std/src/sys/pal/windows/compat.rs index 2b9838437e9..14f2c8d881c 100644 --- a/library/std/src/sys/pal/windows/compat.rs +++ b/library/std/src/sys/pal/windows/compat.rs @@ -145,7 +145,7 @@ macro_rules! compat_fn_with_fallback { use super::*; use crate::mem; use crate::ffi::CStr; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::sys::compat::Module; type F = unsafe extern "system" fn($($argtype),*) -> $rettype; @@ -155,7 +155,7 @@ macro_rules! compat_fn_with_fallback { /// When that is called it attempts to load the requested symbol. /// If it succeeds, `PTR` is set to the address of that symbol. /// If it fails, then `PTR` is set to `fallback`. - static PTR: AtomicPtr<c_void> = AtomicPtr::new(load as *mut _); + static PTR: Atomic<*mut c_void> = AtomicPtr::new(load as *mut _); unsafe extern "system" fn load($($argname: $argtype),*) -> $rettype { unsafe { @@ -212,9 +212,9 @@ macro_rules! compat_fn_optional { use crate::ffi::c_void; use crate::mem; use crate::ptr::{self, NonNull}; - use crate::sync::atomic::{AtomicPtr, Ordering}; + use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; - pub(in crate::sys) static PTR: AtomicPtr<c_void> = AtomicPtr::new(ptr::null_mut()); + pub(in crate::sys) static PTR: Atomic<*mut c_void> = AtomicPtr::new(ptr::null_mut()); type F = unsafe extern "system" fn($($argtype),*) $(-> $rettype)?; diff --git a/library/std/src/sys/pal/windows/futex.rs b/library/std/src/sys/pal/windows/futex.rs index aebf638239c..cfa0a6b3815 100644 --- a/library/std/src/sys/pal/windows/futex.rs +++ b/library/std/src/sys/pal/windows/futex.rs @@ -1,8 +1,8 @@ use core::ffi::c_void; use core::ptr; use core::sync::atomic::{ - AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, AtomicU8, - AtomicU16, AtomicU32, AtomicU64, AtomicUsize, + Atomic, AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr, + AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize, }; use core::time::Duration; @@ -10,12 +10,12 @@ use super::api::{self, WinError}; use crate::sys::{c, dur2timeout}; /// An atomic for use as a futex that is at least 32-bits but may be larger -pub type Futex = AtomicU32; +pub type Futex = Atomic<Primitive>; /// Must be the underlying type of Futex pub type Primitive = u32; /// An atomic for use as a futex that is at least 8-bits but may be larger. -pub type SmallFutex = AtomicU8; +pub type SmallFutex = Atomic<SmallPrimitive>; /// Must be the underlying type of SmallFutex pub type SmallPrimitive = u8; @@ -47,10 +47,10 @@ unsafe_waitable_int! { (usize, AtomicUsize), } unsafe impl<T> Waitable for *const T { - type Futex = AtomicPtr<T>; + type Futex = Atomic<*mut T>; } unsafe impl<T> Waitable for *mut T { - type Futex = AtomicPtr<T>; + type Futex = Atomic<*mut T>; } unsafe impl<T> Futexable for AtomicPtr<T> {} diff --git a/library/std/src/sys/pal/windows/pipe.rs b/library/std/src/sys/pal/windows/pipe.rs index 7fd62339619..00d469fbaf8 100644 --- a/library/std/src/sys/pal/windows/pipe.rs +++ b/library/std/src/sys/pal/windows/pipe.rs @@ -3,8 +3,8 @@ use crate::io::{self, BorrowedCursor, IoSlice, IoSliceMut}; use crate::os::windows::prelude::*; use crate::path::Path; use crate::random::{DefaultRandomSource, Random}; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::c; use crate::sys::fs::{File, OpenOptions}; use crate::sys::handle::Handle; @@ -192,7 +192,7 @@ pub fn spawn_pipe_relay( } fn random_number() -> usize { - static N: AtomicUsize = AtomicUsize::new(0); + static N: Atomic<usize> = AtomicUsize::new(0); loop { if N.load(Relaxed) != 0 { return N.fetch_add(1, Relaxed); diff --git a/library/std/src/sys/pal/windows/time.rs b/library/std/src/sys/pal/windows/time.rs index d9010e39961..68126bd8d2f 100644 --- a/library/std/src/sys/pal/windows/time.rs +++ b/library/std/src/sys/pal/windows/time.rs @@ -164,7 +164,7 @@ fn intervals2dur(intervals: u64) -> Duration { mod perf_counter { use super::NANOS_PER_SEC; - use crate::sync::atomic::{AtomicU64, Ordering}; + use crate::sync::atomic::{Atomic, AtomicU64, Ordering}; use crate::sys::{c, cvt}; use crate::sys_common::mul_div_u64; use crate::time::Duration; @@ -199,7 +199,7 @@ mod perf_counter { // uninitialized. Storing this as a single `AtomicU64` allows us to use // `Relaxed` operations, as we are only interested in the effects on a // single memory location. - static FREQUENCY: AtomicU64 = AtomicU64::new(0); + static FREQUENCY: Atomic<u64> = AtomicU64::new(0); let cached = FREQUENCY.load(Ordering::Relaxed); // If a previous thread has filled in this global state, use that. diff --git a/library/std/src/sys/pal/xous/os.rs b/library/std/src/sys/pal/xous/os.rs index 1b41575358f..2230dabe096 100644 --- a/library/std/src/sys/pal/xous/os.rs +++ b/library/std/src/sys/pal/xous/os.rs @@ -4,12 +4,12 @@ use crate::ffi::{OsStr, OsString}; use crate::marker::PhantomData; use crate::os::xous::ffi::Error as XousError; use crate::path::{self, PathBuf}; -use crate::sync::atomic::{AtomicPtr, Ordering}; +use crate::sync::atomic::{Atomic, AtomicPtr, Ordering}; use crate::{fmt, io}; pub(crate) mod params; -static PARAMS_ADDRESS: AtomicPtr<u8> = AtomicPtr::new(core::ptr::null_mut()); +static PARAMS_ADDRESS: Atomic<*mut u8> = AtomicPtr::new(core::ptr::null_mut()); #[cfg(not(test))] #[cfg(feature = "panic_unwind")] diff --git a/library/std/src/sys/process/unix/unix.rs b/library/std/src/sys/process/unix/unix.rs index 478f6583b1f..ae1c9558281 100644 --- a/library/std/src/sys/process/unix/unix.rs +++ b/library/std/src/sys/process/unix/unix.rs @@ -442,7 +442,7 @@ impl Command { envp: Option<&CStringArray>, ) -> io::Result<Option<Process>> { #[cfg(target_os = "linux")] - use core::sync::atomic::{AtomicU8, Ordering}; + use core::sync::atomic::{Atomic, AtomicU8, Ordering}; use crate::mem::MaybeUninit; use crate::sys::{self, cvt_nz, on_broken_pipe_flag_used}; @@ -475,7 +475,7 @@ impl Command { fn pidfd_getpid(pidfd: libc::c_int) -> libc::c_int; ); - static PIDFD_SUPPORTED: AtomicU8 = AtomicU8::new(0); + static PIDFD_SUPPORTED: Atomic<u8> = AtomicU8::new(0); const UNKNOWN: u8 = 0; const SPAWN: u8 = 1; // Obtaining a pidfd via the fork+exec path might work diff --git a/library/std/src/sys/random/linux.rs b/library/std/src/sys/random/linux.rs index c0591ec0c15..18196fae28b 100644 --- a/library/std/src/sys/random/linux.rs +++ b/library/std/src/sys/random/linux.rs @@ -64,8 +64,8 @@ use crate::fs::File; use crate::io::Read; use crate::os::fd::AsRawFd; use crate::sync::OnceLock; -use crate::sync::atomic::AtomicBool; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicBool}; use crate::sys::pal::os::errno; use crate::sys::pal::weak::syscall; @@ -81,9 +81,9 @@ fn getrandom(mut bytes: &mut [u8], insecure: bool) { ) -> libc::ssize_t; ); - static GETRANDOM_AVAILABLE: AtomicBool = AtomicBool::new(true); - static GRND_INSECURE_AVAILABLE: AtomicBool = AtomicBool::new(true); - static URANDOM_READY: AtomicBool = AtomicBool::new(false); + static GETRANDOM_AVAILABLE: Atomic<bool> = AtomicBool::new(true); + static GRND_INSECURE_AVAILABLE: Atomic<bool> = AtomicBool::new(true); + static URANDOM_READY: Atomic<bool> = AtomicBool::new(false); static DEVICE: OnceLock<File> = OnceLock::new(); if GETRANDOM_AVAILABLE.load(Relaxed) { diff --git a/library/std/src/sys/random/vxworks.rs b/library/std/src/sys/random/vxworks.rs index d549ccebdb2..14f02e8ecd2 100644 --- a/library/std/src/sys/random/vxworks.rs +++ b/library/std/src/sys/random/vxworks.rs @@ -1,7 +1,7 @@ -use crate::sync::atomic::AtomicBool; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicBool}; -static RNG_INIT: AtomicBool = AtomicBool::new(false); +static RNG_INIT: Atomic<bool> = AtomicBool::new(false); pub fn fill_bytes(mut bytes: &mut [u8]) { while !RNG_INIT.load(Relaxed) { diff --git a/library/std/src/sys/sync/condvar/pthread.rs b/library/std/src/sys/sync/condvar/pthread.rs index 5bb7431eecf..938b7071b88 100644 --- a/library/std/src/sys/sync/condvar/pthread.rs +++ b/library/std/src/sys/sync/condvar/pthread.rs @@ -2,15 +2,15 @@ use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::Relaxed; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::pal::sync as pal; use crate::sys::sync::{Mutex, OnceBox}; use crate::time::{Duration, Instant}; pub struct Condvar { cvar: OnceBox<pal::Condvar>, - mutex: AtomicUsize, + mutex: Atomic<usize>, } impl Condvar { diff --git a/library/std/src/sys/sync/condvar/xous.rs b/library/std/src/sys/sync/condvar/xous.rs index b9e5f47abfc..21a1587214a 100644 --- a/library/std/src/sys/sync/condvar/xous.rs +++ b/library/std/src/sys/sync/condvar/xous.rs @@ -1,4 +1,4 @@ -use core::sync::atomic::{AtomicUsize, Ordering}; +use core::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; @@ -11,8 +11,8 @@ use crate::time::Duration; const NOTIFY_TRIES: usize = 3; pub struct Condvar { - counter: AtomicUsize, - timed_out: AtomicUsize, + counter: Atomic<usize>, + timed_out: Atomic<usize>, } unsafe impl Send for Condvar {} diff --git a/library/std/src/sys/sync/mutex/fuchsia.rs b/library/std/src/sys/sync/mutex/fuchsia.rs index 3e871285bea..3d388a4564a 100644 --- a/library/std/src/sys/sync/mutex/fuchsia.rs +++ b/library/std/src/sys/sync/mutex/fuchsia.rs @@ -37,8 +37,8 @@ //! //! [mutex in Fuchsia's libsync]: https://cs.opensource.google/fuchsia/fuchsia/+/main:zircon/system/ulib/sync/mutex.c -use crate::sync::atomic::AtomicU32; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicU32}; use crate::sys::futex::zircon::{ ZX_ERR_BAD_HANDLE, ZX_ERR_BAD_STATE, ZX_ERR_INVALID_ARGS, ZX_ERR_TIMED_OUT, ZX_ERR_WRONG_TYPE, ZX_OK, ZX_TIME_INFINITE, zx_futex_wait, zx_futex_wake_single_owner, zx_handle_t, @@ -52,7 +52,7 @@ const CONTESTED_BIT: u32 = 1; const UNLOCKED: u32 = 0; pub struct Mutex { - futex: AtomicU32, + futex: Atomic<u32>, } #[inline] diff --git a/library/std/src/sys/sync/mutex/xous.rs b/library/std/src/sys/sync/mutex/xous.rs index c6b954c1711..d16faa5aea3 100644 --- a/library/std/src/sys/sync/mutex/xous.rs +++ b/library/std/src/sys/sync/mutex/xous.rs @@ -1,7 +1,7 @@ use crate::os::xous::ffi::{blocking_scalar, do_yield}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicBool, AtomicUsize}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize}; pub struct Mutex { /// The "locked" value indicates how many threads are waiting on this @@ -14,12 +14,12 @@ pub struct Mutex { /// for a lock, or it is locked for long periods of time. Rather than /// spinning, these locks send a Message to the ticktimer server /// requesting that they be woken up when a lock is unlocked. - locked: AtomicUsize, + locked: Atomic<usize>, /// Whether this Mutex ever was contended, and therefore made a trip /// to the ticktimer server. If this was never set, then we were never /// on the slow path and can skip deregistering the mutex. - contended: AtomicBool, + contended: Atomic<bool>, } impl Mutex { diff --git a/library/std/src/sys/sync/once/queue.rs b/library/std/src/sys/sync/once/queue.rs index fde1e0ca510..6a2ab0dcf1b 100644 --- a/library/std/src/sys/sync/once/queue.rs +++ b/library/std/src/sys/sync/once/queue.rs @@ -57,7 +57,7 @@ use crate::cell::Cell; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Release}; -use crate::sync::atomic::{AtomicBool, AtomicPtr}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; use crate::sync::poison::once::ExclusiveState; use crate::thread::{self, Thread}; use crate::{fmt, ptr, sync as public}; @@ -65,7 +65,7 @@ use crate::{fmt, ptr, sync as public}; type StateAndQueue = *mut (); pub struct Once { - state_and_queue: AtomicPtr<()>, + state_and_queue: Atomic<*mut ()>, } pub struct OnceState { @@ -94,7 +94,7 @@ const QUEUE_MASK: usize = !STATE_MASK; #[repr(align(4))] // Ensure the two lower bits are free to use as state bits. struct Waiter { thread: Thread, - signaled: AtomicBool, + signaled: Atomic<bool>, next: Cell<*const Waiter>, } @@ -102,7 +102,7 @@ struct Waiter { // Every node is a struct on the stack of a waiting thread. // Will wake up the waiters when it gets dropped, i.e. also on panic. struct WaiterQueue<'a> { - state_and_queue: &'a AtomicPtr<()>, + state_and_queue: &'a Atomic<*mut ()>, set_state_on_drop_to: StateAndQueue, } @@ -232,7 +232,7 @@ impl Once { } fn wait( - state_and_queue: &AtomicPtr<()>, + state_and_queue: &Atomic<*mut ()>, mut current: StateAndQueue, return_on_poisoned: bool, ) -> StateAndQueue { diff --git a/library/std/src/sys/sync/once_box.rs b/library/std/src/sys/sync/once_box.rs index 6953b91999a..088f51aae78 100644 --- a/library/std/src/sys/sync/once_box.rs +++ b/library/std/src/sys/sync/once_box.rs @@ -8,11 +8,11 @@ use crate::mem::replace; use crate::pin::Pin; use crate::ptr::null_mut; -use crate::sync::atomic::AtomicPtr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicPtr}; pub(crate) struct OnceBox<T> { - ptr: AtomicPtr<T>, + ptr: Atomic<*mut T>, } impl<T> OnceBox<T> { diff --git a/library/std/src/sys/sync/rwlock/queue.rs b/library/std/src/sys/sync/rwlock/queue.rs index bd15f8ee952..62f084acfd2 100644 --- a/library/std/src/sys/sync/rwlock/queue.rs +++ b/library/std/src/sys/sync/rwlock/queue.rs @@ -117,11 +117,11 @@ use crate::hint::spin_loop; use crate::mem; use crate::ptr::{self, NonNull, null_mut, without_provenance_mut}; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicBool, AtomicPtr}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr}; use crate::thread::{self, Thread}; /// The atomic lock state. -type AtomicState = AtomicPtr<()>; +type AtomicState = Atomic<State>; /// The inner lock state. type State = *mut (); @@ -181,11 +181,11 @@ struct Node { tail: AtomicLink, write: bool, thread: OnceCell<Thread>, - completed: AtomicBool, + completed: Atomic<bool>, } /// An atomic node pointer with relaxed operations. -struct AtomicLink(AtomicPtr<Node>); +struct AtomicLink(Atomic<*mut Node>); impl AtomicLink { fn new(v: Option<NonNull<Node>>) -> AtomicLink { diff --git a/library/std/src/sys/sync/thread_parking/darwin.rs b/library/std/src/sys/sync/thread_parking/darwin.rs index a0d24a91e7c..b9bcc538c65 100644 --- a/library/std/src/sys/sync/thread_parking/darwin.rs +++ b/library/std/src/sys/sync/thread_parking/darwin.rs @@ -13,8 +13,8 @@ #![allow(non_camel_case_types)] use crate::pin::Pin; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::time::Duration; type dispatch_semaphore_t = *mut crate::ffi::c_void; @@ -38,7 +38,7 @@ const PARKED: i8 = -1; pub struct Parker { semaphore: dispatch_semaphore_t, - state: AtomicI8, + state: Atomic<i8>, } unsafe impl Sync for Parker {} diff --git a/library/std/src/sys/sync/thread_parking/id.rs b/library/std/src/sys/sync/thread_parking/id.rs index 64964351837..fcc6ecca628 100644 --- a/library/std/src/sys/sync/thread_parking/id.rs +++ b/library/std/src/sys/sync/thread_parking/id.rs @@ -10,12 +10,12 @@ use crate::cell::UnsafeCell; use crate::pin::Pin; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicI8, fence}; +use crate::sync::atomic::{Atomic, AtomicI8, fence}; use crate::sys::thread_parking::{ThreadId, current, park, park_timeout, unpark}; use crate::time::Duration; pub struct Parker { - state: AtomicI8, + state: Atomic<i8>, tid: UnsafeCell<Option<ThreadId>>, } diff --git a/library/std/src/sys/sync/thread_parking/pthread.rs b/library/std/src/sys/sync/thread_parking/pthread.rs index 19cabd7dd75..14bc793c15d 100644 --- a/library/std/src/sys/sync/thread_parking/pthread.rs +++ b/library/std/src/sys/sync/thread_parking/pthread.rs @@ -1,8 +1,8 @@ //! Thread parking without `futex` using the `pthread` synchronization primitives. use crate::pin::Pin; -use crate::sync::atomic::AtomicUsize; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; +use crate::sync::atomic::{Atomic, AtomicUsize}; use crate::sys::pal::sync::{Condvar, Mutex}; use crate::time::Duration; @@ -11,7 +11,7 @@ const PARKED: usize = 1; const NOTIFIED: usize = 2; pub struct Parker { - state: AtomicUsize, + state: Atomic<usize>, lock: Mutex, cvar: Condvar, } diff --git a/library/std/src/sys/sync/thread_parking/windows7.rs b/library/std/src/sys/sync/thread_parking/windows7.rs index a1a0f8427cd..96e94a8053c 100644 --- a/library/std/src/sys/sync/thread_parking/windows7.rs +++ b/library/std/src/sys/sync/thread_parking/windows7.rs @@ -60,13 +60,13 @@ use core::ffi::c_void; use crate::pin::Pin; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::sys::{c, dur2timeout}; use crate::time::Duration; pub struct Parker { - state: AtomicI8, + state: Atomic<i8>, } const PARKED: i8 = -1; @@ -186,8 +186,8 @@ impl Parker { mod keyed_events { use core::pin::Pin; use core::ptr; - use core::sync::atomic::AtomicPtr; use core::sync::atomic::Ordering::{Acquire, Relaxed}; + use core::sync::atomic::{Atomic, AtomicPtr}; use core::time::Duration; use super::{EMPTY, NOTIFIED, Parker}; @@ -244,7 +244,7 @@ mod keyed_events { fn keyed_event_handle() -> c::HANDLE { const INVALID: c::HANDLE = ptr::without_provenance_mut(!0); - static HANDLE: AtomicPtr<crate::ffi::c_void> = AtomicPtr::new(INVALID); + static HANDLE: Atomic<*mut crate::ffi::c_void> = AtomicPtr::new(INVALID); match HANDLE.load(Relaxed) { INVALID => { let mut handle = c::INVALID_HANDLE_VALUE; diff --git a/library/std/src/sys/sync/thread_parking/xous.rs b/library/std/src/sys/sync/thread_parking/xous.rs index 28c90249dc2..0f451c0ac29 100644 --- a/library/std/src/sys/sync/thread_parking/xous.rs +++ b/library/std/src/sys/sync/thread_parking/xous.rs @@ -2,8 +2,8 @@ use crate::os::xous::ffi::{blocking_scalar, scalar}; use crate::os::xous::services::{TicktimerScalar, ticktimer_server}; use crate::pin::Pin; use crate::ptr; -use crate::sync::atomic::AtomicI8; use crate::sync::atomic::Ordering::{Acquire, Release}; +use crate::sync::atomic::{Atomic, AtomicI8}; use crate::time::Duration; const NOTIFIED: i8 = 1; @@ -11,7 +11,7 @@ const EMPTY: i8 = 0; const PARKED: i8 = -1; pub struct Parker { - state: AtomicI8, + state: Atomic<i8>, } impl Parker { diff --git a/library/std/src/sys/thread_local/key/racy.rs b/library/std/src/sys/thread_local/key/racy.rs index e1bc08eabb3..a12ff7ac36b 100644 --- a/library/std/src/sys/thread_local/key/racy.rs +++ b/library/std/src/sys/thread_local/key/racy.rs @@ -6,7 +6,7 @@ //! should be more lightweight and avoids circular dependencies with the rest of //! `std`. -use crate::sync::atomic::{self, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; /// A type for TLS keys that are statically allocated. /// @@ -14,7 +14,7 @@ use crate::sync::atomic::{self, AtomicUsize, Ordering}; /// dependencies with the rest of `std`. pub struct LazyKey { /// Inner static TLS key (internals). - key: AtomicUsize, + key: Atomic<usize>, /// Destructor for the TLS value. dtor: Option<unsafe extern "C" fn(*mut u8)>, } @@ -31,7 +31,7 @@ const KEY_SENTVAL: usize = libc::PTHREAD_KEYS_MAX + 1; impl LazyKey { pub const fn new(dtor: Option<unsafe extern "C" fn(*mut u8)>) -> LazyKey { - LazyKey { key: atomic::AtomicUsize::new(KEY_SENTVAL), dtor } + LazyKey { key: AtomicUsize::new(KEY_SENTVAL), dtor } } #[inline] diff --git a/library/std/src/sys/thread_local/key/windows.rs b/library/std/src/sys/thread_local/key/windows.rs index f4e0f25a476..c34c7bc204f 100644 --- a/library/std/src/sys/thread_local/key/windows.rs +++ b/library/std/src/sys/thread_local/key/windows.rs @@ -27,7 +27,7 @@ use crate::cell::UnsafeCell; use crate::ptr; use crate::sync::atomic::Ordering::{AcqRel, Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicPtr, AtomicU32}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicU32}; use crate::sys::c; use crate::sys::thread_local::guard; @@ -38,9 +38,9 @@ pub struct LazyKey { /// The key value shifted up by one. Since TLS_OUT_OF_INDEXES == u32::MAX /// is not a valid key value, this allows us to use zero as sentinel value /// without risking overflow. - key: AtomicU32, + key: Atomic<Key>, dtor: Option<Dtor>, - next: AtomicPtr<LazyKey>, + next: Atomic<*mut LazyKey>, /// Currently, destructors cannot be unregistered, so we cannot use racy /// initialization for keys. Instead, we need synchronize initialization. /// Use the Windows-provided `Once` since it does not require TLS. @@ -142,7 +142,7 @@ pub unsafe fn get(key: Key) -> *mut u8 { unsafe { c::TlsGetValue(key).cast() } } -static DTORS: AtomicPtr<LazyKey> = AtomicPtr::new(ptr::null_mut()); +static DTORS: Atomic<*mut LazyKey> = AtomicPtr::new(ptr::null_mut()); /// Should only be called once per key, otherwise loops or breaks may occur in /// the linked list. diff --git a/library/std/src/sys/thread_local/key/xous.rs b/library/std/src/sys/thread_local/key/xous.rs index 48dfe17ab32..a27cec5ca1a 100644 --- a/library/std/src/sys/thread_local/key/xous.rs +++ b/library/std/src/sys/thread_local/key/xous.rs @@ -42,7 +42,7 @@ use crate::mem::ManuallyDrop; use crate::os::xous::ffi::{MemoryFlags, map_memory, unmap_memory}; use crate::ptr; use crate::sync::atomic::Ordering::{Acquire, Relaxed, Release}; -use crate::sync::atomic::{AtomicPtr, AtomicUsize}; +use crate::sync::atomic::{Atomic, AtomicPtr, AtomicUsize}; pub type Key = usize; pub type Dtor = unsafe extern "C" fn(*mut u8); @@ -52,19 +52,19 @@ const TLS_MEMORY_SIZE: usize = 4096; /// TLS keys start at `1`. Index `0` is unused #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE")] -static TLS_KEY_INDEX: AtomicUsize = AtomicUsize::new(1); +static TLS_KEY_INDEX: Atomic<usize> = AtomicUsize::new(1); #[cfg(not(test))] #[unsafe(export_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE")] -static DTORS: AtomicPtr<Node> = AtomicPtr::new(ptr::null_mut()); +static DTORS: Atomic<*mut Node> = AtomicPtr::new(ptr::null_mut()); #[cfg(test)] unsafe extern "Rust" { #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key13TLS_KEY_INDEXE"] - static TLS_KEY_INDEX: AtomicUsize; + static TLS_KEY_INDEX: Atomic<usize>; #[link_name = "_ZN16__rust_internals3std3sys4xous16thread_local_key9DTORSE"] - static DTORS: AtomicPtr<Node>; + static DTORS: Atomic<*mut Node>; } fn tls_ptr_addr() -> *mut *mut u8 { diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 2097f1e304c..6838f15e174 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -166,7 +166,7 @@ use crate::mem::{self, ManuallyDrop, forget}; use crate::num::NonZero; use crate::pin::Pin; use crate::sync::Arc; -use crate::sync::atomic::{AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicUsize, Ordering}; use crate::sys::sync::Parker; use crate::sys::thread as imp; use crate::sys_common::{AsInner, IntoInner}; @@ -481,7 +481,7 @@ impl Builder { let Builder { name, stack_size, no_hooks } = self; let stack_size = stack_size.unwrap_or_else(|| { - static MIN: AtomicUsize = AtomicUsize::new(0); + static MIN: Atomic<usize> = AtomicUsize::new(0); match MIN.load(Ordering::Relaxed) { 0 => {} @@ -1195,9 +1195,9 @@ impl ThreadId { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { - use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::{Atomic, AtomicU64}; - static COUNTER: AtomicU64 = AtomicU64::new(0); + static COUNTER: Atomic<u64> = AtomicU64::new(0); let mut last = COUNTER.load(Ordering::Relaxed); loop { @@ -1302,10 +1302,10 @@ pub(crate) mod main_thread { cfg_if::cfg_if! { if #[cfg(target_has_atomic = "64")] { use super::ThreadId; - use crate::sync::atomic::AtomicU64; + use crate::sync::atomic::{Atomic, AtomicU64}; use crate::sync::atomic::Ordering::Relaxed; - static MAIN: AtomicU64 = AtomicU64::new(0); + static MAIN: Atomic<u64> = AtomicU64::new(0); pub(super) fn get() -> Option<ThreadId> { ThreadId::from_u64(MAIN.load(Relaxed)) @@ -1319,10 +1319,10 @@ pub(crate) mod main_thread { } else { use super::ThreadId; use crate::mem::MaybeUninit; - use crate::sync::atomic::AtomicBool; + use crate::sync::atomic::{Atomic, AtomicBool}; use crate::sync::atomic::Ordering::{Acquire, Release}; - static INIT: AtomicBool = AtomicBool::new(false); + static INIT: Atomic<bool> = AtomicBool::new(false); static mut MAIN: MaybeUninit<ThreadId> = MaybeUninit::uninit(); pub(super) fn get() -> Option<ThreadId> { diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 0033fc3a732..a4c0ca5417d 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -2,7 +2,7 @@ use super::{Builder, JoinInner, Result, Thread, current_or_unnamed}; use crate::marker::PhantomData; use crate::panic::{AssertUnwindSafe, catch_unwind, resume_unwind}; use crate::sync::Arc; -use crate::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; +use crate::sync::atomic::{Atomic, AtomicBool, AtomicUsize, Ordering}; use crate::{fmt, io}; /// A scope to spawn scoped threads in. @@ -35,8 +35,8 @@ pub struct Scope<'scope, 'env: 'scope> { pub struct ScopedJoinHandle<'scope, T>(JoinInner<'scope, T>); pub(super) struct ScopeData { - num_running_threads: AtomicUsize, - a_thread_panicked: AtomicBool, + num_running_threads: Atomic<usize>, + a_thread_panicked: Atomic<bool>, main_thread: Thread, } diff --git a/src/bootstrap/src/utils/tests/git.rs b/src/bootstrap/src/utils/tests/git.rs index bd40f398c7b..d9dd9ab9800 100644 --- a/src/bootstrap/src/utils/tests/git.rs +++ b/src/bootstrap/src/utils/tests/git.rs @@ -129,6 +129,9 @@ impl GitCtx { fn git_cmd(&self) -> Command { let mut cmd = Command::new("git"); + cmd.env("GIT_CONFIG_NOSYSTEM", "1"); + cmd.env("GIT_CONFIG_SYSTEM", "/tmp/nonexistent"); + cmd.env("GIT_CONFIG_GLOBAL", "/tmp/nonexistent"); cmd.current_dir(&self.dir); cmd } diff --git a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile index ae5bf8946dd..bedf45c8630 100644 --- a/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile +++ b/src/ci/docker/host-x86_64/dist-x86_64-linux/Dockerfile @@ -2,7 +2,7 @@ # CentOS 7 has headers for kernel 3.10, but that's fine as long as we don't # actually use newer APIs in rustc or std without a fallback. It's more # important that we match glibc for ELF symbol versioning. -FROM centos:7 +FROM ghcr.io/rust-lang/centos:7 WORKDIR /build diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock index dacf531e404..8b6a664ad93 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.lock @@ -1,12 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "r-efi" -version = "4.5.0" +version = "5.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9e935efc5854715dfc0a4c9ef18dc69dee0ec3bf9cc3ab740db831c0fdd86a3" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" [[package]] name = "uefi_qemu_test" diff --git a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml index 976245f5bdd..1a8d0d94368 100644 --- a/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml +++ b/src/ci/docker/host-x86_64/test-various/uefi_qemu_test/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" resolver = "2" [dependencies] -r-efi = "4.1.0" +r-efi = "5.2.0" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 88b29d2df56..afcc092e78e 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -65,6 +65,12 @@ runners: os: codebuild-ubuntu-22-36c-$github.run_id-$github.run_attempt <<: *base-job + - &job-linux-8c-codebuild + free_disk: true + codebuild: true + os: codebuild-ubuntu-22-8c-$github.run_id-$github.run_attempt + <<: *base-job + envs: env-x86_64-apple-tests: &env-x86_64-apple-tests SCRIPT: ./x.py check compiletest --set build.compiletest-use-stage0-libtest=true && ./x.py --stage 2 test --skip tests/ui --skip tests/rustdoc -- --exact @@ -160,7 +166,7 @@ auto: <<: *job-linux-4c - name: dist-arm-linux - <<: *job-linux-36c-codebuild + <<: *job-linux-8c-codebuild - name: dist-armhf-linux <<: *job-linux-4c @@ -213,7 +219,7 @@ auto: - name: dist-x86_64-linux env: CODEGEN_BACKENDS: llvm,cranelift - <<: *job-linux-16c + <<: *job-linux-36c-codebuild - name: dist-x86_64-linux-alt env: diff --git a/src/ci/run.sh b/src/ci/run.sh index 6980d8220e5..b6143af632d 100755 --- a/src/ci/run.sh +++ b/src/ci/run.sh @@ -183,6 +183,9 @@ else RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set llvm.static-libstdcpp" fi + # Download GCC from CI on test builders + RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set gcc.download-ci-gcc=true" + if [ "$NO_DOWNLOAD_CI_RUSTC" = "" ]; then RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set rust.download-rustc=if-unchanged" fi diff --git a/src/ci/scripts/free-disk-space.sh b/src/ci/scripts/free-disk-space.sh index ad7ee136e9c..173f64858b3 100755 --- a/src/ci/scripts/free-disk-space.sh +++ b/src/ci/scripts/free-disk-space.sh @@ -87,59 +87,80 @@ printDF() { removeUnusedFilesAndDirs() { local to_remove=( - "/usr/local/aws-sam-cli" - "/usr/local/doc/cmake" - "/usr/local/julia"* - "/usr/local/lib/android" - "/usr/local/share/chromedriver-"* - "/usr/local/share/chromium" - "/usr/local/share/cmake-"* - "/usr/local/share/edge_driver" - "/usr/local/share/gecko_driver" - "/usr/local/share/icons" - "/usr/local/share/vim" - "/usr/local/share/emacs" - "/usr/local/share/powershell" - "/usr/local/share/vcpkg" - "/usr/share/apache-maven-"* - "/usr/share/gradle-"* "/usr/share/java" - "/usr/share/kotlinc" - "/usr/share/miniconda" - "/usr/share/php" - "/usr/share/ri" - "/usr/share/swift" - - # binaries - "/usr/local/bin/azcopy" - "/usr/local/bin/bicep" - "/usr/local/bin/ccmake" - "/usr/local/bin/cmake-"* - "/usr/local/bin/cmake" - "/usr/local/bin/cpack" - "/usr/local/bin/ctest" - "/usr/local/bin/helm" - "/usr/local/bin/kind" - "/usr/local/bin/kustomize" - "/usr/local/bin/minikube" - "/usr/local/bin/packer" - "/usr/local/bin/phpunit" - "/usr/local/bin/pulumi-"* - "/usr/local/bin/pulumi" - "/usr/local/bin/stack" - - # Haskell runtime - "/usr/local/.ghcup" - - # Azure - "/opt/az" - "/usr/share/az_"* ) - if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then - # Environment variable set by GitHub Actions + if isGitHubRunner; then + to_remove+=( + "/usr/local/aws-sam-cli" + "/usr/local/doc/cmake" + "/usr/local/julia"* + "/usr/local/lib/android" + "/usr/local/share/chromedriver-"* + "/usr/local/share/chromium" + "/usr/local/share/cmake-"* + "/usr/local/share/edge_driver" + "/usr/local/share/emacs" + "/usr/local/share/gecko_driver" + "/usr/local/share/icons" + "/usr/local/share/powershell" + "/usr/local/share/vcpkg" + "/usr/local/share/vim" + "/usr/share/apache-maven-"* + "/usr/share/gradle-"* + "/usr/share/kotlinc" + "/usr/share/miniconda" + "/usr/share/php" + "/usr/share/ri" + "/usr/share/swift" + + # binaries + "/usr/local/bin/azcopy" + "/usr/local/bin/bicep" + "/usr/local/bin/ccmake" + "/usr/local/bin/cmake-"* + "/usr/local/bin/cmake" + "/usr/local/bin/cpack" + "/usr/local/bin/ctest" + "/usr/local/bin/helm" + "/usr/local/bin/kind" + "/usr/local/bin/kustomize" + "/usr/local/bin/minikube" + "/usr/local/bin/packer" + "/usr/local/bin/phpunit" + "/usr/local/bin/pulumi-"* + "/usr/local/bin/pulumi" + "/usr/local/bin/stack" + + # Haskell runtime + "/usr/local/.ghcup" + + # Azure + "/opt/az" + "/usr/share/az_"* + ) + + if [ -n "${AGENT_TOOLSDIRECTORY:-}" ]; then + # Environment variable set by GitHub Actions + to_remove+=( + "${AGENT_TOOLSDIRECTORY}" + ) + else + echo "::warning::AGENT_TOOLSDIRECTORY is not set. Skipping removal." + fi + else + # Remove folders and files present in AWS CodeBuild to_remove+=( - "${AGENT_TOOLSDIRECTORY}" + # binaries + "/usr/local/bin/ecs-cli" + "/usr/local/bin/eksctl" + "/usr/local/bin/kubectl" + + "${HOME}/.gradle" + "${HOME}/.dotnet" + "${HOME}/.goenv" + "${HOME}/.phpenv" + ) fi @@ -194,6 +215,10 @@ cleanPackages() { 'powershell' ) fi + else + packages+=( + 'google-chrome-stable' + ) fi sudo apt-get -qq remove -y --fix-missing "${packages[@]}" diff --git a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml index b19eccf9eb8..1e430d8b4e6 100644 --- a/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml +++ b/src/doc/rustc-dev-guide/.github/workflows/rustc-pull.yml @@ -28,7 +28,7 @@ jobs: # Cache the josh directory with checked out rustc cache-directories: "/home/runner/.cache/rustc-dev-guide-josh" - name: Install josh - run: RUSTFLAGS="--cap-lints warn" cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 + run: RUSTFLAGS="--cap-lints warn" cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 - name: Setup bot git name and email run: | git config --global user.name 'The rustc-dev-guide Cronjob Bot' diff --git a/src/doc/rustc-dev-guide/README.md b/src/doc/rustc-dev-guide/README.md index 9f468b933d7..08158801788 100644 --- a/src/doc/rustc-dev-guide/README.md +++ b/src/doc/rustc-dev-guide/README.md @@ -77,7 +77,7 @@ This repository is linked to `rust-lang/rust` as a [josh](https://josh-project.g You'll need to install `josh-proxy` locally via ``` -cargo +stable install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 +cargo install josh-proxy --git https://github.com/josh-project/josh --tag r24.10.04 ``` Older versions of `josh-proxy` may not round trip commits losslessly so it is important to install this exact version. @@ -106,5 +106,5 @@ You may observe "Nothing to pull" even if you *know* rustc-pull has something to To minimize the likelihood of this happening, you may wish to keep a separate *minimal* git config that *only* has `[user]` entries from global git config, then repoint system git to use the minimal git config instead. E.g. ``` -GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' cargo +stable run --manifest-path josh-sync/Cargo.toml -- rustc-pull +GIT_CONFIG_GLOBAL=/path/to/minimal/gitconfig GIT_CONFIG_SYSTEM='' cargo run --manifest-path josh-sync/Cargo.toml -- rustc-pull ``` diff --git a/src/doc/rustc-dev-guide/rust-version b/src/doc/rustc-dev-guide/rust-version index dc52e0928cc..67fa25f2228 100644 --- a/src/doc/rustc-dev-guide/rust-version +++ b/src/doc/rustc-dev-guide/rust-version @@ -1 +1 @@ -b8005bff3248cfc6e327faf4fa631ac49bb49ba9 +deb947971c8748f5c6203548ce4af9022f21eaf0 diff --git a/src/doc/rustc-dev-guide/src/SUMMARY.md b/src/doc/rustc-dev-guide/src/SUMMARY.md index 68112d06167..d6ec803a60a 100644 --- a/src/doc/rustc-dev-guide/src/SUMMARY.md +++ b/src/doc/rustc-dev-guide/src/SUMMARY.md @@ -1,6 +1,7 @@ # Summary [Getting Started](./getting-started.md) + [About this guide](./about-this-guide.md) --- @@ -231,9 +232,13 @@ --- [Appendix A: Background topics](./appendix/background.md) + [Appendix B: Glossary](./appendix/glossary.md) + [Appendix C: Code Index](./appendix/code-index.md) + [Appendix D: Compiler Lecture Series](./appendix/compiler-lecture.md) + [Appendix E: Bibliography](./appendix/bibliography.md) [Appendix Z: HumorRust](./appendix/humorust.md) diff --git a/src/doc/rustc-dev-guide/src/autodiff/installation.md b/src/doc/rustc-dev-guide/src/autodiff/installation.md index dbea9dbe144..f3c11395523 100644 --- a/src/doc/rustc-dev-guide/src/autodiff/installation.md +++ b/src/doc/rustc-dev-guide/src/autodiff/installation.md @@ -25,9 +25,10 @@ rustup toolchain install nightly # enables -Z unstable-options You can then run our test cases: ```bash -./x.py test --stage 1 library tests/ui/autodiff -./x.py test --stage 1 library tests/codegen/autodiff -./x.py test --stage 1 library tests/pretty/autodiff* +./x.py test --stage 1 tests/codegen/autodiff +./x.py test --stage 1 tests/pretty/autodiff +./x.py test --stage 1 tests/ui/autodiff +./x.py test --stage 1 tests/ui/feature-gates/feature-gate-autodiff.rs ``` Autodiff is still experimental, so if you want to use it in your own projects, you will need to add `lto="fat"` to your Cargo.toml @@ -44,7 +45,7 @@ apt install wget vim python3 git curl libssl-dev pkg-config lld ninja-build cmak ``` Then build rustc in a slightly altered way: ```bash -git clone --depth=1 https://github.com/EnzymeAD/rust.git +git clone --depth=1 https://github.com/rust-lang/rust.git cd rust ./configure --enable-llvm-link-shared --enable-llvm-plugins --enable-llvm-enzyme --release-channel=nightly --enable-llvm-assertions --enable-clang --enable-lld --enable-option-checking --enable-ninja --disable-docs ./x dist @@ -54,7 +55,8 @@ We then copy the tarball to our host. The dockerid is the newest entry under `do docker cp <dockerid>:/rust/build/dist/rust-nightly-x86_64-unknown-linux-gnu.tar.gz rust-nightly-x86_64-unknown-linux-gnu.tar.gz ``` Afterwards we can create a new (pre-release) tag on the EnzymeAD/rust repository and make a PR against the EnzymeAD/enzyme-explorer repository to update the tag. -Remember to ping `tgymnich` on the PR to run his update script. +Remember to ping `tgymnich` on the PR to run his update script. Note: We should archive EnzymeAD/rust and update the instructions here. The explorer should soon +be able to get the rustc toolchain from the official rust servers. ## Build instruction for Enzyme itself diff --git a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md index 1dd5402f4cd..ffcfe259625 100644 --- a/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md +++ b/src/doc/rustc-dev-guide/src/building/bootstrapping/what-bootstrapping-does.md @@ -394,8 +394,8 @@ will be rare to want to use it. Finally, `MAGIC_EXTRA_RUSTFLAGS` bypasses the this is `compiletest`. For unit tests and doc tests this is the `libtest` runner. -Most test runner accept `--help`, which you can use to find out the options -accepted by the runner. +Most test runners accept `--help`, +which you can use to find out the options accepted by the runner. ## Environment Variables diff --git a/src/doc/rustc-dev-guide/src/git.md b/src/doc/rustc-dev-guide/src/git.md index 177495b5362..8118ddff10c 100644 --- a/src/doc/rustc-dev-guide/src/git.md +++ b/src/doc/rustc-dev-guide/src/git.md @@ -38,13 +38,13 @@ If you've cloned your fork, then you will be able to reference it with `origin` in your local repo. It may be helpful to also set up a remote for the official rust-lang/rust repo via -```sh +```console git remote add upstream https://github.com/rust-lang/rust.git ``` if you're using HTTPS, or -```sh +```console git remote add upstream git@github.com:rust-lang/rust.git ``` @@ -112,7 +112,7 @@ See [Rebasing](#rebasing) for more about rebasing. This is not a problem from git's perspective. If you run `git remote -v`, it will say something like this: -``` +```console $ git remote -v origin git@github.com:jyn514/rust.git (fetch) origin git@github.com:jyn514/rust.git (push) @@ -158,11 +158,11 @@ To fix it, do the following things: ### I see "error: cannot rebase" when I try to rebase These are two common errors to see when rebasing: -``` +```console error: cannot rebase: Your index contains uncommitted changes. error: Please commit or stash them. ``` -``` +```console error: cannot rebase: You have unstaged changes. error: Please commit or stash them. ``` @@ -174,7 +174,7 @@ commit your changes, or make a temporary commit called a "stash" to have them st when you finish rebasing. You may want to configure git to make this "stash" automatically, which will prevent the "cannot rebase" error in nearly all cases: -``` +```console git config --global rebase.autostash true ``` @@ -205,7 +205,7 @@ git reset --hard master `git push` will not work properly and say something like this: -``` +```console ! [rejected] issue-xxxxx -> issue-xxxxx (non-fast-forward) error: failed to push some refs to 'https://github.com/username/rust.git' hint: Updates were rejected because the tip of your current branch is behind @@ -226,7 +226,7 @@ didn't write, it likely means you're trying to rebase over the wrong branch. For have a `rust-lang/rust` remote `upstream`, but ran `git rebase origin/master` instead of `git rebase upstream/master`. The fix is to abort the rebase and use the correct branch instead: -``` +```console git rebase --abort git rebase -i upstream/master ``` @@ -243,7 +243,7 @@ When updating your local repository with `git pull`, you may notice that sometim Git says you have modified some files that you have never edited. For example, running `git status` gives you something like (note the `new commits` mention): -``` +```console On branch master Your branch is up to date with 'origin/master'. @@ -256,9 +256,12 @@ Changes not staged for commit: no changes added to commit (use "git add" and/or "git commit -a") ``` -These changes are not changes to files: they are changes to submodules (more on this -[later](#git-submodules)). To get rid of those, run `./x --help`, which will automatically update -the submodules. +These changes are not changes to files: they are changes to submodules (more on this [later](#git-submodules)). +To get rid of those: + +```console +git submodule update +``` Some submodules are not actually needed; for example, `src/llvm-project` doesn't need to be checked out if you're using `download-ci-llvm`. To avoid having to keep fetching its history, you can use @@ -278,12 +281,12 @@ merged. To do that, you need to rebase your work on top of rust-lang/rust. To rebase your feature branch on top of the newest version of the master branch of rust-lang/rust, checkout your branch, and then run this command: -``` +```console git pull --rebase https://github.com/rust-lang/rust.git master ``` > If you are met with the following error: -> ``` +> ```console > error: cannot pull with rebase: Your index contains uncommitted changes. > error: please commit or stash them. > ``` @@ -300,13 +303,13 @@ reapply the changes fails because your changes conflicted with other changes that have been made. You can tell that this happened because you'll see lines in the output that look like -``` +```console CONFLICT (content): Merge conflict in file.rs ``` When you open these files, you'll see sections of the form -``` +```console <<<<<<< HEAD Original code ======= @@ -346,7 +349,7 @@ will keep it up-to-date. You will also want to rebase your feature branches up-to-date as well. After pulling, you can checkout the feature branches and rebase them: -``` +```console git checkout master git pull upstream master --ff-only # to make certain there are no merge commits git rebase master feature_branch @@ -384,7 +387,7 @@ change the order in which they are applied, or "squash" them into each other. Alternatively, you can sacrifice the commit history like this: -``` +```console # squash all the changes into one commit so you only have to worry about conflicts once git rebase -i --keep-base master # and squash all changes along the way git rebase master @@ -422,7 +425,7 @@ it shows you the differences between your old diff and your new diff. Here's an example of `git range-diff` output (taken from [Git's docs][range-diff-example-docs]): -``` +```console -: ------- > 1: 0ddba11 Prepare for the inevitable! 1: c0debee = 2: cab005e Add a helpful message at the start 2: f00dbal ! 3: decafe1 Describe a bug @@ -499,7 +502,7 @@ Git and Github's default diff view for large moves *within* a file is quite poor line as deleted and each line as added, forcing you to compare each line yourself. Git has an option to show moved lines in a different color: -``` +```console git log -p --color-moved=dimmed-zebra --color-moved-ws=allow-indentation-change ``` @@ -515,7 +518,7 @@ that was force-pushed to make sure there are no unexpected changes. Many large files in the repo are autogenerated. To view a diff that ignores changes to those files, you can use the following syntax (e.g. Cargo.lock): -``` +```console git log -p ':!Cargo.lock' ``` @@ -545,7 +548,7 @@ The contents of submodules are ignored by Git: submodules are in some sense isol from the rest of the repository. However, if you try to `cd src/llvm-project` and then run `git status`: -``` +```console HEAD detached at 9567f08afc943 nothing to commit, working tree clean ``` @@ -576,7 +579,7 @@ that Git can nicely and fairly conveniently handle for us. Sometimes you might run into (when you run `git status`) -``` +```console Changes not staged for commit: (use "git add <file>..." to update what will be committed) (use "git restore <file>..." to discard changes in working directory) @@ -586,7 +589,7 @@ Changes not staged for commit: and when you try to run `git submodule update` it breaks horribly with errors like -``` +```console error: RPC failed; curl 92 HTTP/2 stream 7 was not closed cleanly: CANCEL (err 8) error: 2782 bytes of body are still expected fetch-pack: unexpected disconnect while reading sideband packet @@ -597,8 +600,8 @@ fatal: Fetched in submodule path 'src/llvm-project', but it did not contain 5a51 If you see `(new commits, modified content)` you can run -```bash -$ git submodule foreach git reset --hard +```console +git submodule foreach git reset --hard ``` and then try `git submodule update` again. @@ -607,7 +610,7 @@ and then try `git submodule update` again. If that doesn't work, you can try to deinit all git submodules... -``` +```console git submodule deinit -f --all ``` @@ -618,7 +621,7 @@ completely messed up for some reason. Sometimes, for some forsaken reason, you might run into -```text +```console fatal: not a git repository: src/gcc/../../.git/modules/src/gcc ``` diff --git a/src/doc/unstable-book/src/language-features/deref-patterns.md b/src/doc/unstable-book/src/language-features/deref-patterns.md index d0a64538e8c..0cc7106da48 100644 --- a/src/doc/unstable-book/src/language-features/deref-patterns.md +++ b/src/doc/unstable-book/src/language-features/deref-patterns.md @@ -7,7 +7,7 @@ The tracking issue for this feature is: [#87121] ------------------------ > **Note**: This feature is incomplete. In the future, it is meant to supersede -> [`box_patterns`](./box-patterns.md) and [`string_deref_patterns`](./string-deref-patterns.md). +> [`box_patterns`] and [`string_deref_patterns`]. This feature permits pattern matching on [smart pointers in the standard library] through their `Deref` target types, either implicitly or with explicit `deref!(_)` patterns (the syntax of which @@ -54,6 +54,17 @@ if let [b] = &mut *v { assert_eq!(v, [Box::new(Some(2))]); ``` +Like [`box_patterns`], deref patterns may move out of boxes: + +```rust +# #![feature(deref_patterns)] +# #![allow(incomplete_features)] +struct NoCopy; +// Match exhaustiveness analysis is not yet implemented. +let deref!(x) = Box::new(NoCopy) else { unreachable!() }; +drop::<NoCopy>(x); +``` + Additionally, when `deref_patterns` is enabled, string literal patterns may be written where `str` is expected. Likewise, byte string literal patterns may be written where `[u8]` or `[u8; _]` is expected. This lets them be used in `deref!(_)` patterns: @@ -75,4 +86,6 @@ match *"test" { Implicit deref pattern syntax is not yet supported for string or byte string literals. +[`box_patterns`]: ./box-patterns.md +[`string_deref_patterns`]: ./string-deref-patterns.md [smart pointers in the standard library]: https://doc.rust-lang.org/std/ops/trait.DerefPure.html#implementors diff --git a/src/librustdoc/clean/render_macro_matchers.rs b/src/librustdoc/clean/render_macro_matchers.rs index fc99dd08b78..e967fd40609 100644 --- a/src/librustdoc/clean/render_macro_matchers.rs +++ b/src/librustdoc/clean/render_macro_matchers.rs @@ -34,20 +34,20 @@ pub(super) fn render_macro_matcher(tcx: TyCtxt<'_>, matcher: &TokenTree) -> Stri // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~! // ) => {...}; // } - printer.cbox(8); + let cb = printer.cbox(8); printer.word("("); printer.zerobreak(); - printer.ibox(0); + let ib = printer.ibox(0); match matcher { TokenTree::Delimited(_span, _spacing, _delim, tts) => print_tts(&mut printer, tts), // Matcher which is not a Delimited is unexpected and should've failed // to compile, but we render whatever it is wrapped in parens. TokenTree::Token(..) => print_tt(&mut printer, matcher), } - printer.end(); + printer.end(ib); printer.break_offset_if_not_bol(0, -4); printer.word(")"); - printer.end(); + printer.end(cb); printer.s.eof() } diff --git a/src/librustdoc/doctest/make.rs b/src/librustdoc/doctest/make.rs index d5c965f7053..4194abc8d57 100644 --- a/src/librustdoc/doctest/make.rs +++ b/src/librustdoc/doctest/make.rs @@ -407,17 +407,27 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn push_to_s(&mut info.crate_attrs, source, attr.span, &mut prev_span_hi); } } + let mut has_non_items = false; for stmt in &body.stmts { let mut is_extern_crate = false; match stmt.kind { StmtKind::Item(ref item) => { is_extern_crate = check_item(&item, &mut info, crate_name); } - StmtKind::Expr(ref expr) if matches!(expr.kind, ast::ExprKind::Err(_)) => { - reset_error_count(&psess); - return Err(()); + StmtKind::Expr(ref expr) => { + if matches!(expr.kind, ast::ExprKind::Err(_)) { + reset_error_count(&psess); + return Err(()); + } + has_non_items = true; } - StmtKind::MacCall(ref mac_call) if !info.has_main_fn => { + // We assume that the macro calls will expand to item(s) even though they could + // expand to statements and expressions. And the simple fact that we're trying + // to retrieve a `main` function inside it is a terrible idea. + StmtKind::MacCall(ref mac_call) => { + if info.has_main_fn { + continue; + } let mut iter = mac_call.mac.args.tokens.iter(); while let Some(token) = iter.next() { @@ -437,7 +447,9 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn } } } - _ => {} + _ => { + has_non_items = true; + } } // Weirdly enough, the `Stmt` span doesn't include its attributes, so we need to @@ -462,6 +474,11 @@ fn parse_source(source: &str, crate_name: &Option<&str>) -> Result<ParseSourceIn push_to_s(&mut info.crates, source, span, &mut prev_span_hi); } } + if has_non_items { + // FIXME: if `info.has_main_fn` is `true`, emit a warning here to mention that + // this code will not be called. + info.has_main_fn = false; + } Ok(info) } Err(e) => { diff --git a/src/librustdoc/doctest/runner.rs b/src/librustdoc/doctest/runner.rs index 784d628805d..39a4f23560a 100644 --- a/src/librustdoc/doctest/runner.rs +++ b/src/librustdoc/doctest/runner.rs @@ -131,7 +131,22 @@ mod __doctest_mod {{ .output() .expect(\"failed to run command\"); if !out.status.success() {{ - eprint!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + if let Some(code) = out.status.code() {{ + eprintln!(\"Test executable failed (exit status: {{code}}).\"); + }} else {{ + eprintln!(\"Test executable failed (terminated by signal).\"); + }} + if !out.stdout.is_empty() || !out.stderr.is_empty() {{ + eprintln!(); + }} + if !out.stdout.is_empty() {{ + eprintln!(\"stdout:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stdout)); + }} + if !out.stderr.is_empty() {{ + eprintln!(\"stderr:\"); + eprintln!(\"{{}}\", String::from_utf8_lossy(&out.stderr)); + }} ExitCode::FAILURE }} else {{ ExitCode::SUCCESS diff --git a/src/tools/cargo b/src/tools/cargo -Subproject d811228b14ae2707323f37346aee3f4147e247e +Subproject 7918c7eb59614c39f1c4e27e99d557720976bdd diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr index bdac1e42309..ad3c420270c 100644 --- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr +++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr @@ -236,7 +236,7 @@ LL | if result.is_ok() { LL | result.as_mut().unwrap(); | ^^^^^^^^^^^^^^^^^^^^^^^^ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> tests/ui/checked_unwrap/simple_conditionals.rs:183:12 | LL | if X.is_some() { diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml index ba1b8f25658..93f7b1cb7cf 100644 --- a/src/tools/compiletest/Cargo.toml +++ b/src/tools/compiletest/Cargo.toml @@ -18,6 +18,7 @@ glob = "0.3.0" home = "0.5.5" indexmap = "2.0.0" miropt-test-tools = { path = "../miropt-test-tools" } +rayon = "1.10.0" regex = "1.0" rustfix = "0.8.1" semver = { version = "1.0.23", features = ["serde"] } diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index b5bbe70c48c..e0132056d6c 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -414,10 +414,13 @@ pub struct Config { /// ABI tests. pub minicore_path: Utf8PathBuf, - /// If true, run tests with the "new" executor that was written to replace - /// compiletest's dependency on libtest. Eventually this will become the - /// default, and the libtest dependency will be removed. - pub new_executor: bool, + /// If true, disable the "new" executor, and use the older libtest-based + /// executor to run tests instead. This is a temporary fallback, to make + /// manual comparative testing easier if bugs are found in the new executor. + /// + /// FIXME(Zalathar): Eventually remove this flag and remove the libtest + /// dependency. + pub no_new_executor: bool, } impl Config { diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs index 3cf13671ef0..788bafaa724 100644 --- a/src/tools/compiletest/src/lib.rs +++ b/src/tools/compiletest/src/lib.rs @@ -33,6 +33,7 @@ use std::{env, fs, vec}; use build_helper::git::{get_git_modified_files, get_git_untracked_files}; use camino::{Utf8Path, Utf8PathBuf}; use getopts::Options; +use rayon::iter::{ParallelBridge, ParallelIterator}; use tracing::*; use walkdir::WalkDir; @@ -202,7 +203,7 @@ pub fn parse_config(args: Vec<String>) -> Config { "COMMAND", ) .reqopt("", "minicore-path", "path to minicore aux library", "PATH") - .optflag("n", "new-executor", "enables the new test executor instead of using libtest") + .optflag("N", "no-new-executor", "disables the new test executor, and uses libtest instead") .optopt( "", "debugger", @@ -448,7 +449,7 @@ pub fn parse_config(args: Vec<String>) -> Config { minicore_path: opt_path(matches, "minicore-path"), - new_executor: matches.opt_present("new-executor"), + no_new_executor: matches.opt_present("no-new-executor"), } } @@ -575,9 +576,10 @@ pub fn run_tests(config: Arc<Config>) { // Delegate to the executor to filter and run the big list of test structures // created during test discovery. When the executor decides to run a test, // it will return control to the rest of compiletest by calling `runtest::run`. - let res = if config.new_executor { + let res = if !config.no_new_executor { Ok(executor::run_tests(&config, tests)) } else { + // FIXME(Zalathar): Eventually remove the libtest executor entirely. crate::executor::libtest::execute_tests(&config, tests) }; @@ -638,6 +640,18 @@ struct TestCollector { poisoned: bool, } +impl TestCollector { + fn new() -> Self { + TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false } + } + + fn merge(&mut self, mut other: Self) { + self.tests.append(&mut other.tests); + self.found_path_stems.extend(other.found_path_stems); + self.poisoned |= other.poisoned; + } +} + /// Creates test structures for every test/revision in the test suite directory. /// /// This always inspects _all_ test files in the suite (e.g. all 17k+ ui tests), @@ -656,10 +670,7 @@ pub(crate) fn collect_and_make_tests(config: Arc<Config>) -> Vec<CollectedTest> let cache = HeadersCache::load(&config); let cx = TestCollectorCx { config, cache, common_inputs_stamp, modified_tests }; - let mut collector = - TestCollector { tests: vec![], found_path_stems: HashSet::new(), poisoned: false }; - - collect_tests_from_dir(&cx, &mut collector, &cx.config.src_test_suite_root, Utf8Path::new("")) + let collector = collect_tests_from_dir(&cx, &cx.config.src_test_suite_root, Utf8Path::new("")) .unwrap_or_else(|reason| { panic!("Could not read tests from {}: {reason}", cx.config.src_test_suite_root) }); @@ -765,25 +776,25 @@ fn modified_tests(config: &Config, dir: &Utf8Path) -> Result<Vec<Utf8PathBuf>, S /// that will be handed over to libtest. fn collect_tests_from_dir( cx: &TestCollectorCx, - collector: &mut TestCollector, dir: &Utf8Path, relative_dir_path: &Utf8Path, -) -> io::Result<()> { +) -> io::Result<TestCollector> { // Ignore directories that contain a file named `compiletest-ignore-dir`. if dir.join("compiletest-ignore-dir").exists() { - return Ok(()); + return Ok(TestCollector::new()); } // For run-make tests, a "test file" is actually a directory that contains an `rmake.rs`. if cx.config.mode == Mode::RunMake { + let mut collector = TestCollector::new(); if dir.join("rmake.rs").exists() { let paths = TestPaths { file: dir.to_path_buf(), relative_dir: relative_dir_path.parent().unwrap().to_path_buf(), }; - make_test(cx, collector, &paths); + make_test(cx, &mut collector, &paths); // This directory is a test, so don't try to find other tests inside it. - return Ok(()); + return Ok(collector); } } @@ -800,36 +811,47 @@ fn collect_tests_from_dir( // subdirectories we find, except for `auxiliary` directories. // FIXME: this walks full tests tree, even if we have something to ignore // use walkdir/ignore like in tidy? - for file in fs::read_dir(dir.as_std_path())? { - let file = file?; - let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); - let file_name = file_path.file_name().unwrap(); - - if is_test(file_name) - && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) - { - // We found a test file, so create the corresponding libtest structures. - debug!(%file_path, "found test file"); - - // Record the stem of the test file, to check for overlaps later. - let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); - collector.found_path_stems.insert(rel_test_path); - - let paths = - TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; - make_test(cx, collector, &paths); - } else if file_path.is_dir() { - // Recurse to find more tests in a subdirectory. - let relative_file_path = relative_dir_path.join(file_name); - if file_name != "auxiliary" { - debug!(%file_path, "found directory"); - collect_tests_from_dir(cx, collector, &file_path, &relative_file_path)?; + fs::read_dir(dir.as_std_path())? + .par_bridge() + .map(|file| { + let mut collector = TestCollector::new(); + let file = file?; + let file_path = Utf8PathBuf::try_from(file.path()).unwrap(); + let file_name = file_path.file_name().unwrap(); + + if is_test(file_name) + && (!cx.config.only_modified || cx.modified_tests.contains(&file_path)) + { + // We found a test file, so create the corresponding libtest structures. + debug!(%file_path, "found test file"); + + // Record the stem of the test file, to check for overlaps later. + let rel_test_path = relative_dir_path.join(file_path.file_stem().unwrap()); + collector.found_path_stems.insert(rel_test_path); + + let paths = + TestPaths { file: file_path, relative_dir: relative_dir_path.to_path_buf() }; + make_test(cx, &mut collector, &paths); + } else if file_path.is_dir() { + // Recurse to find more tests in a subdirectory. + let relative_file_path = relative_dir_path.join(file_name); + if file_name != "auxiliary" { + debug!(%file_path, "found directory"); + collector.merge(collect_tests_from_dir(cx, &file_path, &relative_file_path)?); + } + } else { + debug!(%file_path, "found other file/directory"); } - } else { - debug!(%file_path, "found other file/directory"); - } - } - Ok(()) + Ok(collector) + }) + .reduce( + || Ok(TestCollector::new()), + |a, b| { + let mut a = a?; + a.merge(b?); + Ok(a) + }, + ) } /// Returns true if `file_name` looks like a proper test file name. diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs index 9fd33e23204..d6c69d39e17 100644 --- a/src/tools/lint-docs/src/lib.rs +++ b/src/tools/lint-docs/src/lib.rs @@ -444,21 +444,15 @@ impl<'a> LintExtractor<'a> { fs::write(&tempfile, source) .map_err(|e| format!("failed to write {}: {}", tempfile.display(), e))?; let mut cmd = Command::new(self.rustc_path); - if options.contains(&"edition2024") { - cmd.arg("--edition=2024"); - cmd.arg("-Zunstable-options"); - } else if options.contains(&"edition2021") { - cmd.arg("--edition=2021"); - } else if options.contains(&"edition2018") { - cmd.arg("--edition=2018"); - } else if options.contains(&"edition2015") { - cmd.arg("--edition=2015"); - } else if options.contains(&"edition") { - panic!("lint-docs: unknown edition"); - } else { + let edition = options + .iter() + .filter_map(|opt| opt.strip_prefix("edition")) + .next() // defaults to latest edition - cmd.arg("--edition=2021"); - } + .unwrap_or("2024"); + cmd.arg(format!("--edition={edition}")); + // Just in case this is an unstable edition. + cmd.arg("-Zunstable-options"); cmd.arg("--error-format=json"); cmd.arg("--target").arg(self.rustc_target); if let Some(target_linker) = self.rustc_linker { diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 1ecaf8675c2..59d53891217 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -555e1d0386f024a8359645c3217f4b3eae9be042 +1b8ab72680f36e783af84c1a3c4f8508572bd9f9 diff --git a/src/tools/miri/tests/pass/async-drop.rs b/src/tools/miri/tests/pass/async-drop.rs index 6d556b77795..4fa84384d9b 100644 --- a/src/tools/miri/tests/pass/async-drop.rs +++ b/src/tools/miri/tests/pass/async-drop.rs @@ -4,7 +4,7 @@ // WARNING: If you would ever want to modify this test, // please consider modifying rustc's async drop test at -// `tests/ui/async-await/async-drop.rs`. +// `tests/ui/async-await/async-drop/async-drop-initial.rs`. #![feature(async_drop, impl_trait_in_assoc_type)] #![allow(incomplete_features, dead_code)] @@ -68,7 +68,8 @@ fn main() { test_async_drop(SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }) .await; - let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + let mut ptr19 = mem::MaybeUninit::new(AsyncInt(19)); + let async_drop_fut = pin!(unsafe { async_drop_in_place(ptr19.as_mut_ptr()) }); test_idempotency(async_drop_fut).await; let foo = AsyncInt(20); @@ -89,13 +90,14 @@ fn main() { struct AsyncInt(i32); +impl Drop for AsyncInt { + fn drop(&mut self) { + println!("AsyncInt::drop: {}", self.0); + } +} impl AsyncDrop for AsyncInt { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncInt::Dropper::poll: {}", self.0); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncInt::async_drop: {}", self.0); } } @@ -124,16 +126,14 @@ struct AsyncReference<'a> { foo: &'a AsyncInt, } +impl Drop for AsyncReference<'_> { + fn drop(&mut self) { + println!("AsyncReference::drop: {}", self.foo.0); + } +} impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> - = impl Future<Output = ()> - where - Self: 'a; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncReference::Dropper::poll: {}", self.foo.0); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncReference::async_drop: {}", self.foo.0); } } @@ -145,13 +145,14 @@ struct AsyncStruct { b: AsyncInt, } +impl Drop for AsyncStruct { + fn drop(&mut self) { + println!("AsyncStruct::drop: {}", self.i); + } +} impl AsyncDrop for AsyncStruct { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncStruct::Dropper::poll: {}", self.i); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncStruct::async_drop: {}", self.i); } } @@ -160,23 +161,34 @@ enum AsyncEnum { B(SyncInt), } +impl Drop for AsyncEnum { + fn drop(&mut self) { + let new_self = match self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::drop: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::drop: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } +} impl AsyncDrop for AsyncEnum { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - let new_self = match &*self { - AsyncEnum::A(foo) => { - println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); - AsyncEnum::B(SyncInt(foo.0)) - } - AsyncEnum::B(foo) => { - println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); - AsyncEnum::A(AsyncInt(foo.0)) - } - }; - mem::forget(mem::replace(&mut *self, new_self)); - } + async fn drop(mut self: Pin<&mut Self>) { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::async_drop: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::async_drop: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); } } @@ -186,14 +198,15 @@ union AsyncUnion { unsigned: u32, } +impl Drop for AsyncUnion { + fn drop(&mut self) { + println!("AsyncUnion::drop: {}, {}", unsafe { self.signed }, unsafe { self.unsigned },); + } +} impl AsyncDrop for AsyncUnion { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncUnion::Dropper::poll: {}, {}", unsafe { self.signed }, unsafe { - self.unsigned - }); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncUnion::async_drop: {}, {}", unsafe { self.signed }, unsafe { + self.unsigned + }); } } diff --git a/src/tools/miri/tests/pass/async-drop.stack.stdout b/src/tools/miri/tests/pass/async-drop.stack.stdout index 9cae4331caf..fc53df2f1b4 100644 --- a/src/tools/miri/tests/pass/async-drop.stack.stdout +++ b/src/tools/miri/tests/pass/async-drop.stack.stdout @@ -1,22 +1,23 @@ -AsyncInt::Dropper::poll: 0 -AsyncInt::Dropper::poll: 1 -AsyncInt::Dropper::poll: 2 -AsyncInt::Dropper::poll: 3 -AsyncInt::Dropper::poll: 4 -AsyncStruct::Dropper::poll: 6 -AsyncInt::Dropper::poll: 7 -AsyncInt::Dropper::poll: 8 -AsyncReference::Dropper::poll: 10 -AsyncInt::Dropper::poll: 11 -AsyncEnum(A)::Dropper::poll: 12 +AsyncInt::async_drop: 0 +AsyncInt::async_drop: 1 +AsyncInt::async_drop: 2 +AsyncInt::async_drop: 3 +AsyncInt::async_drop: 4 +AsyncStruct::async_drop: 6 +AsyncInt::async_drop: 7 +AsyncInt::async_drop: 8 +AsyncReference::async_drop: 10 +AsyncInt::async_drop: 11 +AsyncEnum(A)::async_drop: 12 SyncInt::drop: 12 -AsyncEnum(B)::Dropper::poll: 13 -AsyncInt::Dropper::poll: 13 +AsyncEnum(B)::async_drop: 13 +AsyncInt::async_drop: 13 SyncInt::drop: 14 SyncThenAsync::drop: 15 -AsyncInt::Dropper::poll: 16 +AsyncInt::async_drop: 16 SyncInt::drop: 17 -AsyncInt::Dropper::poll: 18 -AsyncInt::Dropper::poll: 19 -AsyncInt::Dropper::poll: 20 -AsyncUnion::Dropper::poll: 21, 21 +AsyncInt::async_drop: 18 +AsyncInt::async_drop: 19 +AsyncInt::async_drop: 20 +AsyncUnion::async_drop: 21, 21 +AsyncInt::async_drop: 10 diff --git a/src/tools/miri/tests/pass/async-drop.tree.stdout b/src/tools/miri/tests/pass/async-drop.tree.stdout index 9cae4331caf..fc53df2f1b4 100644 --- a/src/tools/miri/tests/pass/async-drop.tree.stdout +++ b/src/tools/miri/tests/pass/async-drop.tree.stdout @@ -1,22 +1,23 @@ -AsyncInt::Dropper::poll: 0 -AsyncInt::Dropper::poll: 1 -AsyncInt::Dropper::poll: 2 -AsyncInt::Dropper::poll: 3 -AsyncInt::Dropper::poll: 4 -AsyncStruct::Dropper::poll: 6 -AsyncInt::Dropper::poll: 7 -AsyncInt::Dropper::poll: 8 -AsyncReference::Dropper::poll: 10 -AsyncInt::Dropper::poll: 11 -AsyncEnum(A)::Dropper::poll: 12 +AsyncInt::async_drop: 0 +AsyncInt::async_drop: 1 +AsyncInt::async_drop: 2 +AsyncInt::async_drop: 3 +AsyncInt::async_drop: 4 +AsyncStruct::async_drop: 6 +AsyncInt::async_drop: 7 +AsyncInt::async_drop: 8 +AsyncReference::async_drop: 10 +AsyncInt::async_drop: 11 +AsyncEnum(A)::async_drop: 12 SyncInt::drop: 12 -AsyncEnum(B)::Dropper::poll: 13 -AsyncInt::Dropper::poll: 13 +AsyncEnum(B)::async_drop: 13 +AsyncInt::async_drop: 13 SyncInt::drop: 14 SyncThenAsync::drop: 15 -AsyncInt::Dropper::poll: 16 +AsyncInt::async_drop: 16 SyncInt::drop: 17 -AsyncInt::Dropper::poll: 18 -AsyncInt::Dropper::poll: 19 -AsyncInt::Dropper::poll: 20 -AsyncUnion::Dropper::poll: 21, 21 +AsyncInt::async_drop: 18 +AsyncInt::async_drop: 19 +AsyncInt::async_drop: 20 +AsyncUnion::async_drop: 21, 21 +AsyncInt::async_drop: 10 diff --git a/src/tools/miri/tests/pass/dst-raw.rs b/src/tools/miri/tests/pass/dst-raw.rs index f26191a1d59..3d0b843b3da 100644 --- a/src/tools/miri/tests/pass/dst-raw.rs +++ b/src/tools/miri/tests/pass/dst-raw.rs @@ -1,5 +1,7 @@ // Test DST raw pointers +#![allow(dangerous_implicit_autorefs)] + trait Trait { fn foo(&self) -> isize; } diff --git a/src/tools/miri/tests/pass/slices.rs b/src/tools/miri/tests/pass/slices.rs index dd18a061cdd..686683c3a25 100644 --- a/src/tools/miri/tests/pass/slices.rs +++ b/src/tools/miri/tests/pass/slices.rs @@ -1,7 +1,6 @@ //@revisions: stack tree //@[tree]compile-flags: -Zmiri-tree-borrows //@compile-flags: -Zmiri-strict-provenance -#![feature(slice_as_chunks)] #![feature(slice_partition_dedup)] #![feature(layout_for_ptr)] @@ -227,7 +226,7 @@ fn test_for_invalidated_pointers() { buffer.reverse(); - // Calls `fn as_chunks_unchecked_mut` internally (requires unstable `#![feature(slice_as_chunks)]`): + // Calls `fn as_chunks_unchecked_mut` internally: assert_eq!(2, buffer.as_chunks_mut::<32>().0.len()); for chunk in buffer.as_chunks_mut::<32>().0 { for elem in chunk { diff --git a/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs b/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs index 830e9c33847..e86cb3711ac 100644 --- a/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs +++ b/src/tools/miri/tests/pass/stacked-borrows/interior_mutability.rs @@ -1,3 +1,5 @@ +#![allow(dangerous_implicit_autorefs)] + use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; use std::mem::{self, MaybeUninit}; diff --git a/src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs b/src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs index 752ae4b20c5..6dde593d2cf 100644 --- a/src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs +++ b/src/tools/miri/tests/pass/tree_borrows/interior_mutability.rs @@ -1,6 +1,7 @@ //@revisions: default uniq //@compile-flags: -Zmiri-tree-borrows //@[uniq]compile-flags: -Zmiri-unique-is-unique +#![allow(dangerous_implicit_autorefs)] use std::cell::{Cell, Ref, RefCell, RefMut, UnsafeCell}; use std::mem::{self, MaybeUninit}; diff --git a/src/tools/wasm-component-ld/Cargo.toml b/src/tools/wasm-component-ld/Cargo.toml index f8ae91f9e6f..642d48b9952 100644 --- a/src/tools/wasm-component-ld/Cargo.toml +++ b/src/tools/wasm-component-ld/Cargo.toml @@ -10,4 +10,4 @@ name = "wasm-component-ld" path = "src/main.rs" [dependencies] -wasm-component-ld = "0.5.12" +wasm-component-ld = "0.5.13" diff --git a/tests/codegen/slice-as_chunks.rs b/tests/codegen/slice-as_chunks.rs index a90ee7c628e..337eb8981f6 100644 --- a/tests/codegen/slice-as_chunks.rs +++ b/tests/codegen/slice-as_chunks.rs @@ -2,7 +2,6 @@ //@ only-64bit (because the LLVM type of i64 for usize shows up) #![crate_type = "lib"] -#![feature(slice_as_chunks)] // CHECK-LABEL: @chunks4 #[no_mangle] diff --git a/tests/crashes/128695.rs b/tests/crashes/128695.rs deleted file mode 100644 index 661f427dc0e..00000000000 --- a/tests/crashes/128695.rs +++ /dev/null @@ -1,11 +0,0 @@ -//@ known-bug: rust-lang/rust#128695 -//@ edition: 2021 - -use core::pin::{pin, Pin}; - -fn main() { - let fut = pin!(async { - let async_drop_fut = pin!(core::future::async_drop(async {})); - (async_drop_fut).await; - }); -} diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir index 0c8a17ff70b..b43af549b23 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{closure#0}.built.after.mir @@ -68,14 +68,18 @@ yields () } bb10: { - drop(_1) -> [return: bb11, unwind: bb12]; + drop(_1) -> [return: bb11, unwind: bb13, drop: bb12]; } bb11: { return; } - bb12 (cleanup): { + bb12: { + coroutine_drop; + } + + bb13 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir index 9070c95bca4..5623b6d64e9 100644 --- a/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_fake_read_for_by_move.foo-{closure#0}-{synthetic#0}.built.after.mir @@ -51,14 +51,18 @@ yields () } bb6: { - drop(_1) -> [return: bb7, unwind: bb8]; + drop(_1) -> [return: bb7, unwind: bb9, drop: bb8]; } bb7: { return; } - bb8 (cleanup): { + bb8: { + coroutine_drop; + } + + bb9 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir index 8a584853e00..4d484b16b50 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{closure#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir index c5f538e5ecd..ace780f773e 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#0}-{synthetic#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir index c6721085eb2..f50ad689f44 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{closure#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir index e295f9b3cf1..62d8adeedcb 100644 --- a/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir +++ b/tests/mir-opt/async_closure_shims.main-{closure#0}-{closure#1}-{synthetic#0}.built.after.mir @@ -34,14 +34,18 @@ yields () StorageDead(_5); StorageDead(_4); StorageDead(_3); - drop(_1) -> [return: bb1, unwind: bb2]; + drop(_1) -> [return: bb1, unwind: bb3, drop: bb2]; } bb1: { return; } - bb2 (cleanup): { + bb2: { + coroutine_drop; + } + + bb3 (cleanup): { resume; } } diff --git a/tests/pretty/hir-if-else.pp b/tests/pretty/hir-if-else.pp new file mode 100644 index 00000000000..200e34ac4f5 --- /dev/null +++ b/tests/pretty/hir-if-else.pp @@ -0,0 +1,39 @@ +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-if-else.pp + +fn f(x: u32, + y: + u32) { + let mut a = 0; + if x > y { a = 1; } else { a = 2; } + + if x < 1 { + a = 1; + } else if x < 2 { + a = 2; + } else if x < 3 { a = 3; } else if x < 4 { a = 4; } else { a = 5; } + + if x < y { + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + } else { a += 1; a += 1; a += 1; a += 1; a += 1; a += 1; } + + if x < 1 { + if x < 2 { + if x < 3 { + a += 1; + } else if x < 4 { a += 1; if x < 5 { a += 1; } } + } else if x < 6 { a += 1; } + } +} + +fn main() { f(3, 4); } diff --git a/tests/pretty/hir-if-else.rs b/tests/pretty/hir-if-else.rs new file mode 100644 index 00000000000..a1cc7504f89 --- /dev/null +++ b/tests/pretty/hir-if-else.rs @@ -0,0 +1,59 @@ +//@ pretty-compare-only +//@ pretty-mode:hir +//@ pp-exact:hir-if-else.pp + +fn f(x: u32, y: u32) { + let mut a = 0; + if x > y { + a = 1; + } else { + a = 2; + } + + if x < 1 { + a = 1; + } else if x < 2 { + a = 2; + } else if x < 3 { + a = 3; + } else if x < 4 { + a = 4; + } else { + a = 5; + } + + if x < y { + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + } else { + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + } + + if x < 1 { + if x < 2 { + if x < 3 { + a += 1; + } else if x < 4 { + a += 1; + if x < 5 { + a += 1; + } + } + } else if x < 6 { + a += 1; + } + } +} + +fn main() { + f(3, 4); +} diff --git a/tests/pretty/if-else.pp b/tests/pretty/if-else.pp new file mode 100644 index 00000000000..d4ff02c5441 --- /dev/null +++ b/tests/pretty/if-else.pp @@ -0,0 +1,52 @@ +#![feature(prelude_import)] +#![no_std] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; +//@ pretty-compare-only +//@ pretty-mode:expanded +//@ pp-exact:if-else.pp + +fn f(x: u32, y: u32) { + let mut a = 0; + if x > y { a = 1; } else { a = 2; } + + if x < 1 { + a = 1; + } else if x < 2 { + a = 2; + } else if x < 3 { a = 3; } else if x < 4 { a = 4; } else { a = 5; } + + if x < y { + a += 1; + a += 1; + a += 1; + } else { + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + } + + if x < 1 { + if x < 2 { + if x < 3 { + a += 1; + } else if x < 4 { a += 1; if x < 5 { a += 1; } } + } else if x < 6 { a += 1; } + } +} + +fn main() { f(3, 4); } diff --git a/tests/pretty/if-else.rs b/tests/pretty/if-else.rs new file mode 100644 index 00000000000..b4085ea5606 --- /dev/null +++ b/tests/pretty/if-else.rs @@ -0,0 +1,65 @@ +//@ pretty-compare-only +//@ pretty-mode:expanded +//@ pp-exact:if-else.pp + +fn f(x: u32, y: u32) { + let mut a = 0; + if x > y { + a = 1; + } else { + a = 2; + } + + if x < 1 { + a = 1; + } else if x < 2 { + a = 2; + } else if x < 3 { + a = 3; + } else if x < 4 { + a = 4; + } else { + a = 5; + } + + if x < y { + a += 1; + a += 1; + a += 1; + } else { + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + a += 1; + } + + if x < 1 { + if x < 2 { + if x < 3 { + a += 1; + } else if x < 4 { + a += 1; + if x < 5 { + a += 1; + } + } + } else if x < 6 { + a += 1; + } + } +} + +fn main() { + f(3, 4); +} diff --git a/tests/pretty/never-pattern.pp b/tests/pretty/never-pattern.pp new file mode 100644 index 00000000000..923ad9b82c7 --- /dev/null +++ b/tests/pretty/never-pattern.pp @@ -0,0 +1,17 @@ +#![feature(prelude_import)] +#![no_std] +//@ pretty-mode:expanded +//@ pp-exact:never-pattern.pp +//@ only-x86_64 + +#![allow(incomplete_features)] +#![feature(never_patterns)] +#![feature(never_type)] +#[prelude_import] +use ::std::prelude::rust_2015::*; +#[macro_use] +extern crate std; + +fn f(x: Result<u32, !>) { _ = match x { Ok(x) => x, Err(!) , }; } + +fn main() {} diff --git a/tests/pretty/never-pattern.rs b/tests/pretty/never-pattern.rs new file mode 100644 index 00000000000..fe170bafc66 --- /dev/null +++ b/tests/pretty/never-pattern.rs @@ -0,0 +1,16 @@ +//@ pretty-mode:expanded +//@ pp-exact:never-pattern.pp +//@ only-x86_64 + +#![allow(incomplete_features)] +#![feature(never_patterns)] +#![feature(never_type)] + +fn f(x: Result<u32, !>) { + _ = match x { + Ok(x) => x, + Err(!), + }; +} + +fn main() {} diff --git a/tests/run-make/amdgpu-kd/rmake.rs b/tests/run-make/amdgpu-kd/rmake.rs index a787fa1da93..cba9030641d 100644 --- a/tests/run-make/amdgpu-kd/rmake.rs +++ b/tests/run-make/amdgpu-kd/rmake.rs @@ -6,13 +6,19 @@ //@ needs-llvm-components: amdgpu //@ needs-rust-lld +use run_make_support::targets::is_windows_gnu; use run_make_support::{llvm_readobj, rustc}; fn main() { + // FIXME(#115985): rust-lld on gnu targets may spuriously fail with + // STATUS_HEAP_CORRUPTION (0xc0000374). + // To try to mitigate this we pass --threads=1 to the linker. + let extra_args: &[&str] = if is_windows_gnu() { &["-C", "link-arg=--threads=1"] } else { &[] }; rustc() .crate_name("foo") .target("amdgcn-amd-amdhsa") .arg("-Ctarget-cpu=gfx900") + .args(&extra_args) .crate_type("cdylib") .input("foo.rs") .run(); diff --git a/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs b/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs new file mode 100644 index 00000000000..ed7584b7425 --- /dev/null +++ b/tests/rustdoc-ui/doctest/auxiliary/macro-after-main.rs @@ -0,0 +1 @@ +use std::string::String; diff --git a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout index 8f056a5f703..273d7071237 100644 --- a/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout +++ b/tests/rustdoc-ui/doctest/edition-2024-error-output.stdout @@ -5,6 +5,9 @@ test $DIR/edition-2024-error-output.rs - (line 12) ... FAILED failures: ---- $DIR/edition-2024-error-output.rs - (line 12) stdout ---- +Test executable failed (exit status: 101). + +stderr: thread 'main' panicked at $TMP:6:1: assertion `left == right` failed @@ -13,6 +16,7 @@ assertion `left == right` failed note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + failures: $DIR/edition-2024-error-output.rs - (line 12) diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs index 508faadcf67..ca5dd787467 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.rs @@ -4,12 +4,12 @@ //@ compile-flags:--test //@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" -//@ failure-status: 101 +//@ check-pass /// <https://github.com/rust-lang/rust/issues/91014> /// /// ```rust -/// struct S {}; // unexpected semicolon after struct def +/// struct S {}; /// /// fn main() { /// assert_eq!(0, 1); diff --git a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stdout b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stdout index 9eb8b391e78..1068b98cb0f 100644 --- a/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stdout +++ b/tests/rustdoc-ui/doctest/failed-doctest-extra-semicolon-on-item.stdout @@ -1,29 +1,6 @@ running 1 test -test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... FAILED +test $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) ... ok -failures: - ----- $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) stdout ---- -error: expected item, found `;` - --> $DIR/failed-doctest-extra-semicolon-on-item.rs:12:12 - | -LL | struct S {}; // unexpected semicolon after struct def - | ^ - | - = help: braced struct declarations are not followed by a semicolon -help: remove this semicolon - | -LL - struct S {}; // unexpected semicolon after struct def -LL + struct S {} // unexpected semicolon after struct def - | - -error: aborting due to 1 previous error - -Couldn't compile the test. - -failures: - $DIR/failed-doctest-extra-semicolon-on-item.rs - m (line 11) - -test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME diff --git a/tests/rustdoc-ui/doctest/macro-after-main.rs b/tests/rustdoc-ui/doctest/macro-after-main.rs new file mode 100644 index 00000000000..0a42343f1c2 --- /dev/null +++ b/tests/rustdoc-ui/doctest/macro-after-main.rs @@ -0,0 +1,16 @@ +// This test checks a corner case where the macro calls used to be skipped, +// making them considered as statement, and therefore some cases where +// `include!` macro was then put into a function body, making the doctest +// compilation fail. + +//@ compile-flags:--test +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + +//! ``` +//! include!("./auxiliary/macro-after-main.rs"); +//! +//! fn main() {} +//! eprintln!(); +//! ``` diff --git a/tests/rustdoc-ui/doctest/macro-after-main.stdout b/tests/rustdoc-ui/doctest/macro-after-main.stdout new file mode 100644 index 00000000000..72ffe2b5a27 --- /dev/null +++ b/tests/rustdoc-ui/doctest/macro-after-main.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/macro-after-main.rs - (line 11) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.rs b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs new file mode 100644 index 00000000000..9b0c69d8839 --- /dev/null +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.rs @@ -0,0 +1,26 @@ +// This test ensures that the output is correctly generated when the +// doctest fails. It checks when there is stderr and stdout, no stdout +// and no stderr/stdout. +// +// This is a regression test for <https://github.com/rust-lang/rust/issues/140289>. + +//@ edition: 2024 +//@ compile-flags:--test --test-args=--test-threads=1 +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ normalize-stdout: "panicked at .+rs:" -> "panicked at $$TMP:" +//@ failure-status: 101 +//@ rustc-env:RUST_BACKTRACE=0 + +//! ``` +//! println!("######## from a DOC TEST ########"); +//! assert_eq!("doc", "test"); +//! ``` +//! +//! ``` +//! assert_eq!("doc", "test"); +//! ``` +//! +//! ``` +//! std::process::exit(1); +//! ``` diff --git a/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout new file mode 100644 index 00000000000..b2febe1344f --- /dev/null +++ b/tests/rustdoc-ui/doctest/stdout-and-stderr.stdout @@ -0,0 +1,46 @@ + +running 3 tests +test $DIR/stdout-and-stderr.rs - (line 15) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 20) ... FAILED +test $DIR/stdout-and-stderr.rs - (line 24) ... FAILED + +failures: + +---- $DIR/stdout-and-stderr.rs - (line 15) stdout ---- +Test executable failed (exit status: 101). + +stdout: +######## from a DOC TEST ######## + +stderr: + +thread 'main' panicked at $TMP:7:1: +assertion `left == right` failed + left: "doc" + right: "test" +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +---- $DIR/stdout-and-stderr.rs - (line 20) stdout ---- +Test executable failed (exit status: 101). + +stderr: + +thread 'main' panicked at $TMP:15:1: +assertion `left == right` failed + left: "doc" + right: "test" +note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace + + +---- $DIR/stdout-and-stderr.rs - (line 24) stdout ---- +Test executable failed (exit status: 1). + + +failures: + $DIR/stdout-and-stderr.rs - (line 15) + $DIR/stdout-and-stderr.rs - (line 20) + $DIR/stdout-and-stderr.rs - (line 24) + +test result: FAILED. 0 passed; 3 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs new file mode 100644 index 00000000000..ee2299c0fd8 --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.rs @@ -0,0 +1,22 @@ +// This test ensures that if there is an expression alongside a `main` +// function, it will not consider the entire code to be part of the `main` +// function and will generate its own function to wrap everything. +// +// This is a regression test for: +// * <https://github.com/rust-lang/rust/issues/140162> +// * <https://github.com/rust-lang/rust/issues/139651> +//@ compile-flags:--test +//@ normalize-stdout: "tests/rustdoc-ui/doctest" -> "$$DIR" +//@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" +//@ check-pass + +#![crate_name = "foo"] + +//! ``` +//! # if cfg!(miri) { return; } +//! use std::ops::Deref; +//! +//! fn main() { +//! println!("Hi!"); +//! } +//! ``` diff --git a/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout new file mode 100644 index 00000000000..90d7c3546bf --- /dev/null +++ b/tests/rustdoc-ui/doctest/test-main-alongside-exprs.stdout @@ -0,0 +1,6 @@ + +running 1 test +test $DIR/test-main-alongside-exprs.rs - (line 15) ... ok + +test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME + diff --git a/tests/ui/asm/named_const_simd_vec_len.rs b/tests/ui/asm/named_const_simd_vec_len.rs index 7df4d922d5c..7fedeb7d4d1 100644 --- a/tests/ui/asm/named_const_simd_vec_len.rs +++ b/tests/ui/asm/named_const_simd_vec_len.rs @@ -3,6 +3,9 @@ //@ only-x86_64 //@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver #![feature(repr_simd)] diff --git a/tests/ui/asm/normalizable-asm-ty.rs b/tests/ui/asm/normalizable-asm-ty.rs new file mode 100644 index 00000000000..c1f3f3ecd61 --- /dev/null +++ b/tests/ui/asm/normalizable-asm-ty.rs @@ -0,0 +1,16 @@ +//@ check-pass +//@ needs-asm-support +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + +fn invoke(pc_section: &[usize]) { + unsafe { + std::arch::asm!( + "/* {} */", + in(reg) pc_section[0] + ); + } +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs new file mode 100644 index 00000000000..650fb10d94b --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.rs @@ -0,0 +1,22 @@ +//@ edition: 2024 + +// Regression test for <https://github.com/rust-lang/rust/issues/140292>. + +struct Test; + +impl Test { + async fn an_async_fn(&mut self) { + todo!() + } + + pub async fn uses_takes_asyncfn(&mut self) { + takes_asyncfn(Box::new(async || self.an_async_fn().await)); + //~^ ERROR expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + } +} + +async fn takes_asyncfn(_: impl AsyncFn()) { + todo!() +} + +fn main() {} diff --git a/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr new file mode 100644 index 00000000000..e79f95c197b --- /dev/null +++ b/tests/ui/async-await/async-closures/kind-due-to-arg-with-box-wrap.stderr @@ -0,0 +1,21 @@ +error[E0525]: expected a closure that implements the `AsyncFn` trait, but this closure only implements `AsyncFnMut` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:13:32 + | +LL | takes_asyncfn(Box::new(async || self.an_async_fn().await)); + | ------------- ---------^^^^^^^^-------------------------- + | | | | | + | | | | closure is `AsyncFnMut` because it mutates the variable `*self` here + | | | this closure implements `AsyncFnMut`, not `AsyncFn` + | | the requirement to implement `AsyncFn` derives from here + | required by a bound introduced by this call + | + = note: required for `Box<{async closure@$DIR/kind-due-to-arg-with-box-wrap.rs:13:32: 13:40}>` to implement `AsyncFn()` +note: required by a bound in `takes_asyncfn` + --> $DIR/kind-due-to-arg-with-box-wrap.rs:18:32 + | +LL | async fn takes_asyncfn(_: impl AsyncFn()) { + | ^^^^^^^^^ required by this bound in `takes_asyncfn` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0525`. diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.rs b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs new file mode 100644 index 00000000000..44dcbd14f48 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.rs @@ -0,0 +1,101 @@ +//@ run-pass +//@ check-run-results +// Future `bar` with internal async drop `Foo` will have async drop itself. +// And we trying to drop this future in sync context (`block_on` func) + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on(bar(10)); + println!("done") +} + +async fn baz(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); + baz(ident_base + 1).await; +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout new file mode 100644 index 00000000000..c2663b3f238 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-from-future.run.stdout @@ -0,0 +1,5 @@ +Foo::new(10) +Foo::new(11) +Foo::async drop(11) +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs new file mode 100644 index 00000000000..417dce62dba --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.rs @@ -0,0 +1,82 @@ +//@ run-pass +//@ check-run-results +// Future `bar` with internal async drop `Foo` will have async drop itself. +// And we trying to drop this future in sync context (`block_on` func) + +#![feature(async_drop)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::{ + future::{Future, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on(bar(10)); + println!("done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on<F>(fut: F) -> F::Output +where + F: Future, +{ + let mut fut = pin!(fut); + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + } +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout new file mode 100644 index 00000000000..ad0b083ca09 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-future-in-sync-context.run.stdout @@ -0,0 +1,3 @@ +Foo::new(10) +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-glue-array.rs b/tests/ui/async-await/async-drop/async-drop-glue-array.rs new file mode 100644 index 00000000000..21c08da2337 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.rs @@ -0,0 +1,112 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout new file mode 100644 index 00000000000..56b61103cc4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-array.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-glue-generic.rs b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs new file mode 100644 index 00000000000..9e8ae130324 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.rs @@ -0,0 +1,111 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(6, 10)); + } + println!("Done") +} + +async fn bar<T>(_arg: T, ident_base: usize) { + let _vec: [Foo; 4] = [ + Foo::new(ident_base), + Foo::new(ident_base + 1), + Foo::new(ident_base + 2), + Foo::new(ident_base + 3) + ]; +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout new file mode 100644 index 00000000000..56b61103cc4 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue-generic.run.stdout @@ -0,0 +1,12 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::new() : 13 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Foo::async drop() : 13 +Done diff --git a/tests/ui/async-await/async-drop/async-drop-glue.rs b/tests/ui/async-await/async-drop/async-drop-glue.rs new file mode 100644 index 00000000000..dc4bb527a51 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.rs @@ -0,0 +1,124 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and has complex async drop glue. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + { + block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) { + let _complex = Complex::new(ident_base); +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-glue.run.stdout b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout new file mode 100644 index 00000000000..e21a9dd34fa --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-glue.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 11 +Foo::async drop() : 12 +Done diff --git a/tests/ui/async-await/async-drop.rs b/tests/ui/async-await/async-drop/async-drop-initial.rs index b1af81423ce..80b34840c8b 100644 --- a/tests/ui/async-await/async-drop.rs +++ b/tests/ui/async-await/async-drop/async-drop-initial.rs @@ -52,21 +52,20 @@ fn main() { let i = 13; let fut = pin!(async { - test_async_drop(Int(0), 0).await; - // FIXME(#63818): niches in coroutines are disabled. - // Some of these sizes should be smaller, as indicated in comments. - test_async_drop(AsyncInt(0), /*104*/ 112).await; - test_async_drop([AsyncInt(1), AsyncInt(2)], /*152*/ 168).await; - test_async_drop((AsyncInt(3), AsyncInt(4)), /*488*/ 528).await; - test_async_drop(5, 0).await; + test_async_drop(Int(0), 16).await; + test_async_drop(AsyncInt(0), 32).await; + test_async_drop([AsyncInt(1), AsyncInt(2)], 104).await; + test_async_drop((AsyncInt(3), AsyncInt(4)), 120).await; + test_async_drop(5, 16).await; let j = 42; - test_async_drop(&i, 0).await; - test_async_drop(&j, 0).await; - test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, /*1688*/ 1792).await; - test_async_drop(ManuallyDrop::new(AsyncInt(9)), 0).await; + test_async_drop(&i, 16).await; + test_async_drop(&j, 16).await; + test_async_drop(AsyncStruct { b: AsyncInt(8), a: AsyncInt(7), i: 6 }, 168).await; + test_async_drop(ManuallyDrop::new(AsyncInt(9)), 16).await; let foo = AsyncInt(10); - test_async_drop(AsyncReference { foo: &foo }, /*104*/ 112).await; + test_async_drop(AsyncReference { foo: &foo }, 32).await; + let _ = ManuallyDrop::new(foo); let foo = AsyncInt(11); test_async_drop( @@ -75,21 +74,22 @@ fn main() { let foo = AsyncInt(10); foo }, - /*120*/ 136, + 48, ) .await; - test_async_drop(AsyncEnum::A(AsyncInt(12)), /*680*/ 736).await; - test_async_drop(AsyncEnum::B(SyncInt(13)), /*680*/ 736).await; + test_async_drop(AsyncEnum::A(AsyncInt(12)), 104).await; + test_async_drop(AsyncEnum::B(SyncInt(13)), 104).await; - test_async_drop(SyncInt(14), /*16*/ 24).await; + test_async_drop(SyncInt(14), 16).await; test_async_drop( SyncThenAsync { i: 15, a: AsyncInt(16), b: SyncInt(17), c: AsyncInt(18) }, - /*3064*/ 3296, + 120, ) .await; - let async_drop_fut = pin!(core::future::async_drop(AsyncInt(19))); + let mut ptr19 = mem::MaybeUninit::new(AsyncInt(19)); + let async_drop_fut = pin!(unsafe { async_drop_in_place(ptr19.as_mut_ptr()) }); test_idempotency(async_drop_fut).await; let foo = AsyncInt(20); @@ -101,11 +101,11 @@ fn main() { black_box(core::future::ready(())).await; foo }, - /*120*/ 136, + 48, ) .await; - test_async_drop(AsyncUnion { signed: 21 }, /*32*/ 40).await; + test_async_drop(AsyncUnion { signed: 21 }, 32).await; }); let res = fut.poll(&mut cx); assert_eq!(res, Poll::Ready(())); @@ -113,13 +113,14 @@ fn main() { struct AsyncInt(i32); +impl Drop for AsyncInt { + fn drop(&mut self) { + println!("AsyncInt::drop: {}", self.0); + } +} impl AsyncDrop for AsyncInt { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncInt::Dropper::poll: {}", self.0); - } + async fn drop(self: Pin<&mut Self>) { + println!("AsyncInt::Dropper::poll: {}", self.0); } } @@ -148,13 +149,15 @@ struct AsyncReference<'a> { foo: &'a AsyncInt, } -impl AsyncDrop for AsyncReference<'_> { - type Dropper<'a> = impl Future<Output = ()> where Self: 'a; +impl Drop for AsyncReference<'_> { + fn drop(&mut self) { + println!("AsyncReference::drop: {}", self.foo.0); + } +} - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncReference::Dropper::poll: {}", self.foo.0); - } +impl AsyncDrop for AsyncReference<'_> { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncReference::Dropper::poll: {}", self.foo.0); } } @@ -166,13 +169,15 @@ struct AsyncStruct { b: AsyncInt, } -impl AsyncDrop for AsyncStruct { - type Dropper<'a> = impl Future<Output = ()>; +impl Drop for AsyncStruct { + fn drop(&mut self) { + println!("AsyncStruct::drop: {}", self.i); + } +} - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!("AsyncStruct::Dropper::poll: {}", self.i); - } +impl AsyncDrop for AsyncStruct { + async fn drop(self: Pin<&mut Self>) { + println!("AsyncStruct::Dropper::poll: {}", self.i); } } @@ -181,23 +186,34 @@ enum AsyncEnum { B(SyncInt), } +impl Drop for AsyncEnum { + fn drop(&mut self) { + let new_self = match self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::drop: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::drop: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); + } +} impl AsyncDrop for AsyncEnum { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(mut self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - let new_self = match &*self { - AsyncEnum::A(foo) => { - println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); - AsyncEnum::B(SyncInt(foo.0)) - } - AsyncEnum::B(foo) => { - println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); - AsyncEnum::A(AsyncInt(foo.0)) - } - }; - mem::forget(mem::replace(&mut *self, new_self)); - } + async fn drop(mut self: Pin<&mut Self>) { + let new_self = match &*self { + AsyncEnum::A(foo) => { + println!("AsyncEnum(A)::Dropper::poll: {}", foo.0); + AsyncEnum::B(SyncInt(foo.0)) + } + AsyncEnum::B(foo) => { + println!("AsyncEnum(B)::Dropper::poll: {}", foo.0); + AsyncEnum::A(AsyncInt(foo.0)) + } + }; + mem::forget(mem::replace(&mut *self, new_self)); } } @@ -207,16 +223,21 @@ union AsyncUnion { unsigned: u32, } +impl Drop for AsyncUnion { + fn drop(&mut self) { + println!( + "AsyncUnion::drop: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); + } +} impl AsyncDrop for AsyncUnion { - type Dropper<'a> = impl Future<Output = ()>; - - fn async_drop(self: Pin<&mut Self>) -> Self::Dropper<'_> { - async move { - println!( - "AsyncUnion::Dropper::poll: {}, {}", - unsafe { self.signed }, - unsafe { self.unsigned }, - ); - } + async fn drop(self: Pin<&mut Self>) { + println!( + "AsyncUnion::Dropper::poll: {}, {}", + unsafe { self.signed }, + unsafe { self.unsigned }, + ); } } diff --git a/tests/ui/async-await/async-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop-initial.run.stdout index 9cae4331caf..9cae4331caf 100644 --- a/tests/ui/async-await/async-drop.run.stdout +++ b/tests/ui/async-await/async-drop/async-drop-initial.run.stdout diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.rs b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs new file mode 100644 index 00000000000..772a853fe1e --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.rs @@ -0,0 +1,110 @@ +//@ run-pass +//@ check-run-results +// Test async drop of coroutine `bar` (with internal async drop), +// stopped at the middle of execution, with AsyncDrop object Foo active. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +//@ edition: 2021 + +use std::mem::ManuallyDrop; + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + println!("Foo::new({})", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + println!("Foo::drop({})", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + println!("Foo::async drop({})", self.my_resource_handle); + } +} + +fn main() { + block_on_and_drop_in_the_middle(bar(10)); + println!("done") +} + +pub struct MiddleFuture { + first_call: bool, +} +impl Future for MiddleFuture { + type Output = (); + fn poll(mut self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<Self::Output> { + if self.first_call { + println!("MiddleFuture first poll"); + self.first_call = false; + Poll::Pending + } else { + println!("MiddleFuture Ready"); + Poll::Ready(()) + } + } +} + +async fn bar(ident_base: usize) { + let middle = MiddleFuture { first_call: true }; + let mut _first = Foo::new(ident_base); + middle.await; // Hanging `bar` future before Foo drop +} + +fn block_on_and_drop_in_the_middle<F>(fut_unpin: F) -> F::Output +where + F: Future<Output = ()>, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let poll1 = fut.as_mut().poll(&mut context); + assert!(poll1.is_pending()); + + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout new file mode 100644 index 00000000000..60df7674d0a --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-middle-drop.run.stdout @@ -0,0 +1,4 @@ +Foo::new(10) +MiddleFuture first poll +Foo::async drop(10) +done diff --git a/tests/ui/async-await/async-drop/async-drop-open.rs b/tests/ui/async-await/async-drop/async-drop-open.rs new file mode 100644 index 00000000000..d14eff874ae --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.rs @@ -0,0 +1,127 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Struct `Complex` contains three `Foo` fields and one of them is moved out. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +#[allow(dead_code)] +struct Complex { + field1: Foo, + field2: Foo, + field3: Foo, +} + +impl Complex { + fn new(my_resource_handle: usize) -> Self { + myprintln("Complex::new()", my_resource_handle); + let field1 = Foo::new(my_resource_handle); + let field2 = Foo::new(my_resource_handle + 1); + let field3 = Foo::new(my_resource_handle + 2); + Complex { field1, field2, field3 } + } +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + // Inside field1 and field3 of Complex must be dropped (as async drop) + // field2 must be dropped here (as sync drop) + { + let _field2 = block_on(bar(10)); + } + println!("Done") +} + +async fn bar(ident_base: usize) -> Foo { + let complex = Complex::new(ident_base); + complex.field2 +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop-open.run.stdout b/tests/ui/async-await/async-drop/async-drop-open.run.stdout new file mode 100644 index 00000000000..e72dfd8a743 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop-open.run.stdout @@ -0,0 +1,11 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Complex::new() : 10 +Foo::new() : 10 +Foo::new() : 11 +Foo::new() : 12 +Foo::async drop() : 10 +Foo::async drop() : 12 +Foo::drop() : 11 +Done diff --git a/tests/ui/async-await/async-drop/async-drop.rs b/tests/ui/async-await/async-drop/async-drop.rs new file mode 100644 index 00000000000..2d9c5934be5 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.rs @@ -0,0 +1,105 @@ +//@ run-pass +//@ check-run-results +// struct `Foo` has both sync and async drop. +// Sync version is called in sync context, async version is called in async function. + +#![feature(async_drop)] +#![allow(incomplete_features)] + +use std::mem::ManuallyDrop; + +//@ edition: 2021 + +#[inline(never)] +fn myprintln(msg: &str, my_resource_handle: usize) { + println!("{} : {}", msg, my_resource_handle); +} + +use std::{ + future::{Future, async_drop_in_place, AsyncDrop}, + pin::{pin, Pin}, + sync::{mpsc, Arc}, + task::{Context, Poll, Wake, Waker}, +}; + +struct Foo { + my_resource_handle: usize, +} + +impl Foo { + fn new(my_resource_handle: usize) -> Self { + let out = Foo { + my_resource_handle, + }; + myprintln("Foo::new()", my_resource_handle); + out + } +} + +impl Drop for Foo { + fn drop(&mut self) { + myprintln("Foo::drop()", self.my_resource_handle); + } +} + +impl AsyncDrop for Foo { + async fn drop(self: Pin<&mut Self>) { + myprintln("Foo::async drop()", self.my_resource_handle); + } +} + +fn main() { + { + let _ = Foo::new(7); + } + println!("Middle"); + block_on(bar(10)); + println!("Done") +} + +async fn bar(ident_base: usize) { + let mut _first = Foo::new(ident_base); +} + +fn block_on<F>(fut_unpin: F) -> F::Output +where + F: Future, +{ + let mut fut_pin = pin!(ManuallyDrop::new(fut_unpin)); + let mut fut: Pin<&mut F> = unsafe { + Pin::map_unchecked_mut(fut_pin.as_mut(), |x| &mut **x) + }; + let (waker, rx) = simple_waker(); + let mut context = Context::from_waker(&waker); + let rv = loop { + match fut.as_mut().poll(&mut context) { + Poll::Ready(out) => break out, + // expect wake in polls + Poll::Pending => rx.try_recv().unwrap(), + } + }; + let drop_fut_unpin = unsafe { async_drop_in_place(fut.get_unchecked_mut()) }; + let mut drop_fut: Pin<&mut _> = pin!(drop_fut_unpin); + loop { + match drop_fut.as_mut().poll(&mut context) { + Poll::Ready(()) => break, + Poll::Pending => rx.try_recv().unwrap(), + } + } + rv +} + +fn simple_waker() -> (Waker, mpsc::Receiver<()>) { + struct SimpleWaker { + tx: std::sync::mpsc::Sender<()>, + } + + impl Wake for SimpleWaker { + fn wake(self: Arc<Self>) { + self.tx.send(()).unwrap(); + } + } + + let (tx, rx) = mpsc::channel(); + (Waker::from(Arc::new(SimpleWaker { tx })), rx) +} diff --git a/tests/ui/async-await/async-drop/async-drop.run.stdout b/tests/ui/async-await/async-drop/async-drop.run.stdout new file mode 100644 index 00000000000..cb7d0b0fea5 --- /dev/null +++ b/tests/ui/async-await/async-drop/async-drop.run.stdout @@ -0,0 +1,6 @@ +Foo::new() : 7 +Foo::drop() : 7 +Middle +Foo::new() : 10 +Foo::async drop() : 10 +Done diff --git a/tests/crashes/132103.rs b/tests/ui/async-await/async-drop/ex-ice-132103.rs index e2d8378efe6..3d32cb14fb0 100644 --- a/tests/crashes/132103.rs +++ b/tests/ui/async-await/async-drop/ex-ice-132103.rs @@ -1,6 +1,11 @@ -//@ known-bug: #132103 +//@ run-pass +//! This test used to ICE: rust-lang/rust#132103 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. //@ compile-flags: -Zvalidate-mir -Zinline-mir=yes //@ edition: 2018 +#![feature(async_drop)] +#![allow(incomplete_features)] + use core::future::{async_drop_in_place, Future}; use core::mem::{self}; use core::pin::pin; @@ -18,5 +23,5 @@ fn main() { let fut = pin!(async { test_async_drop(test_async_drop(0)).await; }); - fut.poll(&mut cx); + let _ = fut.poll(&mut cx); } diff --git a/tests/ui/async-await/async-drop/ex-ice1.rs b/tests/ui/async-await/async-drop/ex-ice1.rs new file mode 100644 index 00000000000..f514c57097a --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.rs @@ -0,0 +1,13 @@ +//! This test used to ICE: rust-lang/rust#128695 +//! Fixed when re-work async drop to shim drop glue coroutine scheme. +//@ edition: 2021 + +use core::pin::{pin, Pin}; + +fn main() { + let fut = pin!(async { + let async_drop_fut = pin!(core::future::async_drop(async {})); //~ ERROR: expected function, found module `core::future::async_drop` + //~^ ERROR: module `async_drop` is private + (async_drop_fut).await; + }); +} diff --git a/tests/ui/async-await/async-drop/ex-ice1.stderr b/tests/ui/async-await/async-drop/ex-ice1.stderr new file mode 100644 index 00000000000..68533edeb08 --- /dev/null +++ b/tests/ui/async-await/async-drop/ex-ice1.stderr @@ -0,0 +1,19 @@ +error[E0423]: expected function, found module `core::future::async_drop` + --> $DIR/ex-ice1.rs:9:35 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^^^^^^^^^^^^^^^ not a function + +error[E0603]: module `async_drop` is private + --> $DIR/ex-ice1.rs:9:49 + | +LL | let async_drop_fut = pin!(core::future::async_drop(async {})); + | ^^^^^^^^^^ private module + | +note: the module `async_drop` is defined here + --> $SRC_DIR/core/src/future/mod.rs:LL:COL + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0423, E0603. +For more information about an error, try `rustc --explain E0423`. diff --git a/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs b/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs new file mode 100644 index 00000000000..54992c98655 --- /dev/null +++ b/tests/ui/async-await/in-trait/cycle-if-impl-doesnt-apply.rs @@ -0,0 +1,39 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 + +// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/185>. +// Avoid unnecessarily computing the RPITIT type of the first impl when checking the WF of the +// second impl, since the first impl relies on the hidden type of the second impl. + +use std::future::Future; + +trait Handler {} + +struct W<T>(T); + +trait SendTarget { + fn call(self) -> impl Future<Output = ()> + Send; +} + +impl<T> SendTarget for W<T> +where + T: Handler + Send, +{ + async fn call(self) { + todo!() + } +} + +impl<T> SendTarget for T +where + T: Handler + Send, +{ + async fn call(self) { + W(self).call().await + } +} + +fn main() {} diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs index b09c96ada8a..5106ba6c86c 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.rs @@ -17,7 +17,7 @@ impl Foo { fn main() { unsafe { let sfoo: *mut Foo = &mut SFOO; - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] let x = (*sfoo).x(); (*sfoo).x[1] += 1; *x += 1; diff --git a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr index a392177ffe2..4e19fd81735 100644 --- a/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr +++ b/tests/ui/borrowck/borrowck-unsafe-static-mutable-borrows.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/borrowck-unsafe-static-mutable-borrows.rs:19:30 | LL | let sfoo: *mut Foo = &mut SFOO; diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.rs b/tests/ui/const-generics/defaults/concrete-const-param-type.rs new file mode 100644 index 00000000000..c411f81192b --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.rs @@ -0,0 +1,13 @@ +#![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] +//~^ WARN the feature `generic_const_parameter_types` is incomplete +//~| WARN the feature `unsized_const_params` is incomplete +// Make sure that we test the const param type of default const parameters +// if both the type of the default and the type of the parameter are concrete. + +use std::marker::ConstParamTy_; + +struct Foo<const N: u32, const M: u64 = N>; //~ ERROR the constant `N` is not of type `u64` +struct Bar<T: ConstParamTy_, const N: T, const M: u64 = N>(T); // ok +struct Baz<T: ConstParamTy_, const N: u32, const M: T = N>(T); // ok + +fn main() {} diff --git a/tests/ui/const-generics/defaults/concrete-const-param-type.stderr b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr new file mode 100644 index 00000000000..ad077f87e5d --- /dev/null +++ b/tests/ui/const-generics/defaults/concrete-const-param-type.stderr @@ -0,0 +1,25 @@ +warning: the feature `generic_const_parameter_types` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:12 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #137626 <https://github.com/rust-lang/rust/issues/137626> for more information + = note: `#[warn(incomplete_features)]` on by default + +warning: the feature `unsized_const_params` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/concrete-const-param-type.rs:1:43 + | +LL | #![feature(generic_const_parameter_types, unsized_const_params, adt_const_params)] + | ^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information + +error: the constant `N` is not of type `u64` + --> $DIR/concrete-const-param-type.rs:9:26 + | +LL | struct Foo<const N: u32, const M: u64 = N>; + | ^^^^^^^^^^^^^^^^ expected `u64`, found `u32` + +error: aborting due to 1 previous error; 2 warnings emitted + diff --git a/tests/ui/consts/const_let_assign2.rs b/tests/ui/consts/const_let_assign2.rs index e8ebba7b208..5349ae31bb4 100644 --- a/tests/ui/consts/const_let_assign2.rs +++ b/tests/ui/consts/const_let_assign2.rs @@ -16,7 +16,7 @@ static mut BB: AA = AA::new(); fn main() { let ptr = unsafe { &mut BB }; - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] for a in ptr.data.iter() { println!("{}", a); } diff --git a/tests/ui/consts/const_let_assign2.stderr b/tests/ui/consts/const_let_assign2.stderr index 0d76f142d17..1bb560437b6 100644 --- a/tests/ui/consts/const_let_assign2.stderr +++ b/tests/ui/consts/const_let_assign2.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/const_let_assign2.rs:18:24 | LL | let ptr = unsafe { &mut BB }; diff --git a/tests/ui/coroutine/dont-drop-stalled-generators.rs b/tests/ui/coroutine/dont-drop-stalled-generators.rs new file mode 100644 index 00000000000..8e0c45a9773 --- /dev/null +++ b/tests/ui/coroutine/dont-drop-stalled-generators.rs @@ -0,0 +1,25 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +//@ edition: 2024 + +// This test previously used the `is_copy_raw` query during +// HIR typeck, dropping the list of generators from the current +// body. This then caused a query cycle. + +struct W<T>(*const T); + +impl<T: Send> Clone for W<T> { + fn clone(&self) -> Self { W(self.0) } +} + +impl<T: Send> Copy for W<T> {} + +fn main() { + let coro = async {}; + let x = W(&raw const coro); + let c = || { + let x = x; + }; +} diff --git a/tests/ui/dynamically-sized-types/dst-raw.rs b/tests/ui/dynamically-sized-types/dst-raw.rs index 111848c5a7f..935f0f11ca6 100644 --- a/tests/ui/dynamically-sized-types/dst-raw.rs +++ b/tests/ui/dynamically-sized-types/dst-raw.rs @@ -1,6 +1,8 @@ //@ run-pass // Test DST raw pointers +#![allow(dangerous_implicit_autorefs)] + trait Trait { fn foo(&self) -> isize; } diff --git a/tests/ui/feature-gates/feature-gate-async-drop.rs b/tests/ui/feature-gates/feature-gate-async-drop.rs new file mode 100644 index 00000000000..20511b1d8ee --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.rs @@ -0,0 +1,16 @@ +//@ edition: 2021 + +use std::future::AsyncDrop; //~ ERROR use of unstable library feature `async_drop` +use std::pin::Pin; + +struct Foo {} + +impl Drop for Foo { + fn drop(&mut self) {} +} + +impl AsyncDrop for Foo { //~ ERROR use of unstable library feature `async_drop` + async fn drop(self: Pin<&mut Self>) {} //~ ERROR use of unstable library feature `async_drop` +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-async-drop.stderr b/tests/ui/feature-gates/feature-gate-async-drop.stderr new file mode 100644 index 00000000000..e795c3a3422 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-async-drop.stderr @@ -0,0 +1,33 @@ +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:3:5 + | +LL | use std::future::AsyncDrop; + | ^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:13:5 + | +LL | async fn drop(self: Pin<&mut Self>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `async_drop` + --> $DIR/feature-gate-async-drop.rs:12:6 + | +LL | impl AsyncDrop for Foo { + | ^^^^^^^^^ + | + = note: see issue #126482 <https://github.com/rust-lang/rust/issues/126482> for more information + = help: add `#![feature(async_drop)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/impl-trait/in-trait/cycle-if-impl-doesnt-apply.rs b/tests/ui/impl-trait/in-trait/cycle-if-impl-doesnt-apply.rs new file mode 100644 index 00000000000..72e08ed3504 --- /dev/null +++ b/tests/ui/impl-trait/in-trait/cycle-if-impl-doesnt-apply.rs @@ -0,0 +1,32 @@ +//@ check-pass +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 + +// Regression test for <https://github.com/rust-lang/trait-system-refactor-initiative/issues/185>. +// Avoid unnecessarily computing the RPITIT type of the first impl when checking the WF of the +// second impl, since the first impl relies on the hidden type of the second impl. + +trait Foo { + fn call(self) -> impl Send; +} + +trait Nested {} +impl<T> Foo for T +where + T: Nested, +{ + fn call(self) -> impl Sized { + NotSatisfied.call() + } +} + +struct NotSatisfied; +impl Foo for NotSatisfied { + fn call(self) -> impl Sized { + todo!() + } +} + +fn main() {} diff --git a/tests/ui/issues/issue-39367.rs b/tests/ui/issues/issue-39367.rs index 937d6c4b9e0..68b4d28aae3 100644 --- a/tests/ui/issues/issue-39367.rs +++ b/tests/ui/issues/issue-39367.rs @@ -20,7 +20,7 @@ fn arena() -> &'static ArenaSet<Vec<u8>> { static mut ONCE: Once = Once::new(); ONCE.call_once(|| { - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] DATA = transmute ::<Box<ArenaSet<Vec<u8>>>, *const ArenaSet<Vec<u8>>> (Box::new(__static_ref_initialize())); diff --git a/tests/ui/issues/issue-39367.stderr b/tests/ui/issues/issue-39367.stderr index 333c9f9634a..df21c09983e 100644 --- a/tests/ui/issues/issue-39367.stderr +++ b/tests/ui/issues/issue-39367.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/issue-39367.rs:22:13 | LL | / ONCE.call_once(|| { diff --git a/tests/ui/lint/static-mut-refs.e2021.stderr b/tests/ui/lint/static-mut-refs.e2021.stderr index 00a2ca99f24..320e0cee8e8 100644 --- a/tests/ui/lint/static-mut-refs.e2021.stderr +++ b/tests/ui/lint/static-mut-refs.e2021.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:38:18 | LL | let _y = &X; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _y = &raw const X; | +++++++++ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:42:18 | LL | let _y = &mut X; @@ -25,7 +25,7 @@ help: use `&raw mut` instead to create a raw pointer LL | let _y = &raw mut X; | +++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:50:22 | LL | let ref _a = X; @@ -34,7 +34,7 @@ LL | let ref _a = X; = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:25 | LL | let (_b, _c) = (&X, &Y); @@ -47,7 +47,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&raw const X, &Y); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:29 | LL | let (_b, _c) = (&X, &Y); @@ -60,7 +60,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&X, &raw const Y); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:60:13 | LL | foo(&X); @@ -73,7 +73,7 @@ help: use `&raw const` instead to create a raw pointer LL | foo(&raw const X); | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:66:17 | LL | let _ = Z.len(); @@ -82,7 +82,7 @@ LL | let _ = Z.len(); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:72:33 | LL | let _ = format!("{:?}", Z); @@ -91,7 +91,7 @@ LL | let _ = format!("{:?}", Z); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:76:18 | LL | let _v = &A.value; @@ -104,7 +104,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _v = &raw const A.value; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:80:18 | LL | let _s = &A.s.value; @@ -117,7 +117,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _s = &raw const A.s.value; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:84:22 | LL | let ref _v = A.value; @@ -126,7 +126,7 @@ LL | let ref _v = A.value; = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:14:14 | LL | &mut ($x.0) diff --git a/tests/ui/lint/static-mut-refs.e2024.stderr b/tests/ui/lint/static-mut-refs.e2024.stderr index ff41f316250..bf7ffc62ce1 100644 --- a/tests/ui/lint/static-mut-refs.e2024.stderr +++ b/tests/ui/lint/static-mut-refs.e2024.stderr @@ -1,4 +1,4 @@ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:38:18 | LL | let _y = &X; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _y = &raw const X; | +++++++++ -error: creating a mutable reference to mutable static is discouraged +error: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:42:18 | LL | let _y = &mut X; @@ -25,7 +25,7 @@ help: use `&raw mut` instead to create a raw pointer LL | let _y = &raw mut X; | +++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:50:22 | LL | let ref _a = X; @@ -34,7 +34,7 @@ LL | let ref _a = X; = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:25 | LL | let (_b, _c) = (&X, &Y); @@ -47,7 +47,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&raw const X, &Y); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:54:29 | LL | let (_b, _c) = (&X, &Y); @@ -60,7 +60,7 @@ help: use `&raw const` instead to create a raw pointer LL | let (_b, _c) = (&X, &raw const Y); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:60:13 | LL | foo(&X); @@ -73,7 +73,7 @@ help: use `&raw const` instead to create a raw pointer LL | foo(&raw const X); | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:66:17 | LL | let _ = Z.len(); @@ -82,7 +82,7 @@ LL | let _ = Z.len(); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:72:33 | LL | let _ = format!("{:?}", Z); @@ -91,7 +91,7 @@ LL | let _ = format!("{:?}", Z); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:76:18 | LL | let _v = &A.value; @@ -104,7 +104,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _v = &raw const A.value; | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:80:18 | LL | let _s = &A.s.value; @@ -117,7 +117,7 @@ help: use `&raw const` instead to create a raw pointer LL | let _s = &raw const A.s.value; | +++++++++ -error: creating a shared reference to mutable static is discouraged +error: creating a shared reference to mutable static --> $DIR/static-mut-refs.rs:84:22 | LL | let ref _v = A.value; @@ -126,7 +126,7 @@ LL | let ref _v = A.value; = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -error: creating a mutable reference to mutable static is discouraged +error: creating a mutable reference to mutable static --> $DIR/static-mut-refs.rs:14:14 | LL | &mut ($x.0) diff --git a/tests/ui/lint/static-mut-refs.rs b/tests/ui/lint/static-mut-refs.rs index 1040dfcae7a..d2511fb5b12 100644 --- a/tests/ui/lint/static-mut-refs.rs +++ b/tests/ui/lint/static-mut-refs.rs @@ -12,8 +12,8 @@ static mut FOO: (u32, u32) = (1, 2); macro_rules! bar { ($x:expr) => { &mut ($x.0) - //[e2021]~^ WARN creating a mutable reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a mutable reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a mutable reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a mutable reference to mutable static [static_mut_refs] }; } @@ -36,54 +36,54 @@ fn main() { unsafe { let _y = &X; - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] let _y = &mut X; - //[e2021]~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR mutable reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN mutable reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR mutable reference to mutable static [static_mut_refs] let _z = &raw mut X; let _p = &raw const X; let ref _a = X; - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] let (_b, _c) = (&X, &Y); - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] - //[e2021]~^^^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] + //[e2021]~^^^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^^^ ERROR shared reference to mutable static [static_mut_refs] foo(&X); - //[e2021]~^ WARN shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR shared reference to mutable static [static_mut_refs] static mut Z: &[i32; 3] = &[0, 1, 2]; let _ = Z.len(); - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _ = Z[0]; let _ = format!("{:?}", Z); - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _v = &A.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _s = &A.s.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let ref _v = A.value; - //[e2021]~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] - //[e2024]~^^ ERROR creating a shared reference to mutable static is discouraged [static_mut_refs] + //[e2021]~^ WARN creating a shared reference to mutable static [static_mut_refs] + //[e2024]~^^ ERROR creating a shared reference to mutable static [static_mut_refs] let _x = bar!(FOO); diff --git a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout index 9300f610f8e..33193c78334 100644 --- a/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout +++ b/tests/ui/macros/rfc-2011-nicer-assert-messages/non-consuming-methods-have-optimized-codegen.stdout @@ -18,18 +18,18 @@ fn arbitrary_consuming_method_for_demonstration_purposes() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*{ - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - __local_bind0 - } as usize)) { + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + __local_bind0 + } as usize)) { - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem as usize\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn addr_of() { @@ -40,12 +40,12 @@ fn addr_of() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!&*__local_bind0) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: &elem\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn binary() { @@ -56,12 +56,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 == 1)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem == 1\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -69,12 +69,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 >= 1)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem >= 1\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -82,12 +82,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 > 0)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem > 0\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -95,12 +95,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 < 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem < 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -108,12 +108,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 <= 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem <= 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; { #[allow(unused_imports)] @@ -121,12 +121,12 @@ fn binary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!(*__local_bind0 != 3)) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: elem != 3\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn unary() { @@ -137,12 +137,12 @@ fn unary() { let mut __capture0 = ::core::asserting::Capture::new(); let __local_bind0 = &elem; if ::core::intrinsics::unlikely(!**__local_bind0) { - (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); - { - ::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n", - __capture0)); - } + (&::core::asserting::Wrapper(__local_bind0)).try_capture(&mut __capture0); + { + ::std::rt::panic_fmt(format_args!("Assertion failed: *elem\nWith captures:\n elem = {0:?}\n", + __capture0)); } + } }; } fn main() {} diff --git a/tests/ui/match/issue-82392.stdout b/tests/ui/match/issue-82392.stdout index 8b7edabf004..a0d83d962e7 100644 --- a/tests/ui/match/issue-82392.stdout +++ b/tests/ui/match/issue-82392.stdout @@ -8,10 +8,10 @@ extern crate std; //@ edition:2015 fn main() ({ - (if (true as bool) - ({ } as - ()) else if (let Some(a) = - ((Some as - fn(i32) -> Option<i32> {Option::<i32>::Some})((3 as i32)) as - Option<i32>) as bool) ({ } as ()) as ()) - } as ()) + (if (true as bool) { + } else if (let Some(a) = + ((Some as + fn(i32) -> Option<i32> {Option::<i32>::Some})((3 as i32)) as + Option<i32>) as bool) { + } as ()) +} as ()) diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs index be882085c5c..73597408d10 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.rs @@ -15,7 +15,7 @@ struct S1 { impl S1 { fn new(_x: u64) -> S1 { S1 { a: unsafe { &mut X1 } } - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] } } diff --git a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr index 9dbd6769693..8268f5df236 100644 --- a/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr +++ b/tests/ui/nll/borrowck-thread-local-static-mut-borrow-outlives-fn.stderr @@ -1,4 +1,4 @@ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/borrowck-thread-local-static-mut-borrow-outlives-fn.rs:17:26 | LL | S1 { a: unsafe { &mut X1 } } diff --git a/tests/ui/parser/or-in-let-chain.edition2021.stderr b/tests/ui/parser/or-in-let-chain.edition2021.stderr new file mode 100644 index 00000000000..a97095cc3b8 --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2021.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.edition2024.stderr b/tests/ui/parser/or-in-let-chain.edition2024.stderr new file mode 100644 index 00000000000..a97095cc3b8 --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.edition2024.stderr @@ -0,0 +1,28 @@ +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:6:24 + | +LL | if let true = true || false {} + | ^^ + +error: expected expression, found `let` statement + --> $DIR/or-in-let-chain.rs:9:9 + | +LL | if (let true = true) || false {} + | ^^^^^^^^^^^^^^^ + | + = note: only supported directly in conditions of `if` and `while` expressions + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:12:24 + | +LL | if let true = true || false || true {} + | ^^ + +error: `||` operators are not supported in let chain conditions + --> $DIR/or-in-let-chain.rs:15:33 + | +LL | if let true = true && false || true {} + | ^^ + +error: aborting due to 4 previous errors + diff --git a/tests/ui/parser/or-in-let-chain.rs b/tests/ui/parser/or-in-let-chain.rs new file mode 100644 index 00000000000..4c4372bb00f --- /dev/null +++ b/tests/ui/parser/or-in-let-chain.rs @@ -0,0 +1,17 @@ +//@ revisions: edition2021 edition2024 +//@ [edition2021] edition: 2021 +//@ [edition2024] edition: 2024 + +fn main() { + if let true = true || false {} + //~^ ERROR `||` operators are not supported in let chain conditions + // With parentheses + if (let true = true) || false {} + //~^ ERROR expected expression, found `let` statement + // Multiple || operators + if let true = true || false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions + // Mixed operators (should still show error for ||) + if let true = true && false || true {} + //~^ ERROR `||` operators are not supported in let chain conditions +} diff --git a/tests/ui/pattern/deref-patterns/bindings.rs b/tests/ui/pattern/deref-patterns/bindings.rs index c14d57f3f24..ac48e3ffefc 100644 --- a/tests/ui/pattern/deref-patterns/bindings.rs +++ b/tests/ui/pattern/deref-patterns/bindings.rs @@ -3,6 +3,8 @@ #![feature(deref_patterns)] #![allow(incomplete_features)] +use std::rc::Rc; + #[cfg(explicit)] fn simple_vec(vec: Vec<u32>) -> u32 { match vec { @@ -53,29 +55,29 @@ fn nested_vec(vecvec: Vec<Vec<u32>>) -> u32 { #[cfg(explicit)] fn ref_mut(val: u32) -> u32 { - let mut b = Box::new(0u32); + let mut b = vec![0u32]; match &mut b { - deref!(_x) if false => unreachable!(), - deref!(x) => { + deref!([_x]) if false => unreachable!(), + deref!([x]) => { *x = val; } _ => unreachable!(), } - let deref!(x) = &b else { unreachable!() }; + let deref!([x]) = &b else { unreachable!() }; *x } #[cfg(implicit)] fn ref_mut(val: u32) -> u32 { - let mut b = Box::new((0u32,)); + let mut b = vec![0u32]; match &mut b { - (_x,) if false => unreachable!(), - (x,) => { + [_x] if false => unreachable!(), + [x] => { *x = val; } _ => unreachable!(), } - let (x,) = &b else { unreachable!() }; + let [x] = &b else { unreachable!() }; *x } @@ -83,7 +85,7 @@ fn ref_mut(val: u32) -> u32 { #[rustfmt::skip] fn or_and_guard(tuple: (u32, u32)) -> u32 { let mut sum = 0; - let b = Box::new(tuple); + let b = Rc::new(tuple); match b { deref!((x, _) | (_, x)) if { sum += x; false } => {}, _ => {}, @@ -95,7 +97,7 @@ fn or_and_guard(tuple: (u32, u32)) -> u32 { #[rustfmt::skip] fn or_and_guard(tuple: (u32, u32)) -> u32 { let mut sum = 0; - let b = Box::new(tuple); + let b = Rc::new(tuple); match b { (x, _) | (_, x) if { sum += x; false } => {}, _ => {}, diff --git a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs index 791776be5ac..2b4746e33e6 100644 --- a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs +++ b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.rs @@ -5,11 +5,11 @@ use std::rc::Rc; struct Struct; -fn cant_move_out_box(b: Box<Struct>) -> Struct { +fn cant_move_out_vec(b: Vec<Struct>) -> Struct { match b { - //~^ ERROR: cannot move out of a shared reference - deref!(x) => x, - _ => unreachable!(), + //~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice + deref!([x]) => x, + _ => panic!(), } } @@ -21,16 +21,16 @@ fn cant_move_out_rc(rc: Rc<Struct>) -> Struct { } } -struct Container(Struct); - -fn cant_move_out_box_implicit(b: Box<Container>) -> Struct { +fn cant_move_out_vec_implicit(b: Vec<Struct>) -> Struct { match b { - //~^ ERROR: cannot move out of a shared reference - Container(x) => x, - _ => unreachable!(), + //~^ ERROR: cannot move out of type `[Struct]`, a non-copy slice + [x] => x, + _ => panic!(), } } +struct Container(Struct); + fn cant_move_out_rc_implicit(rc: Rc<Container>) -> Struct { match rc { //~^ ERROR: cannot move out of a shared reference diff --git a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr index 1887800fc38..a548ac5909a 100644 --- a/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr +++ b/tests/ui/pattern/deref-patterns/cant_move_out_of_pattern.stderr @@ -1,19 +1,19 @@ -error[E0507]: cannot move out of a shared reference +error[E0508]: cannot move out of type `[Struct]`, a non-copy slice --> $DIR/cant_move_out_of_pattern.rs:9:11 | LL | match b { - | ^ + | ^ cannot move out of here LL | -LL | deref!(x) => x, - | - - | | - | data moved here - | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait +LL | deref!([x]) => x, + | - + | | + | data moved here + | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait | help: consider borrowing the pattern binding | -LL | deref!(ref x) => x, - | +++ +LL | deref!([ref x]) => x, + | +++ error[E0507]: cannot move out of a shared reference --> $DIR/cant_move_out_of_pattern.rs:17:11 @@ -32,22 +32,22 @@ help: consider borrowing the pattern binding LL | deref!(ref x) => x, | +++ -error[E0507]: cannot move out of a shared reference - --> $DIR/cant_move_out_of_pattern.rs:27:11 +error[E0508]: cannot move out of type `[Struct]`, a non-copy slice + --> $DIR/cant_move_out_of_pattern.rs:25:11 | LL | match b { - | ^ + | ^ cannot move out of here LL | -LL | Container(x) => x, - | - - | | - | data moved here - | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait +LL | [x] => x, + | - + | | + | data moved here + | move occurs because `x` has type `Struct`, which does not implement the `Copy` trait | help: consider borrowing the pattern binding | -LL | Container(ref x) => x, - | +++ +LL | [ref x] => x, + | +++ error[E0507]: cannot move out of a shared reference --> $DIR/cant_move_out_of_pattern.rs:35:11 @@ -68,4 +68,5 @@ LL | Container(ref x) => x, error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0507`. +Some errors have detailed explanations: E0507, E0508. +For more information about an error, try `rustc --explain E0507`. diff --git a/tests/ui/pattern/deref-patterns/closure_capture.rs b/tests/ui/pattern/deref-patterns/closure_capture.rs index 08586b6c7ab..cf78eeda1d5 100644 --- a/tests/ui/pattern/deref-patterns/closure_capture.rs +++ b/tests/ui/pattern/deref-patterns/closure_capture.rs @@ -2,8 +2,12 @@ #![feature(deref_patterns)] #![allow(incomplete_features)] +use std::rc::Rc; + +struct NoCopy; + fn main() { - let b = Box::new("aaa".to_string()); + let b = Rc::new("aaa".to_string()); let f = || { let deref!(ref s) = b else { unreachable!() }; assert_eq!(s.len(), 3); @@ -20,13 +24,13 @@ fn main() { assert_eq!(v, [1, 2, 3]); f(); - let mut b = Box::new("aaa".to_string()); + let mut b = "aaa".to_string(); let mut f = || { let deref!(ref mut s) = b else { unreachable!() }; - s.push_str("aa"); + s.make_ascii_uppercase(); }; f(); - assert_eq!(b.len(), 5); + assert_eq!(b, "AAA"); let mut v = vec![1, 2, 3]; let mut f = || { @@ -45,4 +49,20 @@ fn main() { }; f(); assert_eq!(v, [1, 2, 4]); + + let b = Box::new(NoCopy); + let f = || { + // this should move out of the box rather than borrow. + let deref!(x) = b else { unreachable!() }; + drop::<NoCopy>(x); + }; + f(); + + let b = Box::new((NoCopy,)); + let f = || { + // this should move out of the box rather than borrow. + let (x,) = b else { unreachable!() }; + drop::<NoCopy>(x); + }; + f(); } diff --git a/tests/ui/pattern/deref-patterns/deref-box.rs b/tests/ui/pattern/deref-patterns/deref-box.rs new file mode 100644 index 00000000000..2d0a8d01972 --- /dev/null +++ b/tests/ui/pattern/deref-patterns/deref-box.rs @@ -0,0 +1,37 @@ +//@ run-pass +//! Deref patterns on boxes are lowered using built-in derefs, rather than generic `Deref::deref` +//! and `DerefMut::deref_mut`. Test that they work as expected. + +#![feature(deref_patterns)] +#![expect(incomplete_features)] + +fn unbox_1<T>(b: Box<T>) -> T { + let deref!(x) = b else { unreachable!() }; + x +} + +fn unbox_2<T>(b: Box<(T,)>) -> T { + let (x,) = b else { unreachable!() }; + x +} + +fn unbox_separately<T>(b: Box<(T, T)>) -> (T, T) { + let (x, _) = b else { unreachable!() }; + let (_, y) = b else { unreachable!() }; + (x, y) +} + +fn main() { + // test that deref patterns can move out of boxes + let b1 = Box::new(0); + let b2 = Box::new((0,)); + assert_eq!(unbox_1(b1), unbox_2(b2)); + let b3 = Box::new((1, 2)); + assert_eq!(unbox_separately(b3), (1, 2)); + + // test that borrowing from a box also works + let mut b = "hi".to_owned().into_boxed_str(); + let deref!(ref mut s) = b else { unreachable!() }; + s.make_ascii_uppercase(); + assert_eq!(&*b, "HI"); +} diff --git a/tests/ui/pattern/deref-patterns/fake_borrows.rs b/tests/ui/pattern/deref-patterns/fake_borrows.rs index bf614d7d66f..fba2873fd02 100644 --- a/tests/ui/pattern/deref-patterns/fake_borrows.rs +++ b/tests/ui/pattern/deref-patterns/fake_borrows.rs @@ -3,6 +3,23 @@ #[rustfmt::skip] fn main() { + let mut v = vec![false]; + match v { + deref!([true]) => {} + _ if { v[0] = true; false } => {} + //~^ ERROR cannot borrow `v` as mutable because it is also borrowed as immutable + deref!([false]) => {} + _ => {}, + } + match v { + [true] => {} + _ if { v[0] = true; false } => {} + //~^ ERROR cannot borrow `v` as mutable because it is also borrowed as immutable + [false] => {} + _ => {}, + } + + // deref patterns on boxes are lowered specially; test them separately. let mut b = Box::new(false); match b { deref!(true) => {} diff --git a/tests/ui/pattern/deref-patterns/fake_borrows.stderr b/tests/ui/pattern/deref-patterns/fake_borrows.stderr index 8c060236d0d..7dc3001739e 100644 --- a/tests/ui/pattern/deref-patterns/fake_borrows.stderr +++ b/tests/ui/pattern/deref-patterns/fake_borrows.stderr @@ -1,6 +1,28 @@ -error[E0510]: cannot assign `*b` in match guard +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable --> $DIR/fake_borrows.rs:9:16 | +LL | match v { + | - immutable borrow occurs here +LL | deref!([true]) => {} +LL | _ if { v[0] = true; false } => {} + | ^ - immutable borrow later used here + | | + | mutable borrow occurs here + +error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable + --> $DIR/fake_borrows.rs:16:16 + | +LL | match v { + | - immutable borrow occurs here +LL | [true] => {} +LL | _ if { v[0] = true; false } => {} + | ^ - immutable borrow later used here + | | + | mutable borrow occurs here + +error[E0510]: cannot assign `*b` in match guard + --> $DIR/fake_borrows.rs:26:16 + | LL | match b { | - value is immutable in match guard LL | deref!(true) => {} @@ -8,7 +30,7 @@ LL | _ if { *b = true; false } => {} | ^^^^^^^^^ cannot assign error[E0510]: cannot assign `*b` in match guard - --> $DIR/fake_borrows.rs:16:16 + --> $DIR/fake_borrows.rs:33:16 | LL | match b { | - value is immutable in match guard @@ -16,6 +38,7 @@ LL | true => {} LL | _ if { *b = true; false } => {} | ^^^^^^^^^ cannot assign -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors -For more information about this error, try `rustc --explain E0510`. +Some errors have detailed explanations: E0502, E0510. +For more information about an error, try `rustc --explain E0502`. diff --git a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs index a9b8de86010..04c83d4c33f 100644 --- a/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs +++ b/tests/ui/pattern/deref-patterns/implicit-cow-deref.rs @@ -4,6 +4,7 @@ #![allow(incomplete_features)] use std::borrow::Cow; +use std::rc::Rc; fn main() { let cow: Cow<'static, [u8]> = Cow::Borrowed(&[1, 2, 3]); @@ -18,7 +19,7 @@ fn main() { Cow::Owned(_) => unreachable!(), } - match Box::new(&cow) { + match Rc::new(&cow) { Cow::Borrowed { 0: _ } => {} Cow::Owned { 0: _ } => unreachable!(), _ => unreachable!(), @@ -37,7 +38,7 @@ fn main() { Cow::Owned(_) => {} } - match Box::new(&cow_of_cow) { + match Rc::new(&cow_of_cow) { Cow::Borrowed { 0: _ } => unreachable!(), Cow::Owned { 0: _ } => {} _ => unreachable!(), diff --git a/tests/ui/proc-macro/quote/debug.stdout b/tests/ui/proc-macro/quote/debug.stdout index 6ebb3a37951..3acb472d9c0 100644 --- a/tests/ui/proc-macro/quote/debug.stdout +++ b/tests/ui/proc-macro/quote/debug.stdout @@ -32,12 +32,12 @@ fn main() { let mut iter = "\"world\"".parse::<crate::TokenStream>().unwrap().into_iter(); if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(2)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(2)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } }), &mut ts); crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', crate::Spacing::Alone)), &mut ts); @@ -51,12 +51,12 @@ fn main() { let mut iter = "r#\"raw\"literal\"#".parse::<crate::TokenStream>().unwrap().into_iter(); if let (Some(crate::TokenTree::Literal(mut lit)), None) = - (iter.next(), iter.next()) { - lit.set_span(crate::Span::recover_proc_macro_span(5)); - lit - } else { - ::core::panicking::panic("internal error: entered unreachable code") - } + (iter.next(), iter.next()) { + lit.set_span(crate::Span::recover_proc_macro_span(5)); + lit + } else { + ::core::panicking::panic("internal error: entered unreachable code") + } }), &mut ts); crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(';', crate::Spacing::Alone)), &mut ts); diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs index ae13f7c76ba..ed461af66aa 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.rs @@ -3,7 +3,7 @@ fn let_or_guard(x: Result<Option<i32>, ()>) { match x { Ok(opt) if let Some(4) = opt || false => {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions _ => {} } } diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr index 4b85fdd5050..0566d96fddc 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/ast-validate-guards.stderr @@ -1,11 +1,4 @@ -error: expected expression, found `let` statement - --> $DIR/ast-validate-guards.rs:5:20 - | -LL | Ok(opt) if let Some(4) = opt || false => {} - | ^^^^^^^^^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/ast-validate-guards.rs:5:38 | LL | Ok(opt) if let Some(4) = opt || false => {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr index 817e226bc45..141a6d255d0 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr index bab50c22c03..dda09de4c53 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.no_feature.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr index 943956feb4e..5b53691cbf5 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.nothing.stderr @@ -272,14 +272,7 @@ LL | if (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:121:16 - | -LL | if true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:121:13 | LL | if true || let 0 = 0 {} @@ -485,14 +478,7 @@ LL | while (let 0 = 0)? {} | = note: only supported directly in conditions of `if` and `while` expressions -error: expected expression, found `let` statement - --> $DIR/disallowed-positions.rs:212:19 - | -LL | while true || let 0 = 0 {} - | ^^^^^^^^^ - | - = note: only supported directly in conditions of `if` and `while` expressions -note: `||` operators are not supported in let chain expressions +error: `||` operators are not supported in let chain conditions --> $DIR/disallowed-positions.rs:212:16 | LL | while true || let 0 = 0 {} diff --git a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs index 0b0abe6ec17..65beccf2214 100644 --- a/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs +++ b/tests/ui/rfcs/rfc-2497-if-let-chains/disallowed-positions.rs @@ -119,7 +119,7 @@ fn nested_within_if_expr() { //~^ ERROR expected expression, found `let` statement if true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions if (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement if true && (true || let 0 = 0) {} @@ -210,7 +210,7 @@ fn nested_within_while_expr() { //~^ ERROR expected expression, found `let` statement while true || let 0 = 0 {} - //~^ ERROR expected expression, found `let` statement + //~^ ERROR `||` operators are not supported in let chain conditions while (true || let 0 = 0) {} //~^ ERROR expected expression, found `let` statement while true && (true || let 0 = 0) {} diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr new file mode 100644 index 00000000000..7e3df0c83f9 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.current.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr new file mode 100644 index 00000000000..7e3df0c83f9 --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.next.stderr @@ -0,0 +1,12 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/prefer-specializing-impl-over-default.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +warning: 1 warning emitted + diff --git a/tests/ui/specialization/prefer-specializing-impl-over-default.rs b/tests/ui/specialization/prefer-specializing-impl-over-default.rs new file mode 100644 index 00000000000..af6837b30ca --- /dev/null +++ b/tests/ui/specialization/prefer-specializing-impl-over-default.rs @@ -0,0 +1,29 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver +//@ check-pass +#![feature(specialization)] +//~^ WARN the feature `specialization` is incomplete + +trait WithAssoc: 'static { + type Assoc; +} +impl<T: 'static> WithAssoc for (T,) { + type Assoc = (); +} + +struct GenericArray<U: WithAssoc>(U::Assoc); + +trait AbiExample { + fn example(); +} +impl<U: WithAssoc> AbiExample for GenericArray<U> { + fn example() {} +} +impl<T> AbiExample for T { + default fn example() {} +} + +fn main() { + let _ = GenericArray::<((),)>::example(); +} diff --git a/tests/ui/specialization/specialization-default-projection.stderr b/tests/ui/specialization/specialization-default-projection.current.stderr index b8b81876d81..038c379c43e 100644 --- a/tests/ui/specialization/specialization-default-projection.stderr +++ b/tests/ui/specialization/specialization-default-projection.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-projection.rs:1:12 + --> $DIR/specialization-default-projection.rs:5:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:21:5 + --> $DIR/specialization-default-projection.rs:25:5 | LL | fn generic<T>() -> <T as Foo>::Assoc { | ----------------- expected `<T as Foo>::Assoc` because of return type @@ -23,7 +23,7 @@ LL | () = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html error[E0308]: mismatched types - --> $DIR/specialization-default-projection.rs:28:5 + --> $DIR/specialization-default-projection.rs:32:5 | LL | fn monomorphic() -> () { | -- expected `()` because of return type diff --git a/tests/ui/specialization/specialization-default-projection.next.stderr b/tests/ui/specialization/specialization-default-projection.next.stderr new file mode 100644 index 00000000000..9111f173a9c --- /dev/null +++ b/tests/ui/specialization/specialization-default-projection.next.stderr @@ -0,0 +1,43 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-projection.rs:5:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:25:5 + | +LL | fn generic<T>() -> <T as Foo>::Assoc { + | ----------------- expected `<T as Foo>::Assoc` because of return type +... +LL | () + | ^^ types differ + | + = note: expected associated type `<T as Foo>::Assoc` + found unit type `()` + = help: consider constraining the associated type `<T as Foo>::Assoc` to `()` or calling a method that returns `<T as Foo>::Assoc` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error[E0308]: mismatched types + --> $DIR/specialization-default-projection.rs:32:5 + | +LL | fn monomorphic() -> () { + | -- expected `()` because of return type +... +LL | generic::<()>() + | ^^^^^^^^^^^^^^^- help: consider using a semicolon here: `;` + | | + | types differ + | + = note: expected unit type `()` + found associated type `<() as Foo>::Assoc` + = help: consider constraining the associated type `<() as Foo>::Assoc` to `()` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-projection.rs b/tests/ui/specialization/specialization-default-projection.rs index 7f3ae951287..4f69ccb5974 100644 --- a/tests/ui/specialization/specialization-default-projection.rs +++ b/tests/ui/specialization/specialization-default-projection.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + #![feature(specialization)] //~ WARN the feature `specialization` is incomplete // Make sure we can't project defaulted associated types diff --git a/tests/ui/specialization/specialization-default-types.stderr b/tests/ui/specialization/specialization-default-types.current.stderr index 774ac953617..67477f9a6d5 100644 --- a/tests/ui/specialization/specialization-default-types.stderr +++ b/tests/ui/specialization/specialization-default-types.current.stderr @@ -1,5 +1,5 @@ warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes - --> $DIR/specialization-default-types.rs:5:12 + --> $DIR/specialization-default-types.rs:9:12 | LL | #![feature(specialization)] | ^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | #![feature(specialization)] = note: `#[warn(incomplete_features)]` on by default error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:15:9 + --> $DIR/specialization-default-types.rs:19:9 | LL | default type Output = Box<T>; | ----------------------------- associated type is `default` and may be overridden @@ -22,7 +22,7 @@ LL | Box::new(self) found struct `Box<T>` error[E0308]: mismatched types - --> $DIR/specialization-default-types.rs:25:5 + --> $DIR/specialization-default-types.rs:29:5 | LL | fn trouble<T>(t: T) -> Box<T> { | ------ expected `Box<T>` because of return type diff --git a/tests/ui/specialization/specialization-default-types.next.stderr b/tests/ui/specialization/specialization-default-types.next.stderr new file mode 100644 index 00000000000..4f7c4765446 --- /dev/null +++ b/tests/ui/specialization/specialization-default-types.next.stderr @@ -0,0 +1,39 @@ +warning: the feature `specialization` is incomplete and may not be safe to use and/or cause compiler crashes + --> $DIR/specialization-default-types.rs:9:12 + | +LL | #![feature(specialization)] + | ^^^^^^^^^^^^^^ + | + = note: see issue #31844 <https://github.com/rust-lang/rust/issues/31844> for more information + = help: consider using `min_specialization` instead, which is more stable and complete + = note: `#[warn(incomplete_features)]` on by default + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:19:9 + | +LL | default type Output = Box<T>; + | ----------------------------- associated type is `default` and may be overridden +LL | default fn generate(self) -> Self::Output { + | ------------ expected `<T as Example>::Output` because of return type +LL | Box::new(self) + | ^^^^^^^^^^^^^^ types differ + | + = note: expected associated type `<T as Example>::Output` + found struct `Box<T>` + +error[E0308]: mismatched types + --> $DIR/specialization-default-types.rs:29:5 + | +LL | fn trouble<T>(t: T) -> Box<T> { + | ------ expected `Box<T>` because of return type +LL | Example::generate(t) + | ^^^^^^^^^^^^^^^^^^^^ types differ + | + = note: expected struct `Box<T>` + found associated type `<T as Example>::Output` + = help: consider constraining the associated type `<T as Example>::Output` to `Box<T>` + = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html + +error: aborting due to 2 previous errors; 1 warning emitted + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/specialization/specialization-default-types.rs b/tests/ui/specialization/specialization-default-types.rs index 346471f11e4..77817abea12 100644 --- a/tests/ui/specialization/specialization-default-types.rs +++ b/tests/ui/specialization/specialization-default-types.rs @@ -1,3 +1,7 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@[next] compile-flags: -Znext-solver + // It should not be possible to use the concrete value of a defaulted // associated type in the impl defining it -- otherwise, what happens // if it's overridden? diff --git a/tests/ui/statics/issue-15261.rs b/tests/ui/statics/issue-15261.rs index ed79a201488..f8c5ebaa548 100644 --- a/tests/ui/statics/issue-15261.rs +++ b/tests/ui/statics/issue-15261.rs @@ -2,10 +2,9 @@ #![allow(dead_code)] #![allow(non_upper_case_globals)] - static mut n_mut: usize = 0; static n: &'static usize = unsafe { &n_mut }; -//~^ WARN shared reference to mutable static is discouraged [static_mut_refs] +//~^ WARN shared reference to mutable static [static_mut_refs] fn main() {} diff --git a/tests/ui/statics/issue-15261.stderr b/tests/ui/statics/issue-15261.stderr index 7e6aebcbb1f..d2dd762aa66 100644 --- a/tests/ui/statics/issue-15261.stderr +++ b/tests/ui/statics/issue-15261.stderr @@ -1,5 +1,5 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/issue-15261.rs:8:37 +warning: creating a shared reference to mutable static + --> $DIR/issue-15261.rs:7:37 | LL | static n: &'static usize = unsafe { &n_mut }; | ^^^^^^ shared reference to mutable static diff --git a/tests/ui/statics/static-mut-shared-parens.rs b/tests/ui/statics/static-mut-shared-parens.rs index 8e58152e27a..7eda7d67d92 100644 --- a/tests/ui/statics/static-mut-shared-parens.rs +++ b/tests/ui/statics/static-mut-shared-parens.rs @@ -1,13 +1,12 @@ //Missing paren in diagnostic msg: https://github.com/rust-lang/rust/issues/131977 //@check-pass - static mut TEST: usize = 0; fn main() { let _ = unsafe { (&TEST) as *const usize }; - //~^WARN creating a shared reference to mutable static is discouraged + //~^WARN creating a shared reference to mutable static - let _ = unsafe { ((&mut TEST)) as *const usize }; - //~^WARN creating a mutable reference to mutable static is discouraged + let _ = unsafe { (&mut TEST) as *const usize }; + //~^WARN creating a mutable reference to mutable static } diff --git a/tests/ui/statics/static-mut-shared-parens.stderr b/tests/ui/statics/static-mut-shared-parens.stderr index 30a586c286a..3825e8efc42 100644 --- a/tests/ui/statics/static-mut-shared-parens.stderr +++ b/tests/ui/statics/static-mut-shared-parens.stderr @@ -1,5 +1,5 @@ -warning: creating a shared reference to mutable static is discouraged - --> $DIR/static-mut-shared-parens.rs:8:22 +warning: creating a shared reference to mutable static + --> $DIR/static-mut-shared-parens.rs:7:22 | LL | let _ = unsafe { (&TEST) as *const usize }; | ^^^^^^^ shared reference to mutable static @@ -12,18 +12,18 @@ help: use `&raw const` instead to create a raw pointer LL | let _ = unsafe { (&raw const TEST) as *const usize }; | +++++++++ -warning: creating a mutable reference to mutable static is discouraged - --> $DIR/static-mut-shared-parens.rs:11:22 +warning: creating a mutable reference to mutable static + --> $DIR/static-mut-shared-parens.rs:10:22 | -LL | let _ = unsafe { ((&mut TEST)) as *const usize }; - | ^^^^^^^^^^^^^ mutable reference to mutable static +LL | let _ = unsafe { (&mut TEST) as *const usize }; + | ^^^^^^^^^^^ mutable reference to mutable static | = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: mutable references to mutable statics are dangerous; it's undefined behavior if any other pointer to the static is used or if any other reference is created for the static while the mutable reference lives help: use `&raw mut` instead to create a raw pointer | -LL | let _ = unsafe { ((&raw mut TEST)) as *const usize }; - | +++ +LL | let _ = unsafe { (&raw mut TEST) as *const usize }; + | +++ warning: 2 warnings emitted diff --git a/tests/ui/statics/static-mut-xc.rs b/tests/ui/statics/static-mut-xc.rs index c23cc822ce7..878f5a8e0f7 100644 --- a/tests/ui/statics/static-mut-xc.rs +++ b/tests/ui/statics/static-mut-xc.rs @@ -17,23 +17,23 @@ fn static_bound_set(a: &'static mut isize) { unsafe fn run() { assert_eq!(static_mut_xc::a, 3); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a = 4; assert_eq!(static_mut_xc::a, 4); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a += 1; assert_eq!(static_mut_xc::a, 5); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a *= 3; assert_eq!(static_mut_xc::a, 15); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_mut_xc::a = -3; assert_eq!(static_mut_xc::a, -3); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] static_bound(&static_mut_xc::a); - //~^ WARN shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN shared reference to mutable static [static_mut_refs] static_bound_set(&mut static_mut_xc::a); - //~^ WARN mutable reference to mutable static is discouraged [static_mut_refs] + //~^ WARN mutable reference to mutable static [static_mut_refs] } pub fn main() { diff --git a/tests/ui/statics/static-mut-xc.stderr b/tests/ui/statics/static-mut-xc.stderr index 69f334a5636..2d7a0553e92 100644 --- a/tests/ui/statics/static-mut-xc.stderr +++ b/tests/ui/statics/static-mut-xc.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:19:16 | LL | assert_eq!(static_mut_xc::a, 3); @@ -8,7 +8,7 @@ LL | assert_eq!(static_mut_xc::a, 3); = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives = note: `#[warn(static_mut_refs)]` on by default -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:22:16 | LL | assert_eq!(static_mut_xc::a, 4); @@ -17,7 +17,7 @@ LL | assert_eq!(static_mut_xc::a, 4); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:25:16 | LL | assert_eq!(static_mut_xc::a, 5); @@ -26,7 +26,7 @@ LL | assert_eq!(static_mut_xc::a, 5); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:28:16 | LL | assert_eq!(static_mut_xc::a, 15); @@ -35,7 +35,7 @@ LL | assert_eq!(static_mut_xc::a, 15); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:31:16 | LL | assert_eq!(static_mut_xc::a, -3); @@ -44,7 +44,7 @@ LL | assert_eq!(static_mut_xc::a, -3); = note: for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html> = note: shared references to mutable statics are dangerous; it's undefined behavior if the static is mutated or if a mutable reference is created for it while the shared reference lives -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-mut-xc.rs:33:18 | LL | static_bound(&static_mut_xc::a); @@ -57,7 +57,7 @@ help: use `&raw const` instead to create a raw pointer LL | static_bound(&raw const static_mut_xc::a); | +++++++++ -warning: creating a mutable reference to mutable static is discouraged +warning: creating a mutable reference to mutable static --> $DIR/static-mut-xc.rs:35:22 | LL | static_bound_set(&mut static_mut_xc::a); diff --git a/tests/ui/statics/static-recursive.rs b/tests/ui/statics/static-recursive.rs index da23b54d1fc..dab60dd8641 100644 --- a/tests/ui/statics/static-recursive.rs +++ b/tests/ui/statics/static-recursive.rs @@ -1,7 +1,7 @@ //@ run-pass static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; -//~^ WARN shared reference to mutable static is discouraged [static_mut_refs] +//~^ WARN shared reference to mutable static [static_mut_refs] struct StaticDoubleLinked { prev: &'static StaticDoubleLinked, @@ -17,7 +17,7 @@ static L3: StaticDoubleLinked = StaticDoubleLinked { prev: &L2, next: &L1, data: pub fn main() { unsafe { assert_eq!(S, *(S as *const *const u8)); - //~^ WARN creating a shared reference to mutable static is discouraged [static_mut_refs] + //~^ WARN creating a shared reference to mutable static [static_mut_refs] } let mut test_vec = Vec::new(); diff --git a/tests/ui/statics/static-recursive.stderr b/tests/ui/statics/static-recursive.stderr index 039934dfc69..252807e2e5d 100644 --- a/tests/ui/statics/static-recursive.stderr +++ b/tests/ui/statics/static-recursive.stderr @@ -1,4 +1,4 @@ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-recursive.rs:3:36 | LL | static mut S: *const u8 = unsafe { &S as *const *const u8 as *const u8 }; @@ -12,7 +12,7 @@ help: use `&raw const` instead to create a raw pointer LL | static mut S: *const u8 = unsafe { &raw const S as *const *const u8 as *const u8 }; | +++++++++ -warning: creating a shared reference to mutable static is discouraged +warning: creating a shared reference to mutable static --> $DIR/static-recursive.rs:19:20 | LL | assert_eq!(S, *(S as *const *const u8)); diff --git a/tests/ui/traits/next-solver/coerce-depth.rs b/tests/ui/traits/next-solver/coerce-depth.rs new file mode 100644 index 00000000000..c8fc3fcab59 --- /dev/null +++ b/tests/ui/traits/next-solver/coerce-depth.rs @@ -0,0 +1,31 @@ +//@ check-pass +//@ compile-flags: -Znext-solver + +// Ensure that a stack of coerce predicates doesn't end up overflowing when they get procesed +// in *reverse* order, which may require O(N) iterations of the fulfillment loop. + +#![recursion_limit = "16"] + +fn main() { + match 0 { + 0 => None, + 1 => None, + 2 => None, + 3 => None, + 4 => None, + 5 => None, + 6 => None, + 7 => None, + 8 => None, + 9 => None, + 10 => None, + 11 => None, + 12 => None, + 13 => None, + 14 => None, + 15 => None, + 16 => None, + 17 => None, + _ => Some(1u32), + }; +} diff --git a/tests/ui/traits/next-solver/specialization-transmute.rs b/tests/ui/traits/next-solver/specialization-transmute.rs index 376fa22ae19..f1447cd6a9e 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.rs +++ b/tests/ui/traits/next-solver/specialization-transmute.rs @@ -10,11 +10,8 @@ trait Default { impl<T> Default for T { default type Id = T; - // This will be fixed by #111994 fn intu(&self) -> &Self::Id { - //~^ ERROR type annotations needed - //~| ERROR cannot normalize `<T as Default>::Id: '_` - self //~ ERROR cannot satisfy + self //~ ERROR mismatched types } } @@ -25,6 +22,7 @@ fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U { use std::num::NonZero; fn main() { - let s = transmute::<u8, Option<NonZero<u8>>>(0); //~ ERROR cannot satisfy + let s = transmute::<u8, Option<NonZero<u8>>>(0); + //~^ ERROR type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>` assert_eq!(s, None); } diff --git a/tests/ui/traits/next-solver/specialization-transmute.stderr b/tests/ui/traits/next-solver/specialization-transmute.stderr index eeb101911c4..8bd290ea197 100644 --- a/tests/ui/traits/next-solver/specialization-transmute.stderr +++ b/tests/ui/traits/next-solver/specialization-transmute.stderr @@ -8,37 +8,32 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error: cannot normalize `<T as Default>::Id: '_` - --> $DIR/specialization-transmute.rs:14:5 +error[E0308]: mismatched types + --> $DIR/specialization-transmute.rs:14:9 | LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -error[E0282]: type annotations needed - --> $DIR/specialization-transmute.rs:14:23 - | -LL | fn intu(&self) -> &Self::Id { - | ^^^^^^^^^ cannot infer type for reference `&<T as Default>::Id` - -error[E0284]: type annotations needed: cannot satisfy `<T as Default>::Id normalizes-to T` - --> $DIR/specialization-transmute.rs:17:9 - | + | --------- expected `&<T as Default>::Id` because of return type LL | self - | ^^^^ cannot satisfy `<T as Default>::Id normalizes-to T` + | ^^^^ types differ + | + = note: expected reference `&<T as Default>::Id` + found reference `&T` -error[E0284]: type annotations needed: cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` - --> $DIR/specialization-transmute.rs:28:13 +error[E0271]: type mismatch resolving `<u8 as Default>::Id == Option<NonZero<u8>>` + --> $DIR/specialization-transmute.rs:25:50 | LL | let s = transmute::<u8, Option<NonZero<u8>>>(0); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot satisfy `<u8 as Default>::Id normalizes-to Option<NonZero<u8>>` + | ------------------------------------ ^ types differ + | | + | required by a bound introduced by this call | note: required by a bound in `transmute` - --> $DIR/specialization-transmute.rs:21:25 + --> $DIR/specialization-transmute.rs:18:25 | LL | fn transmute<T: Default<Id = U>, U: Copy>(t: T) -> U { | ^^^^^^ required by this bound in `transmute` -error: aborting due to 4 previous errors; 1 warning emitted +error: aborting due to 2 previous errors; 1 warning emitted -Some errors have detailed explanations: E0282, E0284. -For more information about an error, try `rustc --explain E0282`. +Some errors have detailed explanations: E0271, E0308. +For more information about an error, try `rustc --explain E0271`. diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.rs b/tests/ui/traits/next-solver/specialization-unconstrained.rs index 2bbe7840110..6835c0764d6 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.rs +++ b/tests/ui/traits/next-solver/specialization-unconstrained.rs @@ -18,5 +18,5 @@ fn test<T: Default<Id = U>, U>() {} fn main() { test::<u32, ()>(); - //~^ ERROR cannot satisfy `<u32 as Default>::Id normalizes-to ()` + //~^ ERROR type mismatch resolving `<u32 as Default>::Id == ()` } diff --git a/tests/ui/traits/next-solver/specialization-unconstrained.stderr b/tests/ui/traits/next-solver/specialization-unconstrained.stderr index e1d785b554b..1bcf5eddb5b 100644 --- a/tests/ui/traits/next-solver/specialization-unconstrained.stderr +++ b/tests/ui/traits/next-solver/specialization-unconstrained.stderr @@ -8,11 +8,11 @@ LL | #![feature(specialization)] = help: consider using `min_specialization` instead, which is more stable and complete = note: `#[warn(incomplete_features)]` on by default -error[E0284]: type annotations needed: cannot satisfy `<u32 as Default>::Id normalizes-to ()` - --> $DIR/specialization-unconstrained.rs:20:5 +error[E0271]: type mismatch resolving `<u32 as Default>::Id == ()` + --> $DIR/specialization-unconstrained.rs:20:12 | LL | test::<u32, ()>(); - | ^^^^^^^^^^^^^^^^^ cannot satisfy `<u32 as Default>::Id normalizes-to ()` + | ^^^ types differ | note: required by a bound in `test` --> $DIR/specialization-unconstrained.rs:17:20 @@ -22,4 +22,4 @@ LL | fn test<T: Default<Id = U>, U>() {} error: aborting due to 1 previous error; 1 warning emitted -For more information about this error, try `rustc --explain E0284`. +For more information about this error, try `rustc --explain E0271`. diff --git a/tests/ui/transmutability/char.rs b/tests/ui/transmutability/char.rs new file mode 100644 index 00000000000..55a61537329 --- /dev/null +++ b/tests/ui/transmutability/char.rs @@ -0,0 +1,41 @@ +#![feature(never_type)] +#![feature(transmutability)] + +use std::mem::{Assume, TransmuteFrom}; + +pub fn is_transmutable<Src, Dst>() +where + Dst: TransmuteFrom<Src, { Assume::SAFETY }>, +{ +} + +fn main() { + is_transmutable::<char, u32>(); + + // `char`s can be in the following ranges: + // - [0, 0xD7FF] + // - [0xE000, 10FFFF] + // + // `Char` has variants whose tags are in the top and bottom of each range. + // It also has generic variants which are out of bounds of these ranges, but + // are generic on types which may be set to `!` to "disable" them in the + // transmutability analysis. + #[repr(u32)] + enum Char<B, C, D> { + A = 0, + B = 0xD7FF, + OverB(B) = 0xD800, + UnderC(C) = 0xDFFF, + C = 0xE000, + D = 0x10FFFF, + OverD(D) = 0x110000, + } + + is_transmutable::<Char<!, !, !>, char>(); + is_transmutable::<Char<(), !, !>, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::<Char<!, (), !>, char>(); + //~^ ERROR cannot be safely transmuted into `char` + is_transmutable::<Char<!, !, ()>, char>(); + //~^ ERROR cannot be safely transmuted into `char` +} diff --git a/tests/ui/transmutability/char.stderr b/tests/ui/transmutability/char.stderr new file mode 100644 index 00000000000..98e7ae9c0d4 --- /dev/null +++ b/tests/ui/transmutability/char.stderr @@ -0,0 +1,48 @@ +error[E0277]: `main::Char<(), !, !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:35:39 + | +LL | is_transmutable::<Char<(), !, !>, char>(); + | ^^^^ at least one value of `main::Char<(), !, !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char<!, (), !>` cannot be safely transmuted into `char` + --> $DIR/char.rs:37:39 + | +LL | is_transmutable::<Char<!, (), !>, char>(); + | ^^^^ at least one value of `main::Char<!, (), !>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error[E0277]: `main::Char<!, !, ()>` cannot be safely transmuted into `char` + --> $DIR/char.rs:39:39 + | +LL | is_transmutable::<Char<!, !, ()>, char>(); + | ^^^^ at least one value of `main::Char<!, !, ()>` isn't a bit-valid value of `char` + | +note: required by a bound in `is_transmutable` + --> $DIR/char.rs:8:10 + | +LL | pub fn is_transmutable<Src, Dst>() + | --------------- required by a bound in this function +LL | where +LL | Dst: TransmuteFrom<Src, { Assume::SAFETY }>, + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `is_transmutable` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. |
