From d3299af26d8e884c484c5d06991b4cbf7fced590 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 12 Mar 2024 13:29:15 +0100 Subject: [PATCH 01/29] transmute: caution against int2ptr transmutation --- library/core/src/intrinsics.rs | 37 ++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index 86b9a39d68a67..ce498561eeb12 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1169,14 +1169,6 @@ extern "rust-intrinsic" { /// may lead to unexpected and unstable compilation results. This makes `transmute` **incredibly /// unsafe**. `transmute` should be the absolute last resort. /// - /// Transmuting pointers *to* integers in a `const` context is [undefined behavior][ub], - /// unless the pointer was originally created *from* an integer. - /// (That includes this function specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], - /// but also semantically-equivalent conversions such as punning through `repr(C)` union fields.) - /// Any attempt to use the resulting value for integer operations will abort const-evaluation. - /// (And even outside `const`, such transmutation is touching on many unspecified aspects of the - /// Rust memory model and should be avoided. See below for alternatives.) - /// /// Because `transmute` is a by-value operation, alignment of the *transmuted values /// themselves* is not a concern. As with any other function, the compiler already ensures /// both `Src` and `Dst` are properly aligned. However, when transmuting values that *point @@ -1187,6 +1179,35 @@ extern "rust-intrinsic" { /// /// [ub]: ../../reference/behavior-considered-undefined.html /// + /// # Transmutation between pointers and integers + /// + /// Special care has to be taken when transmuting between pointers and integers, e.g. + /// transmuting between `*const ()` and `usize`. + /// + /// Transmuting *pointers to integers* in a `const` context is [undefined behavior][ub], unless + /// the pointer was originally created *from* an integer. (That includes this function + /// specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], + /// but also semantically-equivalent conversions such as punning through `repr(C)` union + /// fields.) Any attempt to use the resulting value for integer operations will abort + /// const-evaluation. (And even outside `const`, such transmutation is touching on many + /// unspecified aspects of the Rust memory model and should be avoided. See below for + /// alternatives.) + /// + /// Transmuting *integers to pointers* is a largely unspecified operation. It is likely *not* + /// equivalent to an `as` cast. Doing non-zero-sized memory accesses with a pointer constructed + /// this way is currently considered undefined behavior. + /// + /// All this also applies when the integer is nested inside an array, tuple, struct, or enum. + /// However, `MaybeUninit` is not considered an integer type for the purpose of this + /// section. Transmuting `*const ()` to `MaybeUninit` is fine---but then calling + /// `assume_init()` on that result is considered as completing the pointer-to-integer transmute + /// and thus runs into the issues discussed above. + /// + /// In particular, doing a pointer-to-integer-to-pointer roundtrip via `transmute` is *not* a + /// lossless process. If you want to round-trip a pointer through an integer in a way that you + /// can get back the original pointer, you need to use `as` casts, or replace the integer type + /// by `MaybeUninit<$int>` (and never call `assume_init()`). + /// /// # Examples /// /// There are a few things that `transmute` is really useful for. From 982918f4935fd4677af06d42e0d0b298bfb1c243 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Mar 2024 00:35:57 +0000 Subject: [PATCH 02/29] Handle str literals written with `'` lexed as lifetime Given `'hello world'` and `'1 str', provide a structured suggestion for a valid string literal: ``` error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-3.rs:2:26 | LL | println!('hello world'); | ^^^^ | help: if you meant to write a `str` literal, use double quotes | LL | println!("hello world"); | ~ ~ ``` ``` error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-1.rs:2:20 | LL | println!('1 + 1'); | ^^^^ | help: if you meant to write a `str` literal, use double quotes | LL | println!("1 + 1"); | ~ ~ ``` Fix #119685. --- compiler/rustc_lexer/src/cursor.rs | 2 +- compiler/rustc_parse/messages.ftl | 1 + compiler/rustc_parse/src/errors.rs | 11 +++++ compiler/rustc_parse/src/lexer/mod.rs | 46 +++++++++++++++++-- .../lexer/lex-bad-str-literal-as-char-1.fixed | 6 +++ .../ui/lexer/lex-bad-str-literal-as-char-1.rs | 6 +++ .../lex-bad-str-literal-as-char-1.stderr | 20 ++++++++ .../lexer/lex-bad-str-literal-as-char-2.fixed | 4 ++ .../ui/lexer/lex-bad-str-literal-as-char-2.rs | 4 ++ .../lex-bad-str-literal-as-char-2.stderr | 13 ++++++ .../lexer/lex-bad-str-literal-as-char-3.fixed | 4 ++ .../ui/lexer/lex-bad-str-literal-as-char-3.rs | 4 ++ .../lex-bad-str-literal-as-char-3.stderr | 14 ++++++ 13 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-1.rs create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-2.rs create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.rs create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index aba7f95487e9d..8d8cc9ecc3e9f 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -46,7 +46,7 @@ impl<'a> Cursor<'a> { /// If requested position doesn't exist, `EOF_CHAR` is returned. /// However, getting `EOF_CHAR` doesn't always mean actual end of file, /// it should be checked with `is_eof` method. - pub(crate) fn first(&self) -> char { + pub fn first(&self) -> char { // `.next()` optimizes better than `.nth(0)` self.chars.clone().next().unwrap_or(EOF_CHAR) } diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index a100e2d47bbbb..888c621f02afd 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -835,6 +835,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string + .suggestion_str = if you meant to write a `str` literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 32b56bb7e877e..140eb6cd18752 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1994,6 +1994,17 @@ pub enum UnknownPrefixSugg { style = "verbose" )] Whitespace(#[primary_span] Span), + #[multipart_suggestion( + parse_suggestion_str, + applicability = "maybe-incorrect", + style = "verbose" + )] + MeantStr { + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + }, } #[derive(Diagnostic)] diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index f57945a52df37..659e0b63d2ed8 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -63,6 +63,7 @@ pub(crate) fn parse_token_trees<'psess, 'src>( cursor, override_span, nbsp_is_whitespace: false, + last_lifetime: None, }; let (stream, res, unmatched_delims) = tokentrees::TokenTreesReader::parse_all_token_trees(string_reader); @@ -105,6 +106,10 @@ struct StringReader<'psess, 'src> { /// in this file, it's safe to treat further occurrences of the non-breaking /// space character as whitespace. nbsp_is_whitespace: bool, + + /// Track the `Span` for the leading `'` of the last lifetime. Used for + /// diagnostics to detect possible typo where `"` was meant. + last_lifetime: Option, } impl<'psess, 'src> StringReader<'psess, 'src> { @@ -130,6 +135,18 @@ impl<'psess, 'src> StringReader<'psess, 'src> { debug!("next_token: {:?}({:?})", token.kind, self.str_from(start)); + if let rustc_lexer::TokenKind::Semi + | rustc_lexer::TokenKind::LineComment { .. } + | rustc_lexer::TokenKind::BlockComment { .. } + | rustc_lexer::TokenKind::CloseParen + | rustc_lexer::TokenKind::CloseBrace + | rustc_lexer::TokenKind::CloseBracket = token.kind + { + // Heuristic: we assume that it is unlikely we're dealing with an unterminated + // string surrounded by single quotes. + self.last_lifetime = None; + } + // Now "cook" the token, converting the simple `rustc_lexer::TokenKind` enum into a // rich `rustc_ast::TokenKind`. This turns strings into interned symbols and runs // additional validation. @@ -247,6 +264,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { // expansion purposes. See #12512 for the gory details of why // this is necessary. let lifetime_name = self.str_from(start); + self.last_lifetime = Some(self.mk_sp(start, start + BytePos(1))); if starts_with_number { let span = self.mk_sp(start, self.pos); self.dcx().struct_err("lifetimes cannot start with a number") @@ -395,10 +413,21 @@ impl<'psess, 'src> StringReader<'psess, 'src> { match kind { rustc_lexer::LiteralKind::Char { terminated } => { if !terminated { - self.dcx() + let mut err = self + .dcx() .struct_span_fatal(self.mk_sp(start, end), "unterminated character literal") - .with_code(E0762) - .emit() + .with_code(E0762); + if let Some(lt_sp) = self.last_lifetime { + err.multipart_suggestion( + "if you meant to write a `str` literal, use double quotes", + vec![ + (lt_sp, "\"".to_string()), + (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), + ], + Applicability::MaybeIncorrect, + ); + } + err.emit() } self.cook_unicode(token::Char, Mode::Char, start, end, 1, 1) // ' ' } @@ -673,7 +702,16 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { - Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + if self.cursor.first() == '\'' + && let Some(start) = self.last_lifetime + { + Some(errors::UnknownPrefixSugg::MeantStr { + start, + end: self.mk_sp(self.pos, self.pos + BytePos(1)), + }) + } else { + Some(errors::UnknownPrefixSugg::Whitespace(prefix_span.shrink_to_hi())) + } } else { None }; diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed new file mode 100644 index 0000000000000..b12139b0b40e9 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.fixed @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!("1 + 1"); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs new file mode 100644 index 0000000000000..6548792f33b4f --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.rs @@ -0,0 +1,6 @@ +//@ run-rustfix +fn main() { + println!('1 + 1'); + //~^ ERROR unterminated character literal + //~| ERROR lifetimes cannot start with a number +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr new file mode 100644 index 0000000000000..675624cfa9415 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -0,0 +1,20 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:20 + | +LL | println!('1 + 1'); + | ^^^ + | +help: if you meant to write a `str` literal, use double quotes + | +LL | println!("1 + 1"); + | ~ ~ + +error: lifetimes cannot start with a number + --> $DIR/lex-bad-str-literal-as-char-1.rs:3:14 + | +LL | println!('1 + 1'); + | ^^ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed new file mode 100644 index 0000000000000..3ccec537c6c34 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.fixed @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(" 1 + 1"); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs new file mode 100644 index 0000000000000..8af72e47dbb4b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.rs @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!(' 1 + 1'); //~ ERROR character literal may only contain one codepoint +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr new file mode 100644 index 0000000000000..8445d0595f3ed --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -0,0 +1,13 @@ +error: character literal may only contain one codepoint + --> $DIR/lex-bad-str-literal-as-char-2.rs:3:14 + | +LL | println!(' 1 + 1'); + | ^^^^^^^^ + | +help: if you meant to write a `str` literal, use double quotes + | +LL | println!(" 1 + 1"); + | ~~~~~~~~ + +error: aborting due to 1 previous error + diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed new file mode 100644 index 0000000000000..3fbe0ea1dabef --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!("hello world"); //~ ERROR unterminated character literal +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs new file mode 100644 index 0000000000000..289f3f1d6570f --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -0,0 +1,4 @@ +//@ run-rustfix +fn main() { + println!('hello world'); //~ ERROR unterminated character literal +} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr new file mode 100644 index 0000000000000..ebfbeac02600b --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:3:26 + | +LL | println!('hello world'); + | ^^^^ + | +help: if you meant to write a `str` literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. From 4a10b01f9504f8ad2ffb9b357845341f4fba6bf0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sat, 9 Mar 2024 01:07:23 +0000 Subject: [PATCH 03/29] Use shorter span for existing `'` -> `"` structured suggestion --- compiler/rustc_infer/src/errors/mod.rs | 13 +++++------- .../src/infer/error_reporting/mod.rs | 14 ++++--------- compiler/rustc_parse/src/errors.rs | 11 +++++++++- .../src/lexer/unescape_error_reporting.rs | 20 ++++++++++++++----- tests/ui/inference/str-as-char.stderr | 4 ++-- tests/ui/issues/issue-23589.stderr | 2 +- tests/ui/lexer/lex-bad-char-literals-2.stderr | 2 +- tests/ui/lexer/lex-bad-char-literals-3.stderr | 4 ++-- tests/ui/lexer/lex-bad-char-literals-5.stderr | 4 ++-- tests/ui/lexer/lex-bad-char-literals-6.stderr | 6 +++--- .../lex-bad-str-literal-as-char-2.stderr | 2 +- tests/ui/parser/issues/issue-64732.stderr | 4 ++-- .../parser/unicode-character-literal.stderr | 4 ++-- tests/ui/str/str-as-char.stderr | 2 +- 14 files changed, 51 insertions(+), 41 deletions(-) diff --git a/compiler/rustc_infer/src/errors/mod.rs b/compiler/rustc_infer/src/errors/mod.rs index a3cf0d8e5208a..d0b1f2848ff3f 100644 --- a/compiler/rustc_infer/src/errors/mod.rs +++ b/compiler/rustc_infer/src/errors/mod.rs @@ -1339,15 +1339,12 @@ pub enum TypeErrorAdditionalDiags { span: Span, code: String, }, - #[suggestion( - infer_meant_str_literal, - code = "\"{code}\"", - applicability = "machine-applicable" - )] + #[multipart_suggestion(infer_meant_str_literal, applicability = "machine-applicable")] MeantStrLiteral { - #[primary_span] - span: Span, - code: String, + #[suggestion_part(code = "\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, }, #[suggestion( infer_consider_specifying_length, diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 5d2a95593cd25..4b104756b181f 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -2078,16 +2078,10 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { // If a string was expected and the found expression is a character literal, // perhaps the user meant to write `"s"` to specify a string literal. (ty::Ref(_, r, _), ty::Char) if r.is_str() => { - if let Ok(code) = self.tcx.sess().source_map().span_to_snippet(span) { - if let Some(code) = - code.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) - { - suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { - span, - code: escape_literal(code), - }) - } - } + suggestions.push(TypeErrorAdditionalDiags::MeantStrLiteral { + start: span.with_hi(span.lo() + BytePos(1)), + end: span.with_lo(span.hi() - BytePos(1)), + }) } // For code `if Some(..) = expr `, the type mismatch may be expected `bool` but found `()`, // we try to suggest to add the missing `let` for `if let Some(..) = expr` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 140eb6cd18752..d12818444fc5f 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2216,12 +2216,21 @@ pub enum MoreThanOneCharSugg { ch: String, }, #[suggestion(parse_use_double_quotes, code = "{sugg}", applicability = "machine-applicable")] - Quotes { + QuotesFull { #[primary_span] span: Span, is_byte: bool, sugg: String, }, + #[multipart_suggestion(parse_use_double_quotes, applicability = "machine-applicable")] + Quotes { + #[suggestion_part(code = "{prefix}\"")] + start: Span, + #[suggestion_part(code = "\"")] + end: Span, + is_byte: bool, + prefix: &'static str, + }, } #[derive(Subdiagnostic)] diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 3ebad6a9fd73e..1ac6b6fe11566 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,11 +95,21 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - let sugg = format!("{prefix}\"{escaped}\""); - MoreThanOneCharSugg::Quotes { - span: full_lit_span, - is_byte: mode == Mode::Byte, - sugg, + if escaped.len() != lit.len() { + let sugg = format!("{prefix}\"{escaped}\""); + MoreThanOneCharSugg::QuotesFull { + span: full_lit_span, + is_byte: mode == Mode::Byte, + sugg, + } + } else { + MoreThanOneCharSugg::Quotes { + start: full_lit_span + .with_hi(full_lit_span.lo() + BytePos((prefix.len() + 1) as u32)), + end: full_lit_span.with_lo(full_lit_span.hi() - BytePos(1)), + is_byte: mode == Mode::Byte, + prefix, + } } }); dcx.emit_err(UnescapeError::MoreThanOneChar { diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 216f4cda69888..42302435c91bf 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -18,7 +18,7 @@ LL | let _: &str = '\"\"\"'; help: if you meant to write a `str` literal, use double quotes | LL | let _: &str = "\"\"\""; - | ~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/str-as-char.rs:10:19 @@ -42,7 +42,7 @@ LL | let _: &str = 'a'; help: if you meant to write a `str` literal, use double quotes | LL | let _: &str = "a"; - | ~~~ + | ~ ~ error: aborting due to 4 previous errors diff --git a/tests/ui/issues/issue-23589.stderr b/tests/ui/issues/issue-23589.stderr index 1a91f5e04dbce..bf055a7e31c59 100644 --- a/tests/ui/issues/issue-23589.stderr +++ b/tests/ui/issues/issue-23589.stderr @@ -18,7 +18,7 @@ LL | let v: Vec(&str) = vec!['1', '2']; help: if you meant to write a `str` literal, use double quotes | LL | let v: Vec(&str) = vec!["1", '2']; - | ~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-2.stderr b/tests/ui/lexer/lex-bad-char-literals-2.stderr index 1518a37ab53f3..af64e6efe45ff 100644 --- a/tests/ui/lexer/lex-bad-char-literals-2.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-2.stderr @@ -7,7 +7,7 @@ LL | 'nope' help: if you meant to write a `str` literal, use double quotes | LL | "nope" - | ~~~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/lexer/lex-bad-char-literals-3.stderr b/tests/ui/lexer/lex-bad-char-literals-3.stderr index 62a5e424cb469..312b42fc83174 100644 --- a/tests/ui/lexer/lex-bad-char-literals-3.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-3.stderr @@ -7,7 +7,7 @@ LL | static c: char = '●●'; help: if you meant to write a `str` literal, use double quotes | LL | static c: char = "●●"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-3.rs:5:20 @@ -18,7 +18,7 @@ LL | let ch: &str = '●●'; help: if you meant to write a `str` literal, use double quotes | LL | let ch: &str = "●●"; - | ~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-5.stderr b/tests/ui/lexer/lex-bad-char-literals-5.stderr index 184817a6579d0..185f460b10def 100644 --- a/tests/ui/lexer/lex-bad-char-literals-5.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-5.stderr @@ -7,7 +7,7 @@ LL | static c: char = '\x10\x10'; help: if you meant to write a `str` literal, use double quotes | LL | static c: char = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-5.rs:5:20 @@ -18,7 +18,7 @@ LL | let ch: &str = '\x10\x10'; help: if you meant to write a `str` literal, use double quotes | LL | let ch: &str = "\x10\x10"; - | ~~~~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/lexer/lex-bad-char-literals-6.stderr b/tests/ui/lexer/lex-bad-char-literals-6.stderr index 2fe30304a50d6..f49e5a095b34a 100644 --- a/tests/ui/lexer/lex-bad-char-literals-6.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-6.stderr @@ -7,7 +7,7 @@ LL | let x: &str = 'ab'; help: if you meant to write a `str` literal, use double quotes | LL | let x: &str = "ab"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:4:19 @@ -18,7 +18,7 @@ LL | let y: char = 'cd'; help: if you meant to write a `str` literal, use double quotes | LL | let y: char = "cd"; - | ~~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/lex-bad-char-literals-6.rs:6:13 @@ -29,7 +29,7 @@ LL | let z = 'ef'; help: if you meant to write a `str` literal, use double quotes | LL | let z = "ef"; - | ~~~~ + | ~ ~ error[E0308]: mismatched types --> $DIR/lex-bad-char-literals-6.rs:13:20 diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr index 8445d0595f3ed..06b38522ad2dd 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -7,7 +7,7 @@ LL | println!(' 1 + 1'); help: if you meant to write a `str` literal, use double quotes | LL | println!(" 1 + 1"); - | ~~~~~~~~ + | ~ ~ error: aborting due to 1 previous error diff --git a/tests/ui/parser/issues/issue-64732.stderr b/tests/ui/parser/issues/issue-64732.stderr index 8046254937763..8893fa8aae239 100644 --- a/tests/ui/parser/issues/issue-64732.stderr +++ b/tests/ui/parser/issues/issue-64732.stderr @@ -7,7 +7,7 @@ LL | let _foo = b'hello\0'; help: if you meant to write a byte string literal, use double quotes | LL | let _foo = b"hello\0"; - | ~~~~~~~~~~ + | ~~ ~ error: character literal may only contain one codepoint --> $DIR/issue-64732.rs:6:16 @@ -18,7 +18,7 @@ LL | let _bar = 'hello'; help: if you meant to write a `str` literal, use double quotes | LL | let _bar = "hello"; - | ~~~~~~~ + | ~ ~ error: aborting due to 2 previous errors diff --git a/tests/ui/parser/unicode-character-literal.stderr b/tests/ui/parser/unicode-character-literal.stderr index 5cd3bd0fe69d7..1104eaeb8d478 100644 --- a/tests/ui/parser/unicode-character-literal.stderr +++ b/tests/ui/parser/unicode-character-literal.stderr @@ -12,7 +12,7 @@ LL | let _spade = '♠️'; help: if you meant to write a `str` literal, use double quotes | LL | let _spade = "♠️"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:12:14 @@ -28,7 +28,7 @@ LL | let _s = 'ṩ̂̊'; help: if you meant to write a `str` literal, use double quotes | LL | let _s = "ṩ̂̊"; - | ~~~ + | ~ ~ error: character literal may only contain one codepoint --> $DIR/unicode-character-literal.rs:17:14 diff --git a/tests/ui/str/str-as-char.stderr b/tests/ui/str/str-as-char.stderr index 44ec079e92918..03b2309a3171a 100644 --- a/tests/ui/str/str-as-char.stderr +++ b/tests/ui/str/str-as-char.stderr @@ -7,7 +7,7 @@ LL | println!('●●'); help: if you meant to write a `str` literal, use double quotes | LL | println!("●●"); - | ~~~~ + | ~ ~ error: aborting due to 1 previous error From 999a0dc300b7f95eb7d83666514c4ceae76020f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 13 Mar 2024 23:52:04 +0000 Subject: [PATCH 04/29] review comment: `str` -> string in messages --- compiler/rustc_infer/messages.ftl | 2 +- compiler/rustc_parse/messages.ftl | 4 ++-- compiler/rustc_parse/src/lexer/mod.rs | 2 +- tests/ui/inference/str-as-char.stderr | 8 ++++---- tests/ui/issues/issue-23589.stderr | 2 +- tests/ui/lexer/lex-bad-char-literals-2.stderr | 2 +- tests/ui/lexer/lex-bad-char-literals-3.stderr | 4 ++-- tests/ui/lexer/lex-bad-char-literals-5.stderr | 4 ++-- tests/ui/lexer/lex-bad-char-literals-6.stderr | 6 +++--- tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr | 2 +- tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr | 2 +- tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr | 2 +- tests/ui/parser/issues/issue-64732.rs | 2 +- tests/ui/parser/issues/issue-64732.stderr | 2 +- tests/ui/parser/unicode-character-literal.fixed | 4 ++-- tests/ui/parser/unicode-character-literal.rs | 4 ++-- tests/ui/parser/unicode-character-literal.stderr | 4 ++-- tests/ui/str/str-as-char.stderr | 2 +- 18 files changed, 29 insertions(+), 29 deletions(-) diff --git a/compiler/rustc_infer/messages.ftl b/compiler/rustc_infer/messages.ftl index e44a6ae3b3f2e..521c65c600918 100644 --- a/compiler/rustc_infer/messages.ftl +++ b/compiler/rustc_infer/messages.ftl @@ -169,7 +169,7 @@ infer_lifetime_param_suggestion_elided = each elided lifetime in input position infer_meant_byte_literal = if you meant to write a byte literal, prefix with `b` infer_meant_char_literal = if you meant to write a `char` literal, use single quotes -infer_meant_str_literal = if you meant to write a `str` literal, use double quotes +infer_meant_str_literal = if you meant to write a string literal, use double quotes infer_mismatched_static_lifetime = incompatible lifetime on type infer_more_targeted = {$has_param_name -> [true] `{$param_name}` diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 888c621f02afd..7f828c0274361 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -570,7 +570,7 @@ parse_more_than_one_char = character literal may only contain one codepoint .remove_non = consider removing the non-printing characters .use_double_quotes = if you meant to write a {$is_byte -> [true] byte string - *[false] `str` + *[false] string } literal, use double quotes parse_multiple_skipped_lines = multiple lines skipped by escaped newline @@ -835,7 +835,7 @@ parse_unknown_prefix = prefix `{$prefix}` is unknown .label = unknown prefix .note = prefixed identifiers and literals are reserved since Rust 2021 .suggestion_br = use `br` for a raw byte string - .suggestion_str = if you meant to write a `str` literal, use double quotes + .suggestion_str = if you meant to write a string literal, use double quotes .suggestion_whitespace = consider inserting whitespace here parse_unknown_start_of_token = unknown start of token: {$escaped} diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 659e0b63d2ed8..5583e49ba6011 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -419,7 +419,7 @@ impl<'psess, 'src> StringReader<'psess, 'src> { .with_code(E0762); if let Some(lt_sp) = self.last_lifetime { err.multipart_suggestion( - "if you meant to write a `str` literal, use double quotes", + "if you meant to write a string literal, use double quotes", vec![ (lt_sp, "\"".to_string()), (self.mk_sp(start, start + BytePos(1)), "\"".to_string()), diff --git a/tests/ui/inference/str-as-char.stderr b/tests/ui/inference/str-as-char.stderr index 42302435c91bf..4ca71c5f067fd 100644 --- a/tests/ui/inference/str-as-char.stderr +++ b/tests/ui/inference/str-as-char.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"""'; | ^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; | ~~~~~~~~ @@ -15,7 +15,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '\"\"\"'; | ^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\""; | ~ ~ @@ -26,7 +26,7 @@ error: character literal may only contain one codepoint LL | let _: &str = '"\"\"\\"\\"'; | ^^^^^^^^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "\"\"\\"\\"\\\""; | ~~~~~~~~~~~~~~~~~~~~ @@ -39,7 +39,7 @@ LL | let _: &str = 'a'; | | | expected due to this | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _: &str = "a"; | ~ ~ diff --git a/tests/ui/issues/issue-23589.stderr b/tests/ui/issues/issue-23589.stderr index bf055a7e31c59..21d383b0e8ce2 100644 --- a/tests/ui/issues/issue-23589.stderr +++ b/tests/ui/issues/issue-23589.stderr @@ -15,7 +15,7 @@ error[E0308]: mismatched types LL | let v: Vec(&str) = vec!['1', '2']; | ^^^ expected `&str`, found `char` | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let v: Vec(&str) = vec!["1", '2']; | ~ ~ diff --git a/tests/ui/lexer/lex-bad-char-literals-2.stderr b/tests/ui/lexer/lex-bad-char-literals-2.stderr index af64e6efe45ff..76cde00404a15 100644 --- a/tests/ui/lexer/lex-bad-char-literals-2.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-2.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | 'nope' | ^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | "nope" | ~ ~ diff --git a/tests/ui/lexer/lex-bad-char-literals-3.stderr b/tests/ui/lexer/lex-bad-char-literals-3.stderr index 312b42fc83174..3f339b2ef7d93 100644 --- a/tests/ui/lexer/lex-bad-char-literals-3.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-3.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | static c: char = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "●●"; | ~ ~ @@ -15,7 +15,7 @@ error: character literal may only contain one codepoint LL | let ch: &str = '●●'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "●●"; | ~ ~ diff --git a/tests/ui/lexer/lex-bad-char-literals-5.stderr b/tests/ui/lexer/lex-bad-char-literals-5.stderr index 185f460b10def..8004157e87f7a 100644 --- a/tests/ui/lexer/lex-bad-char-literals-5.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-5.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | static c: char = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | static c: char = "\x10\x10"; | ~ ~ @@ -15,7 +15,7 @@ error: character literal may only contain one codepoint LL | let ch: &str = '\x10\x10'; | ^^^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let ch: &str = "\x10\x10"; | ~ ~ diff --git a/tests/ui/lexer/lex-bad-char-literals-6.stderr b/tests/ui/lexer/lex-bad-char-literals-6.stderr index f49e5a095b34a..96d409d59bb25 100644 --- a/tests/ui/lexer/lex-bad-char-literals-6.stderr +++ b/tests/ui/lexer/lex-bad-char-literals-6.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | let x: &str = 'ab'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let x: &str = "ab"; | ~ ~ @@ -15,7 +15,7 @@ error: character literal may only contain one codepoint LL | let y: char = 'cd'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let y: char = "cd"; | ~ ~ @@ -26,7 +26,7 @@ error: character literal may only contain one codepoint LL | let z = 'ef'; | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let z = "ef"; | ~ ~ diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr index 675624cfa9415..57c5f82704ec7 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-1.stderr @@ -4,7 +4,7 @@ error[E0762]: unterminated character literal LL | println!('1 + 1'); | ^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!("1 + 1"); | ~ ~ diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr index 06b38522ad2dd..f64761af64193 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-2.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | println!(' 1 + 1'); | ^^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!(" 1 + 1"); | ~ ~ diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr index ebfbeac02600b..262f78569839d 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr @@ -4,7 +4,7 @@ error[E0762]: unterminated character literal LL | println!('hello world'); | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!("hello world"); | ~ ~ diff --git a/tests/ui/parser/issues/issue-64732.rs b/tests/ui/parser/issues/issue-64732.rs index 2db51ea6042aa..ff0f97ea211dc 100644 --- a/tests/ui/parser/issues/issue-64732.rs +++ b/tests/ui/parser/issues/issue-64732.rs @@ -5,5 +5,5 @@ fn main() { //~| HELP if you meant to write a byte string literal, use double quotes let _bar = 'hello'; //~^ ERROR character literal may only contain one codepoint - //~| HELP if you meant to write a `str` literal, use double quotes + //~| HELP if you meant to write a string literal, use double quotes } diff --git a/tests/ui/parser/issues/issue-64732.stderr b/tests/ui/parser/issues/issue-64732.stderr index 8893fa8aae239..7ec2df6d3bf7e 100644 --- a/tests/ui/parser/issues/issue-64732.stderr +++ b/tests/ui/parser/issues/issue-64732.stderr @@ -15,7 +15,7 @@ error: character literal may only contain one codepoint LL | let _bar = 'hello'; | ^^^^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _bar = "hello"; | ~ ~ diff --git a/tests/ui/parser/unicode-character-literal.fixed b/tests/ui/parser/unicode-character-literal.fixed index 9e31890151cc1..e401ecaf5da77 100644 --- a/tests/ui/parser/unicode-character-literal.fixed +++ b/tests/ui/parser/unicode-character-literal.fixed @@ -7,12 +7,12 @@ fn main() { let _spade = "♠️"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = "ṩ̂̊"; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.rs b/tests/ui/parser/unicode-character-literal.rs index d886e5b26a56b..428e4e1ac5a08 100644 --- a/tests/ui/parser/unicode-character-literal.rs +++ b/tests/ui/parser/unicode-character-literal.rs @@ -7,12 +7,12 @@ fn main() { let _spade = '♠️'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `♠` is followed by the combining mark `\u{fe0f}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _s = 'ṩ̂̊'; //~^ ERROR: character literal may only contain one codepoint //~| NOTE: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` - //~| HELP: if you meant to write a `str` literal, use double quotes + //~| HELP: if you meant to write a string literal, use double quotes let _a = 'Å'; //~^ ERROR: character literal may only contain one codepoint diff --git a/tests/ui/parser/unicode-character-literal.stderr b/tests/ui/parser/unicode-character-literal.stderr index 1104eaeb8d478..726cde2b413e2 100644 --- a/tests/ui/parser/unicode-character-literal.stderr +++ b/tests/ui/parser/unicode-character-literal.stderr @@ -9,7 +9,7 @@ note: this `♠` is followed by the combining mark `\u{fe0f}` | LL | let _spade = '♠️'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _spade = "♠️"; | ~ ~ @@ -25,7 +25,7 @@ note: this `s` is followed by the combining marks `\u{323}\u{307}\u{302}\u{30a}` | LL | let _s = 'ṩ̂̊'; | ^ -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | let _s = "ṩ̂̊"; | ~ ~ diff --git a/tests/ui/str/str-as-char.stderr b/tests/ui/str/str-as-char.stderr index 03b2309a3171a..0638d371c173b 100644 --- a/tests/ui/str/str-as-char.stderr +++ b/tests/ui/str/str-as-char.stderr @@ -4,7 +4,7 @@ error: character literal may only contain one codepoint LL | println!('●●'); | ^^^^ | -help: if you meant to write a `str` literal, use double quotes +help: if you meant to write a string literal, use double quotes | LL | println!("●●"); | ~ ~ From 6f388ef1fb3964fb83bae5c277f9c83bbde4c76b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 14 Mar 2024 00:22:52 +0000 Subject: [PATCH 05/29] Extend test to trigger on 2015, 2018 and 2021 editions --- .../lexer/lex-bad-str-literal-as-char-3.fixed | 4 --- .../ui/lexer/lex-bad-str-literal-as-char-3.rs | 8 ++++-- ...bad-str-literal-as-char-3.rust2015.stderr} | 4 +-- ...-bad-str-literal-as-char-3.rust2018.stderr | 14 ++++++++++ ...-bad-str-literal-as-char-3.rust2021.stderr | 26 +++++++++++++++++++ 5 files changed, 48 insertions(+), 8 deletions(-) delete mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed rename tests/ui/lexer/{lex-bad-str-literal-as-char-3.stderr => lex-bad-str-literal-as-char-3.rust2015.stderr} (79%) create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr create mode 100644 tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed b/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed deleted file mode 100644 index 3fbe0ea1dabef..0000000000000 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.fixed +++ /dev/null @@ -1,4 +0,0 @@ -//@ run-rustfix -fn main() { - println!("hello world"); //~ ERROR unterminated character literal -} diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs index 289f3f1d6570f..2c0eda97853b0 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -1,4 +1,8 @@ -//@ run-rustfix +//@ revisions: rust2015 rust2018 rust2021 +//@[rust2018] edition:2018 +//@[rust2021] edition:2021 fn main() { - println!('hello world'); //~ ERROR unterminated character literal + println!('hello world'); + //[rust2015,rust2018,rust2021]~^ ERROR unterminated character literal + //[rust2021]~^^ ERROR prefix `world` is unknown } diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr similarity index 79% rename from tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr rename to tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr index 262f78569839d..06f127426679f 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2015.stderr @@ -1,8 +1,8 @@ error[E0762]: unterminated character literal - --> $DIR/lex-bad-str-literal-as-char-3.rs:3:26 + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 | LL | println!('hello world'); - | ^^^^ + | ^^^ | help: if you meant to write a string literal, use double quotes | diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr new file mode 100644 index 0000000000000..06f127426679f --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2018.stderr @@ -0,0 +1,14 @@ +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0762`. diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr new file mode 100644 index 0000000000000..4170560cfcb04 --- /dev/null +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -0,0 +1,26 @@ +error: prefix `world` is unknown + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:21 + | +LL | println!('hello world'); + | ^^^^^ unknown prefix + | + = note: prefixed identifiers and literals are reserved since Rust 2021 +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error[E0762]: unterminated character literal + --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 + | +LL | println!('hello world'); + | ^^^ + | +help: if you meant to write a string literal, use double quotes + | +LL | println!("hello world"); + | ~ ~ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0762`. From ea1883d7b2207d1a0f08046f11ca493803bc8a55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 14 Mar 2024 00:40:25 +0000 Subject: [PATCH 06/29] Silence redundant error on char literal that was meant to be a string in 2021 edition --- compiler/rustc_lexer/src/cursor.rs | 9 +++++++++ compiler/rustc_parse/src/lexer/mod.rs | 11 ++++++++++- tests/ui/lexer/lex-bad-str-literal-as-char-3.rs | 1 - .../lex-bad-str-literal-as-char-3.rust2021.stderr | 14 +------------- 4 files changed, 20 insertions(+), 15 deletions(-) diff --git a/compiler/rustc_lexer/src/cursor.rs b/compiler/rustc_lexer/src/cursor.rs index 8d8cc9ecc3e9f..d173c3ac0327b 100644 --- a/compiler/rustc_lexer/src/cursor.rs +++ b/compiler/rustc_lexer/src/cursor.rs @@ -59,6 +59,15 @@ impl<'a> Cursor<'a> { iter.next().unwrap_or(EOF_CHAR) } + /// Peeks the third symbol from the input stream without consuming it. + pub fn third(&self) -> char { + // `.next()` optimizes better than `.nth(1)` + let mut iter = self.chars.clone(); + iter.next(); + iter.next(); + iter.next().unwrap_or(EOF_CHAR) + } + /// Checks if there is nothing more to consume. pub(crate) fn is_eof(&self) -> bool { self.chars.as_str().is_empty() diff --git a/compiler/rustc_parse/src/lexer/mod.rs b/compiler/rustc_parse/src/lexer/mod.rs index 5583e49ba6011..63b2b47630b29 100644 --- a/compiler/rustc_parse/src/lexer/mod.rs +++ b/compiler/rustc_parse/src/lexer/mod.rs @@ -698,13 +698,17 @@ impl<'psess, 'src> StringReader<'psess, 'src> { let expn_data = prefix_span.ctxt().outer_expn_data(); if expn_data.edition >= Edition::Edition2021 { + let mut silence = false; // In Rust 2021, this is a hard error. let sugg = if prefix == "rb" { Some(errors::UnknownPrefixSugg::UseBr(prefix_span)) } else if expn_data.is_root() { if self.cursor.first() == '\'' && let Some(start) = self.last_lifetime + && self.cursor.third() != '\'' { + // An "unclosed `char`" error will be emitted already, silence redundant error. + silence = true; Some(errors::UnknownPrefixSugg::MeantStr { start, end: self.mk_sp(self.pos, self.pos + BytePos(1)), @@ -715,7 +719,12 @@ impl<'psess, 'src> StringReader<'psess, 'src> { } else { None }; - self.dcx().emit_err(errors::UnknownPrefix { span: prefix_span, prefix, sugg }); + let err = errors::UnknownPrefix { span: prefix_span, prefix, sugg }; + if silence { + self.dcx().create_err(err).delay_as_bug(); + } else { + self.dcx().emit_err(err); + } } else { // Before Rust 2021, only emit a lint for migration. self.psess.buffer_lint_with_diagnostic( diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs index 2c0eda97853b0..0ae227da5f1e6 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rs @@ -4,5 +4,4 @@ fn main() { println!('hello world'); //[rust2015,rust2018,rust2021]~^ ERROR unterminated character literal - //[rust2021]~^^ ERROR prefix `world` is unknown } diff --git a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr index 4170560cfcb04..06f127426679f 100644 --- a/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr +++ b/tests/ui/lexer/lex-bad-str-literal-as-char-3.rust2021.stderr @@ -1,15 +1,3 @@ -error: prefix `world` is unknown - --> $DIR/lex-bad-str-literal-as-char-3.rs:5:21 - | -LL | println!('hello world'); - | ^^^^^ unknown prefix - | - = note: prefixed identifiers and literals are reserved since Rust 2021 -help: if you meant to write a string literal, use double quotes - | -LL | println!("hello world"); - | ~ ~ - error[E0762]: unterminated character literal --> $DIR/lex-bad-str-literal-as-char-3.rs:5:26 | @@ -21,6 +9,6 @@ help: if you meant to write a string literal, use double quotes LL | println!("hello world"); | ~ ~ -error: aborting due to 2 previous errors +error: aborting due to 1 previous error For more information about this error, try `rustc --explain E0762`. From f4d30b156b2958ba01b59a1751e16b5f6e157fcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Sun, 17 Mar 2024 23:46:39 +0000 Subject: [PATCH 07/29] fix rustdoc test --- compiler/rustc_parse/src/lexer/unescape_error_reporting.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs index 1ac6b6fe11566..fa242a32a1814 100644 --- a/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs +++ b/compiler/rustc_parse/src/lexer/unescape_error_reporting.rs @@ -95,7 +95,7 @@ pub(crate) fn emit_unescape_error( } escaped.push(c); } - if escaped.len() != lit.len() { + if escaped.len() != lit.len() || full_lit_span.is_empty() { let sugg = format!("{prefix}\"{escaped}\""); MoreThanOneCharSugg::QuotesFull { span: full_lit_span, From f4adb1e6bdaef12d8b5776f74dadab0ddc11694d Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Thu, 14 Mar 2024 21:29:21 +0100 Subject: [PATCH 08/29] add notes on how to store 'ptr or int' --- library/core/src/intrinsics.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/library/core/src/intrinsics.rs b/library/core/src/intrinsics.rs index ce498561eeb12..27ed26fdcf16d 100644 --- a/library/core/src/intrinsics.rs +++ b/library/core/src/intrinsics.rs @@ -1186,7 +1186,7 @@ extern "rust-intrinsic" { /// /// Transmuting *pointers to integers* in a `const` context is [undefined behavior][ub], unless /// the pointer was originally created *from* an integer. (That includes this function - /// specifically, integer-to-pointer casts, and helpers like [`invalid`][crate::ptr::dangling], + /// specifically, integer-to-pointer casts, and helpers like [`dangling`][crate::ptr::dangling], /// but also semantically-equivalent conversions such as punning through `repr(C)` union /// fields.) Any attempt to use the resulting value for integer operations will abort /// const-evaluation. (And even outside `const`, such transmutation is touching on many @@ -1206,7 +1206,11 @@ extern "rust-intrinsic" { /// In particular, doing a pointer-to-integer-to-pointer roundtrip via `transmute` is *not* a /// lossless process. If you want to round-trip a pointer through an integer in a way that you /// can get back the original pointer, you need to use `as` casts, or replace the integer type - /// by `MaybeUninit<$int>` (and never call `assume_init()`). + /// by `MaybeUninit<$int>` (and never call `assume_init()`). If you are looking for a way to + /// store data of arbitrary type, also use `MaybeUninit` (that will also handle uninitialized + /// memory due to padding). If you specifically need to store something that is "either an + /// integer or a pointer", use `*mut ()`: integers can be converted to pointers and back without + /// any loss (via `as` casts or via `transmute`). /// /// # Examples /// From 1fcf2eaa9f5ff9336e9b43f017eaf261acfdc2d3 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 22 Mar 2024 16:35:44 -0400 Subject: [PATCH 09/29] Uniquify ReError on input mode in canonicalizer --- .../src/canonicalizer.rs | 3 +- .../next-solver/dont-canonicalize-re-error.rs | 28 +++++++++++++++++++ .../dont-canonicalize-re-error.stderr | 21 ++++++++++++++ 3 files changed, 50 insertions(+), 2 deletions(-) create mode 100644 tests/ui/traits/next-solver/dont-canonicalize-re-error.rs create mode 100644 tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr diff --git a/compiler/rustc_next_trait_solver/src/canonicalizer.rs b/compiler/rustc_next_trait_solver/src/canonicalizer.rs index 95b3006666202..2dbb44bdee207 100644 --- a/compiler/rustc_next_trait_solver/src/canonicalizer.rs +++ b/compiler/rustc_next_trait_solver/src/canonicalizer.rs @@ -239,7 +239,7 @@ impl, I: Interner> TypeFolder // FIXME: We should investigate the perf implications of not uniquifying // `ReErased`. We may be able to short-circuit registering region // obligations if we encounter a `ReErased` on one side, for example. - ty::ReStatic | ty::ReErased => match self.canonicalize_mode { + ty::ReStatic | ty::ReErased | ty::ReError(_) => match self.canonicalize_mode { CanonicalizeMode::Input => CanonicalVarKind::Region(ty::UniverseIndex::ROOT), CanonicalizeMode::Response { .. } => return r, }, @@ -277,7 +277,6 @@ impl, I: Interner> TypeFolder } } } - ty::ReError(_) => return r, }; let existing_bound_var = match self.canonicalize_mode { diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs new file mode 100644 index 0000000000000..57f814bc81ec2 --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.rs @@ -0,0 +1,28 @@ +//@ compile-flags: -Znext-solver + +trait Tr<'a> {} + +// Fulfillment in the new solver relies on an invariant to hold: Either +// `has_changed` is true, or computing a goal's certainty is idempotent. +// This isn't true for `ReError`, which we used to pass through in the +// canonicalizer even on input mode, which can cause a goal to go from +// ambig => pass, but we don't consider `has_changed` when the response +// only contains region constraints (since we usually uniquify regions). +// +// In this test: +// Implicit negative coherence tries to prove `W: Constrain<'?1>`, +// which will then match with the impl below. This constrains `'?1` to +// `ReError`, but still bails w/ ambiguity bc we can't prove `?0: Sized`. +// Then, when we recompute the goal `W: Constrain<'error>`, when +// collecting ambiguities and overflows, we end up assembling a default +// error candidate w/o ambiguity, which causes the goal to pass, and ICE. +impl<'a, A: ?Sized> Tr<'a> for W {} +struct W(A); +impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} +//~^ ERROR conflicting implementations of trait `Tr<'_>` for type `W<_>` + +trait Constrain<'a> {} +impl Constrain<'missing> for W {} +//~^ ERROR use of undeclared lifetime name `'missing` + +fn main() {} diff --git a/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr new file mode 100644 index 0000000000000..cf85c52fb42ee --- /dev/null +++ b/tests/ui/traits/next-solver/dont-canonicalize-re-error.stderr @@ -0,0 +1,21 @@ +error[E0261]: use of undeclared lifetime name `'missing` + --> $DIR/dont-canonicalize-re-error.rs:25:26 + | +LL | impl Constrain<'missing> for W {} + | - ^^^^^^^^ undeclared lifetime + | | + | help: consider introducing lifetime `'missing` here: `'missing,` + +error[E0119]: conflicting implementations of trait `Tr<'_>` for type `W<_>` + --> $DIR/dont-canonicalize-re-error.rs:21:1 + | +LL | impl<'a, A: ?Sized> Tr<'a> for W {} + | ----------------------------------- first implementation here +LL | struct W(A); +LL | impl<'a, A: ?Sized> Tr<'a> for A where A: Constrain<'a> {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `W<_>` + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0119, E0261. +For more information about an error, try `rustc --explain E0119`. From da8a39a9de150f6b63a0601b4295e4b5a4f74e12 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Fri, 8 Mar 2024 01:34:05 +0000 Subject: [PATCH 10/29] Failing test --- tests/mir-opt/inline_coroutine_body.rs | 28 ++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/mir-opt/inline_coroutine_body.rs diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs new file mode 100644 index 0000000000000..5c305c5510042 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -0,0 +1,28 @@ +// EMIT_MIR_FOR_EACH_PANIC_STRATEGY +// skip-filecheck +//@ unit-test: Inline +//@ edition: 2021 +//@ compile-flags: -Zinline-mir-hint-threshold=10000 -Zinline-mir-threshold=10000 --crate-type=lib + +pub async fn run(permit: ActionPermit<'_, ()>, ctx: &mut core::task::Context<'_>) { + run2(permit, ctx); +} + +// EMIT_MIR inline_coroutine_body.run2.Inline.diff +fn run2(permit: ActionPermit<'_, T>, ctx: &mut core::task::Context) { + _ = || { + let mut fut = ActionPermit::perform(permit); + let fut = unsafe { core::pin::Pin::new_unchecked(&mut fut) }; + _ = core::future::Future::poll(fut, ctx); + }; +} + +pub struct ActionPermit<'a, T> { + _guard: core::cell::Ref<'a, T>, +} + +impl<'a, T> ActionPermit<'a, T> { + async fn perform(self) { + core::future::ready(()).await + } +} From 78ebb939c1f7135bcc851ba9d4d091d40bb7fe64 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Tue, 19 Mar 2024 12:34:56 -0400 Subject: [PATCH 11/29] Fix validation on substituted callee bodies in MIR inliner --- .../src/transform/validate.rs | 19 +- .../src/coroutine/by_move_body.rs | 9 +- compiler/rustc_mir_transform/src/inline.rs | 1 + tests/mir-opt/inline_coroutine_body.rs | 2 +- ...y.run2-{closure#0}.Inline.panic-abort.diff | 281 +++++++++++++++ ....run2-{closure#0}.Inline.panic-unwind.diff | 341 ++++++++++++++++++ 6 files changed, 642 insertions(+), 11 deletions(-) create mode 100644 tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff create mode 100644 tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff diff --git a/compiler/rustc_const_eval/src/transform/validate.rs b/compiler/rustc_const_eval/src/transform/validate.rs index 4bc49f906070d..b6d65de8bc64f 100644 --- a/compiler/rustc_const_eval/src/transform/validate.rs +++ b/compiler/rustc_const_eval/src/transform/validate.rs @@ -85,7 +85,7 @@ impl<'tcx> MirPass<'tcx> for Validator { cfg_checker.check_cleanup_control_flow(); // Also run the TypeChecker. - for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body) { + for (location, msg) in validate_types(tcx, self.mir_phase, param_env, body, body) { cfg_checker.fail(location, msg); } @@ -541,19 +541,25 @@ impl<'a, 'tcx> Visitor<'tcx> for CfgChecker<'a, 'tcx> { /// A faster version of the validation pass that only checks those things which may break when /// instantiating any generic parameters. +/// +/// `caller_body` is used to detect cycles in MIR inlining and MIR validation before +/// `optimized_mir` is available. pub fn validate_types<'tcx>( tcx: TyCtxt<'tcx>, mir_phase: MirPhase, param_env: ty::ParamEnv<'tcx>, body: &Body<'tcx>, + caller_body: &Body<'tcx>, ) -> Vec<(Location, String)> { - let mut type_checker = TypeChecker { body, tcx, param_env, mir_phase, failures: Vec::new() }; + let mut type_checker = + TypeChecker { body, caller_body, tcx, param_env, mir_phase, failures: Vec::new() }; type_checker.visit_body(body); type_checker.failures } struct TypeChecker<'a, 'tcx> { body: &'a Body<'tcx>, + caller_body: &'a Body<'tcx>, tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, mir_phase: MirPhase, @@ -705,8 +711,13 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> { } &ty::Coroutine(def_id, args) => { let f_ty = if let Some(var) = parent_ty.variant_index { - let gen_body = if def_id == self.body.source.def_id() { - self.body + // If we're currently validating an inlined copy of this body, + // then it will no longer be parameterized over the original + // args of the coroutine. Otherwise, we prefer to use this body + // since we may be in the process of computing this MIR in the + // first place. + let gen_body = if def_id == self.caller_body.source.def_id() { + self.caller_body } else { self.tcx.optimized_mir(def_id) }; diff --git a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs index 000b96ee8016f..e0bbd582d88c2 100644 --- a/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs +++ b/compiler/rustc_mir_transform/src/coroutine/by_move_body.rs @@ -64,12 +64,9 @@ impl<'tcx> MirPass<'tcx> for ByMoveBody { let mut by_move_body = body.clone(); MakeByMoveBody { tcx, by_ref_fields, by_move_coroutine_ty }.visit_body(&mut by_move_body); dump_mir(tcx, false, "coroutine_by_move", &0, &by_move_body, |_, _| Ok(())); - by_move_body.source = mir::MirSource { - instance: InstanceDef::CoroutineKindShim { - coroutine_def_id: coroutine_def_id.to_def_id(), - }, - promoted: None, - }; + by_move_body.source = mir::MirSource::from_instance(InstanceDef::CoroutineKindShim { + coroutine_def_id: coroutine_def_id.to_def_id(), + }); body.coroutine.as_mut().unwrap().by_move_body = Some(by_move_body); } } diff --git a/compiler/rustc_mir_transform/src/inline.rs b/compiler/rustc_mir_transform/src/inline.rs index 4ec76eec3a930..78c0615b1650e 100644 --- a/compiler/rustc_mir_transform/src/inline.rs +++ b/compiler/rustc_mir_transform/src/inline.rs @@ -213,6 +213,7 @@ impl<'tcx> Inliner<'tcx> { MirPhase::Runtime(RuntimePhase::Optimized), self.param_env, &callee_body, + &caller_body, ) .is_empty() { diff --git a/tests/mir-opt/inline_coroutine_body.rs b/tests/mir-opt/inline_coroutine_body.rs index 5c305c5510042..be73bc49de59e 100644 --- a/tests/mir-opt/inline_coroutine_body.rs +++ b/tests/mir-opt/inline_coroutine_body.rs @@ -8,7 +8,7 @@ pub async fn run(permit: ActionPermit<'_, ()>, ctx: &mut core::task::Context<'_> run2(permit, ctx); } -// EMIT_MIR inline_coroutine_body.run2.Inline.diff +// EMIT_MIR inline_coroutine_body.run2-{closure#0}.Inline.diff fn run2(permit: ActionPermit<'_, T>, ctx: &mut core::task::Context) { _ = || { let mut fut = ActionPermit::perform(permit); diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff new file mode 100644 index 0000000000000..b189b4e73f405 --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-abort.diff @@ -0,0 +1,281 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _41: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind unreachable]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind unreachable]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind unreachable]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb3, 1: bb13, 3: bb12, otherwise: bb8]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2: { ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind unreachable]; ++ drop(_2) -> [return: bb1, unwind unreachable]; + } + ++ bb3: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_41); ++ _41 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _41); ++ StorageDead(_41); ++ StorageDead(_14); ++ _12 = as IntoFuture>::into_future(move _13) -> [return: bb4, unwind unreachable]; ++ } ++ + bb4: { +- StorageDead(_2); +- return; ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb5; ++ } ++ ++ bb5: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb6, unwind unreachable]; ++ } ++ ++ bb6: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = as Future>::poll(move _19, move _22) -> [return: bb7, unwind unreachable]; ++ } ++ ++ bb7: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb10, 1: bb9, otherwise: bb8]; ++ } ++ ++ bb8: { ++ unreachable; ++ } ++ ++ bb9: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb2; ++ } ++ ++ bb10: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb11, unwind unreachable]; ++ } ++ ++ bb11: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb2; ++ } ++ ++ bb12: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb5; ++ } ++ ++ bb13: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb13, unwind unreachable]; + } + } + diff --git a/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff new file mode 100644 index 0000000000000..ed18c0a3adb8c --- /dev/null +++ b/tests/mir-opt/inline_coroutine_body.run2-{closure#0}.Inline.panic-unwind.diff @@ -0,0 +1,341 @@ +- // MIR for `run2::{closure#0}` before Inline ++ // MIR for `run2::{closure#0}` after Inline + + fn run2::{closure#0}(_1: {closure@$DIR/inline_coroutine_body.rs:13:9: 13:11}) -> () { + debug permit => (_1.0: ActionPermit<'_, T>); + debug ctx => (*(_1.1: &mut std::task::Context<'_>)); + let mut _0: (); + let mut _2: {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let mut _3: ActionPermit<'_, T>; + let mut _5: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; + let _6: (); + let mut _7: std::task::Poll<()>; + let mut _8: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + let mut _9: &mut std::task::Context<'_>; + let mut _10: &mut std::task::Context<'_>; + scope 1 { + debug fut => _2; + let _4: std::pin::Pin<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>; + scope 2 { + debug fut => _4; + scope 4 { + } ++ scope 7 (inlined ActionPermit::<'_, T>::perform::{closure#0}) { ++ debug _task_context => _31; ++ debug self => ((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})).0: ActionPermit<'_, T>); ++ let _11: ActionPermit<'_, T>; ++ let mut _12: std::future::Ready<()>; ++ let mut _13: std::future::Ready<()>; ++ let mut _14: (); ++ let mut _16: (); ++ let _17: (); ++ let mut _18: std::task::Poll<()>; ++ let mut _19: std::pin::Pin<&mut std::future::Ready<()>>; ++ let mut _20: &mut std::future::Ready<()>; ++ let mut _21: &mut std::future::Ready<()>; ++ let mut _22: &mut std::task::Context<'_>; ++ let mut _23: &mut std::task::Context<'_>; ++ let mut _24: &mut std::task::Context<'_>; ++ let mut _25: isize; ++ let mut _27: !; ++ let mut _28: &mut std::task::Context<'_>; ++ let mut _29: (); ++ let mut _30: (); ++ let mut _31: &mut std::task::Context<'_>; ++ let mut _32: u32; ++ let mut _33: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _34: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _35: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _36: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _37: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _38: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _39: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _40: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _41: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ let mut _42: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}; ++ scope 8 { ++ debug self => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).0: ActionPermit<'_, T>); ++ let mut _15: std::future::Ready<()>; ++ scope 9 { ++ debug __awaitee => (((*(_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6})) as variant#3).1: std::future::Ready<()>); ++ let _26: (); ++ scope 10 { ++ } ++ scope 11 { ++ debug result => _26; ++ } ++ } ++ scope 12 (inlined ready::<()>) { ++ debug t => _14; ++ let mut _43: std::option::Option<()>; ++ } ++ } ++ } + } + scope 3 { ++ scope 6 (inlined Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked) { ++ debug pointer => _5; ++ } + } + } ++ scope 5 (inlined ActionPermit::<'_, T>::perform) { ++ debug self => _3; ++ } + + bb0: { + StorageLive(_2); + StorageLive(_3); + _3 = move (_1.0: ActionPermit<'_, T>); +- _2 = ActionPermit::<'_, T>::perform(move _3) -> [return: bb1, unwind: bb6]; +- } +- +- bb1: { ++ _2 = {coroutine@$DIR/inline_coroutine_body.rs:25:28: 27:6 (#0)} { self: move _3 }; + StorageDead(_3); + StorageLive(_4); + StorageLive(_5); + _5 = &mut _2; +- _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}>::new_unchecked(move _5) -> [return: bb2, unwind: bb5]; +- } +- +- bb2: { ++ _4 = Pin::<&mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}> { __pointer: _5 }; + StorageDead(_5); + StorageLive(_6); + StorageLive(_7); + StorageLive(_8); + _8 = move _4; + StorageLive(_9); + _10 = deref_copy (_1.1: &mut std::task::Context<'_>); + _9 = &mut (*_10); +- _7 = <{async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6} as Future>::poll(move _8, move _9) -> [return: bb3, unwind: bb5]; ++ StorageLive(_11); ++ StorageLive(_15); ++ StorageLive(_16); ++ StorageLive(_25); ++ StorageLive(_27); ++ StorageLive(_30); ++ StorageLive(_31); ++ StorageLive(_32); ++ StorageLive(_33); ++ StorageLive(_34); ++ StorageLive(_35); ++ StorageLive(_36); ++ StorageLive(_37); ++ StorageLive(_38); ++ StorageLive(_39); ++ StorageLive(_40); ++ StorageLive(_41); ++ StorageLive(_42); ++ _33 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _32 = discriminant((*_33)); ++ switchInt(move _32) -> [0: bb5, 1: bb22, 2: bb21, 3: bb20, otherwise: bb10]; + } + +- bb3: { ++ bb1: { ++ StorageDead(_2); ++ return; ++ } ++ ++ bb2 (cleanup): { ++ drop(_2) -> [return: bb3, unwind terminate(cleanup)]; ++ } ++ ++ bb3 (cleanup): { ++ resume; ++ } ++ ++ bb4: { ++ StorageDead(_42); ++ StorageDead(_41); ++ StorageDead(_40); ++ StorageDead(_39); ++ StorageDead(_38); ++ StorageDead(_37); ++ StorageDead(_36); ++ StorageDead(_35); ++ StorageDead(_34); ++ StorageDead(_33); ++ StorageDead(_32); ++ StorageDead(_31); ++ StorageDead(_30); ++ StorageDead(_27); ++ StorageDead(_25); ++ StorageDead(_16); ++ StorageDead(_15); ++ StorageDead(_11); + StorageDead(_9); + StorageDead(_8); + StorageDead(_7); + _6 = const (); + StorageDead(_6); + _0 = const (); + StorageDead(_4); +- drop(_2) -> [return: bb4, unwind: bb6]; ++ drop(_2) -> [return: bb1, unwind: bb3]; + } + +- bb4: { +- StorageDead(_2); +- return; ++ bb5: { ++ _31 = move _9; ++ _34 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _35 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_34) as variant#3).0: ActionPermit<'_, T>) = move ((*_35).0: ActionPermit<'_, T>); ++ StorageLive(_12); ++ StorageLive(_13); ++ StorageLive(_14); ++ _14 = (); ++ StorageLive(_43); ++ _43 = Option::<()>::Some(_14); ++ _13 = std::future::Ready::<()>(move _43); ++ StorageDead(_43); ++ StorageDead(_14); ++ _12 = as IntoFuture>::into_future(move _13) -> [return: bb6, unwind: bb17]; + } + +- bb5 (cleanup): { +- drop(_2) -> [return: bb6, unwind terminate(cleanup)]; ++ bb6: { ++ StorageDead(_13); ++ _36 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ (((*_36) as variant#3).1: std::future::Ready<()>) = move _12; ++ goto -> bb7; + } + +- bb6 (cleanup): { +- resume; ++ bb7: { ++ StorageLive(_17); ++ StorageLive(_18); ++ StorageLive(_19); ++ StorageLive(_20); ++ StorageLive(_21); ++ _37 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ _21 = &mut (((*_37) as variant#3).1: std::future::Ready<()>); ++ _20 = &mut (*_21); ++ _19 = Pin::<&mut std::future::Ready<()>>::new_unchecked(move _20) -> [return: bb8, unwind: bb15]; ++ } ++ ++ bb8: { ++ StorageDead(_20); ++ StorageLive(_22); ++ StorageLive(_23); ++ StorageLive(_24); ++ _24 = _31; ++ _23 = move _24; ++ _22 = &mut (*_23); ++ StorageDead(_24); ++ _18 = as Future>::poll(move _19, move _22) -> [return: bb9, unwind: bb14]; ++ } ++ ++ bb9: { ++ StorageDead(_22); ++ StorageDead(_19); ++ _25 = discriminant(_18); ++ switchInt(move _25) -> [0: bb12, 1: bb11, otherwise: bb10]; ++ } ++ ++ bb10: { ++ unreachable; ++ } ++ ++ bb11: { ++ _17 = const (); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageLive(_28); ++ StorageLive(_29); ++ _29 = (); ++ _7 = Poll::<()>::Pending; ++ StorageDead(_12); ++ StorageDead(_28); ++ StorageDead(_29); ++ _38 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_38)) = 3; ++ goto -> bb4; ++ } ++ ++ bb12: { ++ StorageLive(_26); ++ _26 = ((_18 as Ready).0: ()); ++ _30 = _26; ++ StorageDead(_26); ++ StorageDead(_23); ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ StorageDead(_12); ++ _39 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_39) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb13, unwind: bb19]; ++ } ++ ++ bb13: { ++ _7 = Poll::<()>::Ready(move _30); ++ _40 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_40)) = 1; ++ goto -> bb4; ++ } ++ ++ bb14 (cleanup): { ++ StorageDead(_22); ++ StorageDead(_19); ++ StorageDead(_23); ++ goto -> bb16; ++ } ++ ++ bb15 (cleanup): { ++ StorageDead(_20); ++ StorageDead(_19); ++ goto -> bb16; ++ } ++ ++ bb16 (cleanup): { ++ StorageDead(_21); ++ StorageDead(_18); ++ StorageDead(_17); ++ goto -> bb18; ++ } ++ ++ bb17 (cleanup): { ++ StorageDead(_13); ++ goto -> bb18; ++ } ++ ++ bb18 (cleanup): { ++ StorageDead(_12); ++ _41 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ drop((((*_41) as variant#3).0: ActionPermit<'_, T>)) -> [return: bb19, unwind terminate(cleanup)]; ++ } ++ ++ bb19 (cleanup): { ++ _42 = deref_copy (_8.0: &mut {async fn body@$DIR/inline_coroutine_body.rs:25:28: 27:6}); ++ discriminant((*_42)) = 2; ++ goto -> bb2; ++ } ++ ++ bb20: { ++ StorageLive(_12); ++ StorageLive(_28); ++ StorageLive(_29); ++ _28 = move _9; ++ StorageDead(_29); ++ _31 = move _28; ++ StorageDead(_28); ++ _16 = const (); ++ goto -> bb7; ++ } ++ ++ bb21: { ++ assert(const false, "`async fn` resumed after panicking") -> [success: bb21, unwind: bb2]; ++ } ++ ++ bb22: { ++ assert(const false, "`async fn` resumed after completion") -> [success: bb22, unwind: bb2]; + } + } + From 0d3ef800b9d80fde90cd50bacd1dcf08eb02f682 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 21 Mar 2024 21:11:19 +0100 Subject: [PATCH 12/29] `rustdoc --test`: Prevent reaching the maximum size of command-line by using files for arguments if there are too many --- src/librustdoc/doctest.rs | 98 +++++++++++++++++++++++++++----------- src/librustdoc/lib.rs | 4 +- src/librustdoc/markdown.rs | 11 ++++- 3 files changed, 82 insertions(+), 31 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index c6eb7be08cd81..bbd54fa012e9e 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -23,6 +23,7 @@ use rustc_target::spec::{Target, TargetTriple}; use tempfile::Builder as TempFileBuilder; use std::env; +use std::fs::File; use std::io::{self, Write}; use std::panic; use std::path::{Path, PathBuf}; @@ -31,6 +32,8 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; +use tempfile::tempdir; + use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; use crate::html::markdown::{self, ErrorCodes, Ignore, LangString}; @@ -48,7 +51,51 @@ pub(crate) struct GlobalTestOptions { pub(crate) attrs: Vec, } -pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { +pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Result<(), String> { + let mut file = File::create(file_path) + .map_err(|error| format!("failed to create args file: {error:?}"))?; + + // We now put the common arguments into the file we created. + let mut content = vec!["--crate-type=bin".to_string()]; + + for cfg in &options.cfgs { + content.push(format!("--cfg={cfg}")); + } + if !options.check_cfgs.is_empty() { + content.push("-Zunstable-options".to_string()); + for check_cfg in &options.check_cfgs { + content.push(format!("--check-cfg={check_cfg}")); + } + } + + if let Some(sysroot) = &options.maybe_sysroot { + content.push(format!("--sysroot={}", sysroot.display())); + } + for lib_str in &options.lib_strs { + content.push(format!("-L{lib_str}")); + } + for extern_str in &options.extern_strs { + content.push(format!("--extern={extern_str}")); + } + content.push("-Ccodegen-units=1".to_string()); + for codegen_options_str in &options.codegen_options_strs { + content.push(format!("-C{codegen_options_str}")); + } + for unstable_option_str in &options.unstable_opts_strs { + content.push(format!("-Z{unstable_option_str}")); + } + + let content = content.join("\n"); + + file.write(content.as_bytes()) + .map_err(|error| format!("failed to write arguments to temporary file: {error:?}"))?; + Ok(()) +} + +pub(crate) fn run( + dcx: &rustc_errors::DiagCtxt, + options: RustdocOptions, +) -> Result<(), ErrorGuaranteed> { let input = config::Input::File(options.input.clone()); let invalid_codeblock_attributes_name = crate::lint::INVALID_CODEBLOCK_ATTRIBUTES.name; @@ -118,6 +165,15 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; + let temp_dir = match tempdir() + .map_err(|error| format!("failed to create temporary directory: {error:?}")) + { + Ok(temp_dir) => temp_dir, + Err(error) => return crate::wrap_return(dcx, Err(error)), + }; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + crate::wrap_return(dcx, generate_args_file(&file_path, &options))?; + let (tests, unused_extern_reports, compiling_test_count) = interface::run_compiler(config, |compiler| { compiler.enter(|queries| { @@ -134,6 +190,7 @@ pub(crate) fn run(options: RustdocOptions) -> Result<(), ErrorGuaranteed> { Some(compiler.sess.psess.clone_source_map()), None, enable_per_target_ignores, + file_path, ); let mut hir_collector = HirCollector { @@ -334,6 +391,7 @@ fn run_test( path: PathBuf, test_id: &str, report_unused_externs: impl Fn(UnusedExterns), + arg_file: PathBuf, ) -> Result<(), TestFailure> { let (test, line_offset, supports_color) = make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); @@ -347,19 +405,9 @@ fn run_test( .as_deref() .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg("--crate-type").arg("bin"); - for cfg in &rustdoc_options.cfgs { - compiler.arg("--cfg").arg(&cfg); - } - if !rustdoc_options.check_cfgs.is_empty() { - compiler.arg("-Z").arg("unstable-options"); - for check_cfg in &rustdoc_options.check_cfgs { - compiler.arg("--check-cfg").arg(&check_cfg); - } - } - if let Some(sysroot) = rustdoc_options.maybe_sysroot { - compiler.arg("--sysroot").arg(sysroot); - } + + compiler.arg(&format!("@{}", arg_file.display())); + compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); compiler.env("UNSTABLE_RUSTDOC_TEST_LINE", format!("{}", line as isize - line_offset as isize)); @@ -370,22 +418,10 @@ fn run_test( if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); - compiler.arg("-Z").arg("unstable-options"); compiler.arg("-W").arg("unused_crate_dependencies"); + compiler.arg("-Z").arg("unstable-options"); } - for lib_str in &rustdoc_options.lib_strs { - compiler.arg("-L").arg(&lib_str); - } - for extern_str in &rustdoc_options.extern_strs { - compiler.arg("--extern").arg(&extern_str); - } - compiler.arg("-Ccodegen-units=1"); - for codegen_options_str in &rustdoc_options.codegen_options_strs { - compiler.arg("-C").arg(&codegen_options_str); - } - for unstable_option_str in &rustdoc_options.unstable_opts_strs { - compiler.arg("-Z").arg(&unstable_option_str); - } + if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { compiler.arg("--emit=metadata"); } @@ -941,6 +977,7 @@ pub(crate) struct Collector { visited_tests: FxHashMap<(String, usize), usize>, unused_extern_reports: Arc>>, compiling_test_count: AtomicUsize, + arg_file: PathBuf, } impl Collector { @@ -952,6 +989,7 @@ impl Collector { source_map: Option>, filename: Option, enable_per_target_ignores: bool, + arg_file: PathBuf, ) -> Collector { Collector { tests: Vec::new(), @@ -967,6 +1005,7 @@ impl Collector { visited_tests: FxHashMap::default(), unused_extern_reports: Default::default(), compiling_test_count: AtomicUsize::new(0), + arg_file, } } @@ -1067,6 +1106,8 @@ impl Tester for Collector { ) }; + let arg_file = self.arg_file.clone(); + debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { desc: test::TestDesc { @@ -1108,6 +1149,7 @@ impl Tester for Collector { path, &test_id, report_unused_externs, + arg_file, ); if let Err(err) = res { diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 18651875130fc..b78fb4c4eee22 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -664,7 +664,7 @@ fn usage(argv0: &str) { /// A result type used by several functions under `main()`. type MainResult = Result<(), ErrorGuaranteed>; -fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { +pub(crate) fn wrap_return(dcx: &rustc_errors::DiagCtxt, res: Result<(), String>) -> MainResult { match res { Ok(()) => dcx.has_errors().map_or(Ok(()), Err), Err(err) => Err(dcx.err(err)), @@ -731,7 +731,7 @@ fn main_args( match (options.should_test, options.markdown_input()) { (true, true) => return wrap_return(&diag, markdown::test(options)), - (true, false) => return doctest::run(options), + (true, false) => return doctest::run(&diag, options), (false, true) => { let input = options.input.clone(); let edition = options.edition; diff --git a/src/librustdoc/markdown.rs b/src/librustdoc/markdown.rs index b661ced016917..dcd2cf02a30a3 100644 --- a/src/librustdoc/markdown.rs +++ b/src/librustdoc/markdown.rs @@ -3,11 +3,13 @@ use std::fs::{create_dir_all, read_to_string, File}; use std::io::prelude::*; use std::path::Path; +use tempfile::tempdir; + use rustc_span::edition::Edition; use rustc_span::DUMMY_SP; use crate::config::{Options, RenderOptions}; -use crate::doctest::{Collector, GlobalTestOptions}; +use crate::doctest::{generate_args_file, Collector, GlobalTestOptions}; use crate::html::escape::Escape; use crate::html::markdown; use crate::html::markdown::{ @@ -146,6 +148,12 @@ pub(crate) fn test(options: Options) -> Result<(), String> { .map_err(|err| format!("{input}: {err}", input = options.input.display()))?; let mut opts = GlobalTestOptions::default(); opts.no_crate_inject = true; + + let temp_dir = + tempdir().map_err(|error| format!("failed to create temporary directory: {error:?}"))?; + let file_path = temp_dir.path().join("rustdoc-cfgs"); + generate_args_file(&file_path, &options)?; + let mut collector = Collector::new( options.input.display().to_string(), options.clone(), @@ -154,6 +162,7 @@ pub(crate) fn test(options: Options) -> Result<(), String> { None, Some(options.input), options.enable_per_target_ignores, + file_path, ); collector.set_position(DUMMY_SP); let codes = ErrorCodes::from(options.unstable_features.is_nightly_build()); From 33735d5913fbd8f92d4c5b5306a5d23030710d5c Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 22 Mar 2024 14:46:02 +0100 Subject: [PATCH 13/29] Prevent cloning more than necessary `RustdocOptions` fields --- src/librustdoc/doctest.rs | 125 +++++++++++++++++++++++--------------- 1 file changed, 77 insertions(+), 48 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index bbd54fa012e9e..1e1731cbe4a06 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -379,26 +379,26 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: RustdocOptions, + rustdoc_options: RustdocTestOptions, mut lang_string: LangString, no_run: bool, - runtool: Option, - runtool_args: Vec, - target: TargetTriple, opts: &GlobalTestOptions, edition: Edition, - outdir: DirState, path: PathBuf, - test_id: &str, report_unused_externs: impl Fn(UnusedExterns), - arg_file: PathBuf, ) -> Result<(), TestFailure> { - let (test, line_offset, supports_color) = - make_test(test, Some(crate_name), lang_string.test_harness, opts, edition, Some(test_id)); + let (test, line_offset, supports_color) = make_test( + test, + Some(crate_name), + lang_string.test_harness, + opts, + edition, + Some(&rustdoc_options.test_id), + ); // Make sure we emit well-formed executable names for our target. - let rust_out = add_exe_suffix("rust_out".to_owned(), &target); - let output_file = outdir.path().join(rust_out); + let rust_out = add_exe_suffix("rust_out".to_owned(), &rustdoc_options.target); + let output_file = rustdoc_options.outdir.path().join(rust_out); let rustc_binary = rustdoc_options .test_builder @@ -406,7 +406,7 @@ fn run_test( .unwrap_or_else(|| rustc_interface::util::rustc_path().expect("found rustc")); let mut compiler = wrapped_rustc_command(&rustdoc_options.test_builder_wrappers, rustc_binary); - compiler.arg(&format!("@{}", arg_file.display())); + compiler.arg(&format!("@{}", rustdoc_options.arg_file.display())); compiler.arg("--edition").arg(&edition.to_string()); compiler.env("UNSTABLE_RUSTDOC_TEST_PATH", path); @@ -415,17 +415,17 @@ fn run_test( if lang_string.test_harness { compiler.arg("--test"); } - if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail { + if rustdoc_options.is_json_unused_externs_enabled && !lang_string.compile_fail { compiler.arg("--error-format=json"); compiler.arg("--json").arg("unused-externs"); compiler.arg("-W").arg("unused_crate_dependencies"); compiler.arg("-Z").arg("unstable-options"); } - if no_run && !lang_string.compile_fail && rustdoc_options.persist_doctests.is_none() { + if no_run && !lang_string.compile_fail && rustdoc_options.should_persist_doctests { compiler.arg("--emit=metadata"); } - compiler.arg("--target").arg(match target { + compiler.arg("--target").arg(match rustdoc_options.target { TargetTriple::TargetTriple(s) => s, TargetTriple::TargetJson { path_for_rustdoc, .. } => { path_for_rustdoc.to_str().expect("target path must be valid unicode").to_string() @@ -521,10 +521,10 @@ fn run_test( let mut cmd; let output_file = make_maybe_absolute_path(output_file); - if let Some(tool) = runtool { + if let Some(tool) = rustdoc_options.runtool { let tool = make_maybe_absolute_path(tool.into()); cmd = Command::new(tool); - cmd.args(runtool_args); + cmd.args(rustdoc_options.runtool_args); cmd.arg(output_file); } else { cmd = Command::new(output_file); @@ -933,6 +933,61 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { (before, after, crates) } +pub(crate) struct RustdocTestOptions { + test_builder: Option, + test_builder_wrappers: Vec, + is_json_unused_externs_enabled: bool, + should_persist_doctests: bool, + error_format: ErrorOutputType, + test_run_directory: Option, + nocapture: bool, + arg_file: PathBuf, + outdir: DirState, + runtool: Option, + runtool_args: Vec, + target: TargetTriple, + test_id: String, +} + +impl RustdocTestOptions { + fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { + let outdir = if let Some(ref path) = options.persist_doctests { + let mut path = path.clone(); + path.push(&test_id); + + if let Err(err) = std::fs::create_dir_all(&path) { + eprintln!("Couldn't create directory for doctest executables: {err}"); + panic::resume_unwind(Box::new(())); + } + + DirState::Perm(path) + } else { + DirState::Temp( + TempFileBuilder::new() + .prefix("rustdoctest") + .tempdir() + .expect("rustdoc needs a tempdir"), + ) + }; + + Self { + test_builder: options.test_builder.clone(), + test_builder_wrappers: options.test_builder_wrappers.clone(), + is_json_unused_externs_enabled: options.json_unused_externs.is_enabled(), + should_persist_doctests: options.persist_doctests.is_none(), + error_format: options.error_format, + test_run_directory: options.test_run_directory.clone(), + nocapture: options.nocapture, + arg_file: arg_file.into(), + outdir, + runtool: options.runtool.clone(), + runtool_args: options.runtool_args.clone(), + target: options.target.clone(), + test_id, + } + } +} + pub(crate) trait Tester { fn add_test(&mut self, test: String, config: LangString, line: usize); fn get_line(&self) -> usize { @@ -1048,13 +1103,9 @@ impl Tester for Collector { let crate_name = self.crate_name.clone(); let opts = self.opts.clone(); let edition = config.edition.unwrap_or(self.rustdoc_options.edition); - let rustdoc_options = self.rustdoc_options.clone(); - let runtool = self.rustdoc_options.runtool.clone(); - let runtool_args = self.rustdoc_options.runtool_args.clone(); - let target = self.rustdoc_options.target.clone(); - let target_str = target.to_string(); + let target_str = self.rustdoc_options.target.to_string(); let unused_externs = self.unused_extern_reports.clone(); - let no_run = config.no_run || rustdoc_options.no_run; + let no_run = config.no_run || self.rustdoc_options.no_run; if !config.compile_fail { self.compiling_test_count.fetch_add(1, Ordering::SeqCst); } @@ -1088,25 +1139,9 @@ impl Tester for Collector { self.visited_tests.entry((file.clone(), line)).and_modify(|v| *v += 1).or_insert(0) }, ); - let outdir = if let Some(mut path) = rustdoc_options.persist_doctests.clone() { - path.push(&test_id); - - if let Err(err) = std::fs::create_dir_all(&path) { - eprintln!("Couldn't create directory for doctest executables: {err}"); - panic::resume_unwind(Box::new(())); - } - - DirState::Perm(path) - } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) - }; - let arg_file = self.arg_file.clone(); + let rustdoc_test_options = + RustdocTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { @@ -1137,19 +1172,13 @@ impl Tester for Collector { &test, &crate_name, line, - rustdoc_options, + rustdoc_test_options, config, no_run, - runtool, - runtool_args, - target, &opts, edition, - outdir, path, - &test_id, report_unused_externs, - arg_file, ); if let Err(err) = res { From 86fe40021fdac50ed64e04cdae12f0156e5a8908 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Mar 2024 14:52:49 +0100 Subject: [PATCH 14/29] Put temporary directory into one common function --- src/librustdoc/doctest.rs | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index 1e1731cbe4a06..fa4b1d4fbfece 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -20,7 +20,6 @@ use rustc_span::source_map::SourceMap; use rustc_span::symbol::sym; use rustc_span::{BytePos, FileName, Pos, Span, DUMMY_SP}; use rustc_target::spec::{Target, TargetTriple}; -use tempfile::Builder as TempFileBuilder; use std::env; use std::fs::File; @@ -32,7 +31,7 @@ use std::str; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex}; -use tempfile::tempdir; +use tempfile::{Builder as TempFileBuilder, TempDir}; use crate::clean::{types::AttributesExt, Attributes}; use crate::config::Options as RustdocOptions; @@ -92,6 +91,10 @@ pub(crate) fn generate_args_file(file_path: &Path, options: &RustdocOptions) -> Ok(()) } +fn get_doctest_dir() -> io::Result { + TempFileBuilder::new().prefix("rustdoctest").tempdir() +} + pub(crate) fn run( dcx: &rustc_errors::DiagCtxt, options: RustdocOptions, @@ -165,7 +168,7 @@ pub(crate) fn run( let externs = options.externs.clone(); let json_unused_externs = options.json_unused_externs; - let temp_dir = match tempdir() + let temp_dir = match get_doctest_dir() .map_err(|error| format!("failed to create temporary directory: {error:?}")) { Ok(temp_dir) => temp_dir, @@ -962,12 +965,7 @@ impl RustdocTestOptions { DirState::Perm(path) } else { - DirState::Temp( - TempFileBuilder::new() - .prefix("rustdoctest") - .tempdir() - .expect("rustdoc needs a tempdir"), - ) + DirState::Temp(get_doctest_dir().expect("rustdoc needs a tempdir")) }; Self { From 773084ff2f337774c6bca54eba1b2e88d63465d3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Mar 2024 15:00:37 +0100 Subject: [PATCH 15/29] Rename `RustdocTestOptions` into `IndividualTestOptions` --- src/librustdoc/doctest.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustdoc/doctest.rs b/src/librustdoc/doctest.rs index fa4b1d4fbfece..211921715b00c 100644 --- a/src/librustdoc/doctest.rs +++ b/src/librustdoc/doctest.rs @@ -382,7 +382,7 @@ fn run_test( test: &str, crate_name: &str, line: usize, - rustdoc_options: RustdocTestOptions, + rustdoc_options: IndividualTestOptions, mut lang_string: LangString, no_run: bool, opts: &GlobalTestOptions, @@ -936,7 +936,7 @@ fn partition_source(s: &str, edition: Edition) -> (String, String, String) { (before, after, crates) } -pub(crate) struct RustdocTestOptions { +pub(crate) struct IndividualTestOptions { test_builder: Option, test_builder_wrappers: Vec, is_json_unused_externs_enabled: bool, @@ -952,7 +952,7 @@ pub(crate) struct RustdocTestOptions { test_id: String, } -impl RustdocTestOptions { +impl IndividualTestOptions { fn new(options: &RustdocOptions, arg_file: &Path, test_id: String) -> Self { let outdir = if let Some(ref path) = options.persist_doctests { let mut path = path.clone(); @@ -1139,7 +1139,7 @@ impl Tester for Collector { ); let rustdoc_test_options = - RustdocTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); + IndividualTestOptions::new(&self.rustdoc_options, &self.arg_file, test_id); debug!("creating test {name}: {test}"); self.tests.push(test::TestDescAndFn { From 5fab0162cca0bc55a93203f8ce127d026273a70f Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Mar 2024 15:46:09 +0100 Subject: [PATCH 16/29] Add `Rustdoc` into `run-make-support` --- src/tools/run-make-support/src/lib.rs | 41 ++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs index e5e7b559c9292..419b04231b572 100644 --- a/src/tools/run-make-support/src/lib.rs +++ b/src/tools/run-make-support/src/lib.rs @@ -9,8 +9,8 @@ pub fn out_dir() -> PathBuf { env::var_os("TMPDIR").unwrap().into() } -fn setup_common_build_cmd() -> Command { - let rustc = env::var("RUSTC").unwrap(); +fn setup_common_build_cmd(command: &str) -> Command { + let rustc = env::var(command).unwrap(); let mut cmd = Command::new(rustc); cmd.arg("--out-dir").arg(out_dir()).arg("-L").arg(out_dir()); cmd @@ -33,6 +33,10 @@ pub fn aux_build() -> AuxBuildInvocationBuilder { AuxBuildInvocationBuilder::new() } +pub fn rustdoc() -> Rustdoc { + Rustdoc::new() +} + #[derive(Debug)] pub struct RustcInvocationBuilder { cmd: Command, @@ -40,7 +44,7 @@ pub struct RustcInvocationBuilder { impl RustcInvocationBuilder { fn new() -> Self { - let cmd = setup_common_build_cmd(); + let cmd = setup_common_build_cmd("RUSTC"); Self { cmd } } @@ -74,7 +78,7 @@ pub struct AuxBuildInvocationBuilder { impl AuxBuildInvocationBuilder { fn new() -> Self { - let mut cmd = setup_common_build_cmd(); + let mut cmd = setup_common_build_cmd("RUSTC"); cmd.arg("--crate-type=lib"); Self { cmd } } @@ -97,6 +101,35 @@ impl AuxBuildInvocationBuilder { } } +#[derive(Debug)] +pub struct Rustdoc { + cmd: Command, +} + +impl Rustdoc { + fn new() -> Self { + let cmd = setup_common_build_cmd("RUSTDOC"); + Self { cmd } + } + + pub fn arg(&mut self, arg: &str) -> &mut Self { + self.cmd.arg(arg); + self + } + + #[track_caller] + pub fn run(&mut self) -> Output { + let caller_location = std::panic::Location::caller(); + let caller_line_number = caller_location.line(); + + let output = self.cmd.output().unwrap(); + if !output.status.success() { + handle_failed_output(&format!("{:#?}", self.cmd), output, caller_line_number); + } + output + } +} + fn run_common(bin_name: &str) -> (Command, Output) { let target = env::var("TARGET").unwrap(); From bc4f1697fad9a12d5096e63f45890ff289a3c349 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 Mar 2024 15:48:44 +0100 Subject: [PATCH 17/29] Add regression test for #122722 --- tests/run-make/rustdoc-test-args/foo.rs | 3 +++ tests/run-make/rustdoc-test-args/rmake.rs | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 tests/run-make/rustdoc-test-args/foo.rs create mode 100644 tests/run-make/rustdoc-test-args/rmake.rs diff --git a/tests/run-make/rustdoc-test-args/foo.rs b/tests/run-make/rustdoc-test-args/foo.rs new file mode 100644 index 0000000000000..51d17849fd718 --- /dev/null +++ b/tests/run-make/rustdoc-test-args/foo.rs @@ -0,0 +1,3 @@ +//! ``` +//! let x = 12; +//! ``` diff --git a/tests/run-make/rustdoc-test-args/rmake.rs b/tests/run-make/rustdoc-test-args/rmake.rs new file mode 100644 index 0000000000000..808d13928ebdb --- /dev/null +++ b/tests/run-make/rustdoc-test-args/rmake.rs @@ -0,0 +1,18 @@ +extern crate run_make_support; + +use run_make_support::{out_dir, rustdoc}; +use std::{fs, iter}; +use std::path::Path; + +fn generate_a_lot_of_cfgs(path: &Path) { + let content = iter::repeat("--cfg=a\n").take(100_000).collect::(); + fs::write(path, content.as_bytes()).expect("failed to create args file"); +} + +fn main() { + let arg_file = out_dir().join("args"); + generate_a_lot_of_cfgs(&arg_file); + + let arg_file = format!("@{}", arg_file.display()); + rustdoc().arg("--test").arg(&arg_file).arg("foo.rs").run(); +} From 949a3200355fda47076e00e25f485d3998d158c3 Mon Sep 17 00:00:00 2001 From: Kalle Wachsmuth Date: Mon, 19 Feb 2024 01:01:11 +0100 Subject: [PATCH 18/29] regression test for #103626 --- ...onicalize-fresh-infer-vars-issue-103626.rs | 15 +++++++++ ...alize-fresh-infer-vars-issue-103626.stderr | 33 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs create mode 100644 tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs new file mode 100644 index 0000000000000..3af299e5b115a --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.rs @@ -0,0 +1,15 @@ +trait FromResidual::Residual> { + fn from_residual(residual: R) -> Self; +} + +trait Try { + type Residual; +} + +fn w<'a, T: 'a, F: Fn(&'a T)>() { + let b: &dyn FromResidual = &(); + //~^ ERROR: the trait `FromResidual` cannot be made into an object + //~| ERROR: the trait `FromResidual` cannot be made into an object +} + +fn main() {} diff --git a/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr new file mode 100644 index 0000000000000..d5e9b1be63b0b --- /dev/null +++ b/tests/ui/traits/object/canonicalize-fresh-infer-vars-issue-103626.stderr @@ -0,0 +1,33 @@ +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:17 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^ + | + = note: it cannot use `Self` as a type parameter in a supertrait or `where`-clause + +error[E0038]: the trait `FromResidual` cannot be made into an object + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:10:12 + | +LL | let b: &dyn FromResidual = &(); + | ^^^^^^^^^^^^^^^^^ `FromResidual` cannot be made into an object + | +note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit + --> $DIR/canonicalize-fresh-infer-vars-issue-103626.rs:2:8 + | +LL | trait FromResidual::Residual> { + | ------------ this trait cannot be made into an object... +LL | fn from_residual(residual: R) -> Self; + | ^^^^^^^^^^^^^ ...because associated function `from_residual` has no `self` parameter +help: consider turning `from_residual` into a method by giving it a `&self` argument + | +LL | fn from_residual(&self, residual: R) -> Self; + | ++++++ +help: alternatively, consider constraining `from_residual` so it does not apply to trait objects + | +LL | fn from_residual(residual: R) -> Self where Self: Sized; + | +++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0038`. From 2f8b3246ddc07572e7e990ea2fabe9e1bad1eaa9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 11:57:26 +0100 Subject: [PATCH 19/29] add test for #99945 Fixes #99945 --- .../failed-to-normalize-ice-99945.rs | 36 +++++++++++++++++++ .../failed-to-normalize-ice-99945.stderr | 25 +++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs create mode 100644 tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs new file mode 100644 index 0000000000000..023991c29d090 --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.rs @@ -0,0 +1,36 @@ +// issue: rust-lang/rust#99945 +// ICE Failed to normalize + +#![feature(type_alias_impl_trait)] + +trait Widget { + type State; + + fn make_state(&self) -> Self::State; +} + +impl Widget for () { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +struct StatefulWidget(F); + +type StateWidget<'a> = impl Widget<&'a ()>; + +impl Fn(&'a ()) -> StateWidget<'a>> Widget<()> for StatefulWidget { + type State = (); + + fn make_state(&self) -> Self::State {} +} + +fn new_stateful_widget Fn(&'a ()) -> StateWidget<'a>>(build: F) -> impl Widget<()> { + StatefulWidget(build) + //~^ ERROR expected generic lifetime parameter, found `'a` +} + +fn main() { + new_stateful_widget(|_| ()).make_state(); + //~^ ERROR mismatched types +} diff --git a/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr new file mode 100644 index 0000000000000..0c76feae198ba --- /dev/null +++ b/tests/ui/type-alias-impl-trait/failed-to-normalize-ice-99945.stderr @@ -0,0 +1,25 @@ +error[E0308]: mismatched types + --> $DIR/failed-to-normalize-ice-99945.rs:34:29 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | ------------------- the expected opaque type +... +LL | new_stateful_widget(|_| ()).make_state(); + | ^^ expected opaque type, found `()` + | + = note: expected opaque type `StateWidget<'_>` + found unit type `()` + +error[E0792]: expected generic lifetime parameter, found `'a` + --> $DIR/failed-to-normalize-ice-99945.rs:29:5 + | +LL | type StateWidget<'a> = impl Widget<&'a ()>; + | -- this generic parameter must be used with a generic lifetime parameter +... +LL | StatefulWidget(build) + | ^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + +Some errors have detailed explanations: E0308, E0792. +For more information about an error, try `rustc --explain E0308`. From 6c71e5922bca613ae75558c37869f0d18efafc5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:01:39 +0100 Subject: [PATCH 20/29] add test for opaque type with non-universal region substs #101852 Fixes #101852 --- tests/ui/impl-trait/recursive-ice-101862.rs | 12 ++++++++++ .../ui/impl-trait/recursive-ice-101862.stderr | 24 +++++++++++++++++++ 2 files changed, 36 insertions(+) create mode 100644 tests/ui/impl-trait/recursive-ice-101862.rs create mode 100644 tests/ui/impl-trait/recursive-ice-101862.stderr diff --git a/tests/ui/impl-trait/recursive-ice-101862.rs b/tests/ui/impl-trait/recursive-ice-101862.rs new file mode 100644 index 0000000000000..02f95fe5604b2 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.rs @@ -0,0 +1,12 @@ +// issue: rust-lang/rust#101852 +// ICE opaque type with non-universal region substs + +pub fn ice(x: impl AsRef) -> impl IntoIterator { +//~^ WARN function cannot return without recursing + vec![].append(&mut ice(x.as_ref())); + //~^ ERROR expected generic type parameter, found `&str` + + Vec::new() +} + +fn main() {} diff --git a/tests/ui/impl-trait/recursive-ice-101862.stderr b/tests/ui/impl-trait/recursive-ice-101862.stderr new file mode 100644 index 0000000000000..f4148720c3331 --- /dev/null +++ b/tests/ui/impl-trait/recursive-ice-101862.stderr @@ -0,0 +1,24 @@ +warning: function cannot return without recursing + --> $DIR/recursive-ice-101862.rs:4:1 + | +LL | pub fn ice(x: impl AsRef) -> impl IntoIterator { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | --------------- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error[E0792]: expected generic type parameter, found `&str` + --> $DIR/recursive-ice-101862.rs:6:5 + | +LL | pub fn ice(x: impl AsRef) -> impl IntoIterator { + | --------------- this generic parameter must be used with a generic type parameter +LL | +LL | vec![].append(&mut ice(x.as_ref())); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error; 1 warning emitted + +For more information about this error, try `rustc --explain E0792`. From 2b4ca911a902fb339ba1cddb6320cceac4c46a31 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:19:05 +0100 Subject: [PATCH 21/29] add test for #104779 opaque types, patterns and subtyping ICE: IndexMap: key not found Fixes #104779 --- ...que-types-patterns-subtyping-ice-104779.rs | 26 ++++++++++++ ...types-patterns-subtyping-ice-104779.stderr | 42 +++++++++++++++++++ 2 files changed, 68 insertions(+) create mode 100644 tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs create mode 100644 tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs new file mode 100644 index 0000000000000..b9e729bff62a0 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.rs @@ -0,0 +1,26 @@ +// issue: rust-lang/rust#104779 +// ICE region infer, IndexMap: key not found + +struct Inv<'a>(&'a mut &'a ()); +enum Foo { + Bar, + Var(T), +} +type Subtype = Foo fn(Inv<'a>, Inv<'b>)>; +type Supertype = Foo fn(Inv<'a>, Inv<'a>)>; + +fn foo() -> impl Sized { +//~^ WARN function cannot return without recursing + loop { + match foo() { + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Subtype::Bar => (), + //~^ ERROR higher-ranked subtype error + //~^^ ERROR higher-ranked subtype error + Supertype::Var(x) => {} + } + } +} + +pub fn main() {} diff --git a/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr new file mode 100644 index 0000000000000..887cb14a76935 --- /dev/null +++ b/tests/ui/borrowck/opaque-types-patterns-subtyping-ice-104779.stderr @@ -0,0 +1,42 @@ +warning: function cannot return without recursing + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:12:1 + | +LL | fn foo() -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing +... +LL | match foo() { + | ----- recursive call site + | + = help: a `loop` may express intention better if this is on purpose + = note: `#[warn(unconditional_recursion)]` on by default + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:15:15 + | +LL | match foo() { + | ^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/opaque-types-patterns-subtyping-ice-104779.rs:18:13 + | +LL | Subtype::Bar => (), + | ^^^^^^^^^^^^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 4 previous errors; 1 warning emitted + From e9e9f7f31c1d9488caf07af5a6d83f1f7fdbe69b Mon Sep 17 00:00:00 2001 From: Luv-Ray Date: Sat, 23 Mar 2024 23:33:43 +0800 Subject: [PATCH 22/29] Add test in `higher-ranked` --- .../structually-relate-aliases.rs | 17 ++++++++++++ .../structually-relate-aliases.stderr | 27 +++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/ui/higher-ranked/structually-relate-aliases.rs create mode 100644 tests/ui/higher-ranked/structually-relate-aliases.stderr diff --git a/tests/ui/higher-ranked/structually-relate-aliases.rs b/tests/ui/higher-ranked/structually-relate-aliases.rs new file mode 100644 index 0000000000000..8df24702811dd --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.rs @@ -0,0 +1,17 @@ +// regression test for issue #121649. + +trait ToUnit<'a> { + type Unit; +} + +trait Overlap {} + +type Assoc<'a, T> = >::Unit; + +impl Overlap for T {} + +impl Overlap fn(&'a (), Assoc<'a, T>)> for T {} +//~^ ERROR 13:17: 13:49: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] +//~| ERROR 13:36: 13:48: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied [E0277] + +fn main() {} diff --git a/tests/ui/higher-ranked/structually-relate-aliases.stderr b/tests/ui/higher-ranked/structually-relate-aliases.stderr new file mode 100644 index 0000000000000..59fab52b221e2 --- /dev/null +++ b/tests/ui/higher-ranked/structually-relate-aliases.stderr @@ -0,0 +1,27 @@ +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, '^0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +WARN rustc_infer::infer::relate::generalize may incompletely handle alias type: AliasTy { args: [?1t, !2_0.Named(DefId(0:15 ~ structually_relate_aliases[de75]::{impl#1}::'a), "'a")], def_id: DefId(0:5 ~ structually_relate_aliases[de75]::ToUnit::Unit) } +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:36 + | +LL | impl Overlap fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl ToUnit<'a>> Overlap fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error[E0277]: the trait bound `for<'a> T: ToUnit<'a>` is not satisfied + --> $DIR/structually-relate-aliases.rs:13:17 + | +LL | impl Overlap fn(&'a (), Assoc<'a, T>)> for T {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `for<'a> ToUnit<'a>` is not implemented for `T` + | +help: consider restricting type parameter `T` + | +LL | impl ToUnit<'a>> Overlap fn(&'a (), Assoc<'a, T>)> for T {} + | ++++++++++++++++++++ + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0277`. From 612a385e29b8354580402baef5774f4d0cd4746f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:24:40 +0100 Subject: [PATCH 23/29] add test for ICE "raw ptr comparison should already be caught in the trait system" #105047 Fixes #105047 --- .../const-eval-compare-ice-105047.rs | 15 +++++++++ .../const-eval-compare-ice-105047.stderr | 31 +++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs create mode 100644 tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs new file mode 100644 index 0000000000000..0a736a5a8e21b --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs @@ -0,0 +1,15 @@ +// issue: rust-lang/rust#105047 +// ICE raw ptr comparison should already be caught in the trait systems + +#![feature(raw_ref_op)] + +const RCZ: *const i32 = &raw const *&0; + +const fn f() { + if let RCZ = &raw const *&0 { } + //~^ WARN function pointers and raw pointers not derived from integers in patterns + //~^^ ERROR pointers cannot be reliably compared during const eval + //~^^^ WARN this was previously accepted by the compiler but is being phased out +} + +fn main() {} diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr new file mode 100644 index 0000000000000..9c472cda2442c --- /dev/null +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.stderr @@ -0,0 +1,31 @@ +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 + = note: `#[warn(pointer_structural_match)]` on by default + +error: pointers cannot be reliably compared during const eval + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = note: see issue #53020 for more information + +error: aborting due to 1 previous error; 1 warning emitted + +Future incompatibility report: Future breakage diagnostic: +warning: function pointers and raw pointers not derived from integers in patterns behave unpredictably and should not be relied upon. See https://github.com/rust-lang/rust/issues/70861 for details. + --> $DIR/const-eval-compare-ice-105047.rs:9:12 + | +LL | if let RCZ = &raw const *&0 { } + | ^^^ + | + = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release! + = note: for more information, see issue #120362 + = note: `#[warn(pointer_structural_match)]` on by default + From ec01cbf1d8e71d4ea0e428713dc027aa1c8eaadc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:32:32 +0100 Subject: [PATCH 24/29] add test for #106423 Fixes #106423 --- .../poly-const-uneval-ice-106423.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs diff --git a/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs new file mode 100644 index 0000000000000..ed5ba32b62105 --- /dev/null +++ b/tests/ui/const-generics/generic_const_exprs/poly-const-uneval-ice-106423.rs @@ -0,0 +1,57 @@ +// issue: rust-lang/rust#106423 +// ICE collection encountered polymorphic constant: UnevaluatedConst {..} +//@ edition:2021 +//@ check-pass + +#![feature(generic_const_exprs, generic_arg_infer)] +#![allow(incomplete_features)] +#![allow(unused)] + +use core::mem::MaybeUninit; + +pub struct Arr { + v: [MaybeUninit; N], +} + +impl Arr { + const ELEM: MaybeUninit = MaybeUninit::uninit(); + const INIT: [MaybeUninit; N] = [Self::ELEM; N]; // important for optimization of `new` + + fn new() -> Self { + Arr { v: Self::INIT } + } +} + +pub struct BaFormatFilter {} + +pub enum DigitalFilter +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + Ba(BaFormatFilter<{ N * 2 + 1 }>), +} + +pub fn iirfilter_st_copy(_: [f32; M]) -> DigitalFilter +where + [(); N * 2 + 1]: Sized, + [(); N * 2]: Sized, +{ + let zpk = zpk2tf_st(&Arr::::new(), &Arr::::new()); + DigitalFilter::Ba(zpk) +} + +pub fn zpk2tf_st( + _z: &Arr, + _p: &Arr, +) -> BaFormatFilter<{ N + 1 }> +where + [(); N + 1]: Sized, +{ + BaFormatFilter {} +} + + +fn main() { + iirfilter_st_copy::<4, 2>([10., 50.,]); +} From 0b797dfda970a0f151c07a52be57dba9da3656de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:38:50 +0100 Subject: [PATCH 25/29] add test for ICE #106444 Fixes #106444 --- tests/ui/drop/norm-ice-106444.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/ui/drop/norm-ice-106444.rs diff --git a/tests/ui/drop/norm-ice-106444.rs b/tests/ui/drop/norm-ice-106444.rs new file mode 100644 index 0000000000000..b248bc73bbefa --- /dev/null +++ b/tests/ui/drop/norm-ice-106444.rs @@ -0,0 +1,16 @@ +// issue: rust-lang/rust#106444 +// ICE failed to normalize +//@ compile-flags: -Zmir-opt-level=3 +//@ check-pass + +#![crate_type="lib"] + +pub trait A { + type B; +} + +pub struct S(T::B); + +pub fn foo(p: *mut S) { + unsafe { core::ptr::drop_in_place(p) }; +} From 03a844c42c98bb71e4327aef44ea5bacc030c055 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 12:50:21 +0100 Subject: [PATCH 26/29] add test for #106874 ICE BoundUniversalRegionError Fixes #106874 --- tests/ui/nll/ice-106874.rs | 48 ++++++++++++++++++ tests/ui/nll/ice-106874.stderr | 90 ++++++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/ui/nll/ice-106874.rs create mode 100644 tests/ui/nll/ice-106874.stderr diff --git a/tests/ui/nll/ice-106874.rs b/tests/ui/nll/ice-106874.rs new file mode 100644 index 0000000000000..77a91d6d823a2 --- /dev/null +++ b/tests/ui/nll/ice-106874.rs @@ -0,0 +1,48 @@ +// issue: rust-lang/rust#106874 +// ICE BoundUniversalRegionError + +use std::marker::PhantomData; +use std::rc::Rc; + +pub fn func(f: F) -> A { + A(B(C::new(D::new(move |st| f(st))))) + //~^ ERROR implementation of `FnOnce` is not general enough + //~^^ ERROR implementation of `Fn` is not general enough + //~^^^ ERROR implementation of `FnOnce` is not general enough + //~^^^^ ERROR implementation of `FnOnce` is not general enough + //~^^^^^ ERROR implementation of `Fn` is not general enough + //~^^^^^^ ERROR implementation of `FnOnce` is not general enough + //~^^^^^^^ ERROR implementation of `Fn` is not general enough + //~^^^^^^^^ ERROR implementation of `FnOnce` is not general enough + //~^^^^^^^^^ ERROR higher-ranked subtype error + //~^^^^^^^^^^ ERROR higher-ranked subtype error +} + +trait X {} +trait Y { + type V; +} + +struct A(T); + +struct B(Rc); +impl X for B {} + +struct C(T::V); +impl C { + fn new(_: T) -> Rc { + todo!() + } +} +struct D(F, PhantomData); + +impl D { + fn new(_: F) -> Self { + todo!() + } +} +impl Y for D { + type V = V; +} + +pub fn main() {} diff --git a/tests/ui/nll/ice-106874.stderr b/tests/ui/nll/ice-106874.stderr new file mode 100644 index 0000000000000..ead4d490a6248 --- /dev/null +++ b/tests/ui/nll/ice-106874.stderr @@ -0,0 +1,90 @@ +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:5 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'0 mut V)` must implement `FnOnce<(&mut V,)>`, for some specific lifetime `'0`... + = note: ...but it actually implements `FnOnce<(&'1 mut V,)>`, for some specific lifetime `'1` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:7 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `Fn` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Fn` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `Fn<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `Fn<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: implementation of `FnOnce` is not general enough + --> $DIR/ice-106874.rs:8:9 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough + | + = note: closure with signature `fn(&'2 mut V)` must implement `FnOnce<(&'1 mut V,)>`, for any lifetime `'1`... + = note: ...but it actually implements `FnOnce<(&'2 mut V,)>`, for some specific lifetime `'2` + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + +error: higher-ranked subtype error + --> $DIR/ice-106874.rs:8:41 + | +LL | A(B(C::new(D::new(move |st| f(st))))) + | ^ + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + +error: aborting due to 10 previous errors + From 832894a438d03bcbf6b63656a45f2e8b77e4e086 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 13:05:22 +0100 Subject: [PATCH 27/29] add test for #107228 Fixes #107228 --- .../broken-mir-drop-glue-107228.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 tests/ui/specialization/broken-mir-drop-glue-107228.rs diff --git a/tests/ui/specialization/broken-mir-drop-glue-107228.rs b/tests/ui/specialization/broken-mir-drop-glue-107228.rs new file mode 100644 index 0000000000000..5a6dbf9ffc761 --- /dev/null +++ b/tests/ui/specialization/broken-mir-drop-glue-107228.rs @@ -0,0 +1,28 @@ +// issue: rust-lang/rust#107228 +// ICE broken MIR in DropGlue +//@ compile-flags: -Zvalidate-mir +//@ check-pass + +#![feature(specialization)] +#![crate_type="lib"] +#![allow(incomplete_features)] + +pub(crate) trait SpecTrait { + type Assoc; +} + +impl SpecTrait for C { + default type Assoc = Vec; +} + +pub(crate) struct AssocWrap { + _assoc: C::Assoc, +} + +fn instantiate() -> AssocWrap { + loop {} +} + +pub fn main() { + instantiate::<()>(); +} From df6b6b42113c890554b24fc4393bdf4d70a41eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matthias=20Kr=C3=BCger?= Date: Sat, 23 Mar 2024 16:14:42 +0100 Subject: [PATCH 28/29] address review feedback --- tests/ui/nll/ice-106874.rs | 18 +++++++++--------- .../const-eval-compare-ice-105047.rs | 4 ++-- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/tests/ui/nll/ice-106874.rs b/tests/ui/nll/ice-106874.rs index 77a91d6d823a2..9337eee961bfb 100644 --- a/tests/ui/nll/ice-106874.rs +++ b/tests/ui/nll/ice-106874.rs @@ -7,15 +7,15 @@ use std::rc::Rc; pub fn func(f: F) -> A { A(B(C::new(D::new(move |st| f(st))))) //~^ ERROR implementation of `FnOnce` is not general enough - //~^^ ERROR implementation of `Fn` is not general enough - //~^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^^ ERROR implementation of `Fn` is not general enough - //~^^^^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^^^^ ERROR implementation of `Fn` is not general enough - //~^^^^^^^^ ERROR implementation of `FnOnce` is not general enough - //~^^^^^^^^^ ERROR higher-ranked subtype error - //~^^^^^^^^^^ ERROR higher-ranked subtype error + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR implementation of `Fn` is not general enough + //~| ERROR implementation of `FnOnce` is not general enough + //~| ERROR higher-ranked subtype error + //~| ERROR higher-ranked subtype error } trait X {} diff --git a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs index 0a736a5a8e21b..87ce4f1e14d94 100644 --- a/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs +++ b/tests/ui/raw-ref-op/const-eval-compare-ice-105047.rs @@ -8,8 +8,8 @@ const RCZ: *const i32 = &raw const *&0; const fn f() { if let RCZ = &raw const *&0 { } //~^ WARN function pointers and raw pointers not derived from integers in patterns - //~^^ ERROR pointers cannot be reliably compared during const eval - //~^^^ WARN this was previously accepted by the compiler but is being phased out + //~| ERROR pointers cannot be reliably compared during const eval + //~| WARN this was previously accepted by the compiler but is being phased out } fn main() {} From c513dd7226421b351df0cdec5f4621199f2a1300 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 23 Mar 2024 21:36:22 +0100 Subject: [PATCH 29/29] core/panicking: fix outdated comment --- library/core/src/panicking.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/core/src/panicking.rs b/library/core/src/panicking.rs index 9e8dac888166c..a8940d9cd1eb0 100644 --- a/library/core/src/panicking.rs +++ b/library/core/src/panicking.rs @@ -132,11 +132,11 @@ pub const fn panic_nounwind_fmt(fmt: fmt::Arguments<'_>, force_no_backtrace: boo #[rustc_const_unstable(feature = "panic_internals", issue = "none")] #[lang = "panic"] // needed by codegen for panic on overflow and other `Assert` MIR terminators pub const fn panic(expr: &'static str) -> ! { - // Use Arguments::new_v1 instead of format_args!("{expr}") to potentially + // 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_v1 may allow the compiler to omit Formatter::pad from the + // 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(&[expr])); }