From d3bdea115d929178e0a3189fb4fdf0e827529ed3 Mon Sep 17 00:00:00 2001 From: Urgau Date: Tue, 14 Apr 2026 19:57:30 +0200 Subject: [PATCH 001/174] Stabilize `--remap-path-prefix` in rustdoc --- src/doc/rustdoc/src/command-line-arguments.md | 12 ++++++++++++ src/doc/rustdoc/src/unstable-features.md | 8 -------- src/librustdoc/lib.rs | 16 ++++++++-------- .../rustdoc-default-output/output-default.stdout | 4 ++-- tests/rustdoc-html/auxiliary/remapped-paths.rs | 2 +- tests/rustdoc-ui/lints/remap-path-prefix-lint.rs | 2 +- tests/rustdoc-ui/remap-path-prefix-doctest.rs | 2 +- .../remap-path-prefix-failed-doctest-output.rs | 2 +- .../remap-path-prefix-invalid-doctest.rs | 2 +- .../rustdoc-ui/remap-path-prefix-macro-138520.rs | 2 +- .../remap-path-prefix-passed-doctest-output.rs | 2 +- tests/rustdoc-ui/remap-path-prefix.rs | 10 +++++----- 12 files changed, 34 insertions(+), 30 deletions(-) diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index b55ddf6e0e165..6736d3c82fc2e 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -183,6 +183,18 @@ affect that. The arguments to this flag are the same as those for the `-C` flag on rustc. Run `rustc -C help` to get the full list. +## `--remap-path-prefix`: remap source paths in output + +This flag is the equivalent flag from `rustc`: `--remap-path-prefix`. + +```bash +$ rustdoc src/lib.rs --remap-path-prefix="$PWD=/foo" +``` + +It permits remapping (as a best effort) source path prefixes in all output, including diagnostics, +debug information, macro expansions, generated documentation, etc. It takes a value of the +form `FROM=TO` where a path prefix equal to `FROM` is rewritten to the value `TO`. + ## `--test`: run code examples as tests Using this flag looks like this: diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 1c91ad343b8c0..7d8c464002bfc 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -751,14 +751,6 @@ pass `--doctest-build-arg ARG` for each argument `ARG`. This flag enables the generation of toggles to expand macros in the HTML source code pages. -## `--remap-path-prefix`: Remap source code paths in output - -This flag is the equivalent flag from `rustc` `--remap-path-prefix`. - -it permits remapping source path prefixes in all output, including compiler diagnostics, -debug information, macro expansions, etc. It takes a value of the form `FROM=TO` -where a path prefix equal to `FROM` is rewritten to the value `TO`. - ## `--remap-path-scope`: Scopes to which the source remapping should be done This flag is the equivalent flag from `rustc` `--remap-path-scope`. diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 750ce27ea7962..419491e2387be 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -456,6 +456,14 @@ fn opts() -> Vec { By default, it is at `forbid` level.", "LEVEL", ), + opt( + Stable, + Multi, + "", + "remap-path-prefix", + "Remap source names in compiler messages", + "FROM=TO", + ), opt(Unstable, Opt, "", "index-page", "Markdown file to be used as index page", "PATH"), opt( Unstable, @@ -548,14 +556,6 @@ fn opts() -> Vec { "Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error.", "yes|no|auto", ), - opt( - Unstable, - Multi, - "", - "remap-path-prefix", - "Remap source names in compiler messages", - "FROM=TO", - ), opt( Unstable, Opt, diff --git a/tests/run-make/rustdoc-default-output/output-default.stdout b/tests/run-make/rustdoc-default-output/output-default.stdout index 7202ed71a2a82..0a2da1099b820 100644 --- a/tests/run-make/rustdoc-default-output/output-default.stdout +++ b/tests/run-make/rustdoc-default-output/output-default.stdout @@ -125,6 +125,8 @@ Options: Set the most restrictive lint level. More restrictive lints are capped at this level. By default, it is at `forbid` level. + --remap-path-prefix FROM=TO + Remap source names in compiler messages --index-page PATH Markdown file to be used as index page --enable-index-page @@ -158,8 +160,6 @@ Options: Force all doctests to be compiled as a single binary, instead of one binary per test. If merging fails, rustdoc will emit a hard error. - --remap-path-prefix FROM=TO - Remap source names in compiler messages --remap-path-scope [macro,diagnostics,debuginfo,coverage,object,all] Defines which scopes of paths should be remapped by `--remap-path-prefix` diff --git a/tests/rustdoc-html/auxiliary/remapped-paths.rs b/tests/rustdoc-html/auxiliary/remapped-paths.rs index f31d2d316f3aa..b361c012bc238 100644 --- a/tests/rustdoc-html/auxiliary/remapped-paths.rs +++ b/tests/rustdoc-html/auxiliary/remapped-paths.rs @@ -1,4 +1,4 @@ -//@ compile-flags:-Zunstable-options --remap-path-prefix={{src-base}}= +//@ compile-flags:--remap-path-prefix={{src-base}}= pub struct MyStruct { field: u32, diff --git a/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs b/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs index d003e19f200d6..f3c48f024b2de 100644 --- a/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs +++ b/tests/rustdoc-ui/lints/remap-path-prefix-lint.rs @@ -1,7 +1,7 @@ // Regression test for remapped paths in rustdoc errors // . -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@ rustc-env:RUST_BACKTRACE=0 #![deny(rustdoc::invalid_html_tags)] diff --git a/tests/rustdoc-ui/remap-path-prefix-doctest.rs b/tests/rustdoc-ui/remap-path-prefix-doctest.rs index 34fff98d5caa2..dcfc2ddf62e4f 100644 --- a/tests/rustdoc-ui/remap-path-prefix-doctest.rs +++ b/tests/rustdoc-ui/remap-path-prefix-doctest.rs @@ -9,7 +9,7 @@ //@ revisions: without-scope //@ compile-flags:--test --test-args --test-threads=1 -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-scope=diagnostics //@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-scope=macro diff --git a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs index 72c3330709a7c..003663f7d0aa5 100644 --- a/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs +++ b/tests/rustdoc-ui/remap-path-prefix-failed-doctest-output.rs @@ -2,7 +2,7 @@ // adapted to use that, and that normalize line can go away //@ failure-status: 101 -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ rustc-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" //@ normalize-stdout: "exit (status|code): 101" -> "exit status: 101" diff --git a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs index c18a416d43f08..bed051e341a8f 100644 --- a/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs +++ b/tests/rustdoc-ui/remap-path-prefix-invalid-doctest.rs @@ -2,7 +2,7 @@ // adapted to use that, and that normalize line can go away //@ failure-status: 101 -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ rustc-env:RUST_BACKTRACE=0 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" diff --git a/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs b/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs index 1be22694b8cdc..595451a7924bb 100644 --- a/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs +++ b/tests/rustdoc-ui/remap-path-prefix-macro-138520.rs @@ -2,7 +2,7 @@ // when using --remap-path-prefix with macro rendering. // -//@ compile-flags:-Z unstable-options --remap-path-prefix={{src-base}}=remapped_path +//@ compile-flags:--remap-path-prefix={{src-base}}=remapped_path //@ rustc-env:RUST_BACKTRACE=0 //@ build-pass diff --git a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs index 6fa04ef77f32b..5e6be3a2515d4 100644 --- a/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs +++ b/tests/rustdoc-ui/remap-path-prefix-passed-doctest-output.rs @@ -4,7 +4,7 @@ // FIXME: if/when the output of the test harness can be tested on its own, this test should be // adapted to use that, and that normalize line can go away -//@ compile-flags:--test -Z unstable-options --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 +//@ compile-flags:--test --remap-path-prefix={{src-base}}=remapped_path --test-args --test-threads=1 //@ normalize-stdout: "finished in \d+\.\d+s" -> "finished in $$TIME" // doctest passes at runtime diff --git a/tests/rustdoc-ui/remap-path-prefix.rs b/tests/rustdoc-ui/remap-path-prefix.rs index e3efa9a693490..8c6e2bde95717 100644 --- a/tests/rustdoc-ui/remap-path-prefix.rs +++ b/tests/rustdoc-ui/remap-path-prefix.rs @@ -7,11 +7,11 @@ //@ revisions: with-diag-scope with-macro-scope with-debuginfo-scope with-doc-scope //@ revisions: without-scopes without-remap -//@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-debuginfo-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[with-doc-scope] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped -//@[without-scopes] compile-flags: -Zunstable-options --remap-path-prefix={{src-base}}=remapped +//@[with-diag-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-macro-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-debuginfo-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[with-doc-scope] compile-flags: --remap-path-prefix={{src-base}}=remapped +//@[without-scopes] compile-flags: --remap-path-prefix={{src-base}}=remapped //@[with-diag-scope] compile-flags: -Zunstable-options --remap-path-scope=diagnostics //@[with-macro-scope] compile-flags: -Zunstable-options --remap-path-scope=macro From e264a92936000d2a3ed7b4a472a028f483d4bae6 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Sun, 26 Apr 2026 23:53:22 -0500 Subject: [PATCH 002/174] gracefully handle invalid string/vec --- src/etc/lldb_providers.py | 68 ++++++++++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 12 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f20210323eb..e2d4ac5286ccd 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -305,16 +305,33 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): ) length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) if length <= 0: return '""' + + no_hi_bit_max: int = 1 << ((pointer.GetByteSize() * 8) - 1) + # technically length isn't a NoHighBit, but length should always be <= capacity + if length >= no_hi_bit_max or capacity >= no_hi_bit_max: + return "" + if pointer.GetValueAsUnsigned() == 0: + return "" + error = SBError() process = pointer.GetProcess() - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - raise Exception("ReadMemory error: %s", error.GetCString()) + try: + data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) + if error.Success(): + return '"' + data.decode("utf8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -443,6 +460,9 @@ def has_children(self) -> bool: class StdStringSyntheticProvider: def __init__(self, valobj: SBValue, _dict: LLDBOpaque): self.valobj = valobj + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) + self.update() def update(self): @@ -454,7 +474,25 @@ def update(self): .GetChildMemberWithName("pointer") .GetChildMemberWithName("pointer") ) - self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + + self.capacity = ( + inner_vec.GetChildMemberWithName("buf") + .GetChildMemberWithName("cap") + .GetValueAsUnsigned() + ) + + # As of 4/18/2026, LLDB cannot accurately determine the difference between Some("") and None + # this just makes sure we're not trying to access data when the string is clearly in an + # invalid state. + if ( + self.capacity >= self.no_hi_bit_max + or self.data_ptr.GetValueAsUnsigned() == 0 + ): + self.capacity = 0 + self.length = 0 + else: + self.length = inner_vec.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.data_ptr.GetType().GetPointeeType() def has_children(self) -> bool: @@ -928,6 +966,8 @@ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): # logger >> "[StdVecSyntheticProvider] for " + str(valobj.GetName()) self.valobj = valobj self.element_type = None + ptr_size = valobj.GetTarget().GetAddressByteSize() * 8 + self.no_hi_bit_max = 1 << (ptr_size - 1) self.update() def num_children(self) -> int: @@ -949,15 +989,19 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() - self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( - "inner" - ) - + buf: SBValue = self.valobj.GetChildMemberWithName("buf") self.data_ptr = unwrap_unique_or_non_null( - self.buf.GetChildMemberWithName("ptr") + buf.GetChildMemberWithName("inner").GetChildMemberWithName("ptr") ) + capacity: int = buf.GetChildMemberWithName("cap").GetValueAsUnsigned() + + if capacity >= self.no_hi_bit_max or self.data_ptr.GetValueAsUnsigned() == 0: + self.capacity = 0 + self.length = 0 + else: + self.length = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() + self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) if not self.element_type.IsValid(): From 008ea3a340e7a1c9dc8d6e274e9e440e01f34e87 Mon Sep 17 00:00:00 2001 From: mejrs <59372212+mejrs@users.noreply.github.com> Date: Tue, 5 May 2026 18:26:18 +0200 Subject: [PATCH 003/174] rustc_on_unimplemented: introduce format specifiers as printing options for the annotated item. --- .../src/attributes/diagnostic/mod.rs | 45 ++++++++------ compiler/rustc_hir/src/attrs/diagnostic.rs | 20 +++--- compiler/rustc_parse_format/src/lib.rs | 6 +- compiler/rustc_parse_format/src/tests.rs | 4 +- compiler/rustc_span/src/symbol.rs | 1 - .../traits/on_unimplemented.rs | 6 +- library/core/src/ops/function.rs | 12 ++-- src/doc/rustc-dev-guide/src/diagnostics.md | 7 ++- .../on_unimplemented/broken_format.stderr | 12 ++-- ...options_of_the_internal_rustc_attribute.rs | 7 ++- ...ons_of_the_internal_rustc_attribute.stderr | 62 ++++++++++++------- tests/ui/on-unimplemented/format_specs.rs | 13 ++++ tests/ui/on-unimplemented/format_specs.stderr | 22 +++++++ 13 files changed, 143 insertions(+), 74 deletions(-) create mode 100644 tests/ui/on-unimplemented/format_specs.rs create mode 100644 tests/ui/on-unimplemented/format_specs.stderr diff --git a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs index 9b4ffc7ea27f6..c2dfc4a147ee3 100644 --- a/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs +++ b/compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs @@ -396,9 +396,7 @@ pub(crate) fn parse_format_string( .map(|piece| match piece { RpfPiece::Lit(lit) => Piece::Lit(Symbol::intern(lit)), RpfPiece::NextArgument(arg) => { - warn_on_format_spec(&arg.format, &mut warnings, span, parser.is_source_literal); - let arg = parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal); - Piece::Arg(arg) + Piece::Arg(parse_arg(&arg, mode, &mut warnings, span, parser.is_source_literal)) } }) .collect(); @@ -415,15 +413,25 @@ fn parse_arg( ) -> FormatArg { let span = slice_span(input_span, arg.position_span.clone(), is_source_literal); - match arg.position { + let mut check_format = true; + + let ret = match arg.position { // Something like "hello {name}" Position::ArgumentNamed(name) => match (mode, Symbol::intern(name)) { (Mode::RustcOnUnimplemented, sym::ItemContext) => FormatArg::ItemContext, - // Like `{This}`, but sugared. - // FIXME(mejrs) maybe rename/rework this or something - // if we want to apply this to other attrs? - (Mode::RustcOnUnimplemented, sym::Trait) => FormatArg::Trait, + // `{This:ty}` + (Mode::RustcOnUnimplemented, sym::This) => match arg.format.ty { + "resolved" => { + check_format = false; + FormatArg::ThisResolved + } + "path" => { + check_format = false; + FormatArg::ThisPath + } + _ => FormatArg::This, + }, // Some diagnostic attributes can use `{This}` to refer to the annotated item. // For those that don't, we continue and maybe use it as a generic parameter. @@ -431,10 +439,7 @@ fn parse_arg( // FIXME(mejrs) `DiagnosticOnUnimplemented` is intentionally not here; // that requires lang approval which is best kept for a standalone PR. ( - Mode::RustcOnUnimplemented - | Mode::DiagnosticOnUnknown - | Mode::DiagnosticOnMove - | Mode::DiagnosticOnUnmatchArgs, + Mode::DiagnosticOnUnknown | Mode::DiagnosticOnMove | Mode::DiagnosticOnUnmatchArgs, sym::This, ) => FormatArg::This, @@ -471,11 +476,11 @@ fn parse_arg( attr: mode.as_str(), allowed: mode.allowed_format_arguments(), }); - return FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}"))); + FormatArg::AsIs(Symbol::intern(&format!("{{{as_is}}}"))) } }, - // `{:1}` and `{}` are ignored + // `{1}` and `{}` are ignored Position::ArgumentIs(idx) => { warnings.push(FormatWarning::IndexedArgument { span }); FormatArg::AsIs(Symbol::intern(&format!("{{{idx}}}"))) @@ -484,7 +489,11 @@ fn parse_arg( warnings.push(FormatWarning::PositionalArgument { span }); FormatArg::AsIs(sym::empty_braces) } + }; + if check_format { + warn_on_format_spec(&arg.format, warnings, input_span, is_source_literal); } + ret } /// `#[rustc_on_unimplemented]` and `#[diagnostic::...]` don't actually do anything @@ -495,12 +504,8 @@ fn warn_on_format_spec( input_span: Span, is_source_literal: bool, ) { - if spec.ty != "" { - let span = spec - .ty_span - .as_ref() - .map(|inner| slice_span(input_span, inner.clone(), is_source_literal)) - .unwrap_or(input_span); + if let Some(ty_span) = &spec.ty_span { + let span = slice_span(input_span, ty_span.clone(), is_source_literal); warnings.push(FormatWarning::InvalidSpecifier { span }) } } diff --git a/compiler/rustc_hir/src/attrs/diagnostic.rs b/compiler/rustc_hir/src/attrs/diagnostic.rs index 7cbb0ea45b969..7d4ae803cee59 100644 --- a/compiler/rustc_hir/src/attrs/diagnostic.rs +++ b/compiler/rustc_hir/src/attrs/diagnostic.rs @@ -147,11 +147,12 @@ impl FormatString { }; ret.push_str(&slf); } + Piece::Arg(FormatArg::This) => ret.push_str(&args.this), // It's only `rustc_onunimplemented` from here - Piece::Arg(FormatArg::This) => ret.push_str(&args.this), - Piece::Arg(FormatArg::Trait) => { - let _ = fmt::write(&mut ret, format_args!("{}", &args.this_sugared)); + Piece::Arg(FormatArg::ThisPath) => ret.push_str(&args.this_path), + Piece::Arg(FormatArg::ThisResolved) => { + let _ = fmt::write(&mut ret, format_args!("{}", &args.this_resolved)); } Piece::Arg(FormatArg::ItemContext) => ret.push_str(args.item_context), } @@ -197,7 +198,7 @@ impl FormatString { /// ```rust,ignore (just an example) /// FormatArgs { /// this: "FromResidual", -/// this_sugared: "FromResidual>", +/// this_resolved: "FromResidual>", /// item_context: "an async function", /// generic_args: [("Self", "u32"), ("R", "Option")], /// } @@ -206,7 +207,8 @@ impl FormatString { pub struct FormatArgs { /// The name of the item the attribute is on. pub this: String, - pub this_sugared: String = String::new(), + pub this_resolved: String = String::new(), + pub this_path: String = String::new(), pub item_context: &'static str = "", pub generic_args: Vec<(Symbol, String)> = Vec::new(), } @@ -226,10 +228,12 @@ pub enum FormatArg { }, // `{Self}` SelfUpper, - /// `{This}` or `{TraitName}` + /// `{This}` and `{This:name}`. This, - /// The sugared form of the trait - Trait, + /// The sugared form: `{This:sugared}`. + ThisResolved, + /// The full path: `{This:path}`. + ThisPath, /// what we're in, like a function, method, closure etc. ItemContext, /// What the user typed, if it doesn't match anything we can use. diff --git a/compiler/rustc_parse_format/src/lib.rs b/compiler/rustc_parse_format/src/lib.rs index 20f4646490a6b..9e1281fb82cdc 100644 --- a/compiler/rustc_parse_format/src/lib.rs +++ b/compiler/rustc_parse_format/src/lib.rs @@ -750,15 +750,15 @@ impl<'input> Parser<'input> { spec } - /// Always returns an empty `FormatSpec` + /// Always returns an empty `FormatSpec`, except for the `ty` and `ty_span` fields. fn diagnostic(&mut self) -> FormatSpec<'input> { let mut spec = FormatSpec::default(); - let Some((Range { start, .. }, start_idx)) = self.consume_pos(':') else { + let Some((Range { start, .. }, _)) = self.consume_pos(':') else { return spec; }; - spec.ty = self.string(start_idx); + spec.ty = self.string(self.input_vec_index); spec.ty_span = { let end = self.input_vec_index2range(self.input_vec_index).start; Some(start..end) diff --git a/compiler/rustc_parse_format/src/tests.rs b/compiler/rustc_parse_format/src/tests.rs index a6c7e1890abd1..de6046ec8b9dd 100644 --- a/compiler/rustc_parse_format/src/tests.rs +++ b/compiler/rustc_parse_format/src/tests.rs @@ -567,7 +567,7 @@ fn diagnostic_format_flags() { Argument { position: ArgumentNamed("thing"), position_span: 2..7, - format: FormatSpec { ty: ":blah", ty_span: Some(7..12), ..Default::default() }, + format: FormatSpec { ty: "blah", ty_span: Some(7..12), ..Default::default() }, } ); @@ -588,7 +588,7 @@ fn diagnostic_format_mod() { Argument { position: ArgumentNamed("thing"), position_span: 2..7, - format: FormatSpec { ty: ":+", ty_span: Some(7..9), ..Default::default() }, + format: FormatSpec { ty: "+", ty_span: Some(7..9), ..Default::default() }, } ); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 695103a249b49..998588d2d835e 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -317,7 +317,6 @@ symbols! { Target, This, TokenStream, - Trait, TrivialClone, Try, TryCaptureGeneric, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs index b15f528b7261e..89dd0cf22f612 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/on_unimplemented.rs @@ -217,7 +217,9 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { })); let this = self.tcx.def_path_str(trait_pred.trait_ref.def_id); - let this_sugared = trait_pred.trait_ref.print_trait_sugared().to_string(); + let this_resolved = trait_pred.trait_ref.print_trait_sugared().to_string(); + let this_path = + ty::TraitRef::identity(self.tcx, def_id).print_only_trait_path().to_string(); let filter_options = FilterOptions { self_types, from_desugaring, cause, crate_local, direct, generic_args }; @@ -249,7 +251,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { }) .collect(); - let format_args = FormatArgs { this, this_sugared, generic_args, item_context }; + let format_args = FormatArgs { this, this_path, this_resolved, generic_args, item_context }; (filter_options, format_args) } } diff --git a/library/core/src/ops/function.rs b/library/core/src/ops/function.rs index efe5c0871fb8b..57b41e6ad6ccd 100644 --- a/library/core/src/ops/function.rs +++ b/library/core/src/ops/function.rs @@ -67,8 +67,8 @@ use crate::marker::Tuple; // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] @@ -154,8 +154,8 @@ pub const trait Fn: [const] FnMut { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] @@ -233,8 +233,8 @@ pub const trait FnMut: FnOnce { // SAFETY: tidy is not smart enough to tell that the below unsafe block is a string label = "call the function in a closure: `|| unsafe {{ /* code */ }}`" ), - message = "expected a `{Trait}` closure, found `{Self}`", - label = "expected an `{Trait}` closure, found `{Self}`" + message = "expected a `{This:resolved}` closure, found `{Self}`", + label = "expected an `{This:resolved}` closure, found `{Self}`" )] #[fundamental] // so that regex can rely that `&str: !FnMut` #[must_use = "closures are lazy and do nothing unless called"] diff --git a/src/doc/rustc-dev-guide/src/diagnostics.md b/src/doc/rustc-dev-guide/src/diagnostics.md index 554bcfcf8e4b6..7e3da67d872c1 100644 --- a/src/doc/rustc-dev-guide/src/diagnostics.md +++ b/src/doc/rustc-dev-guide/src/diagnostics.md @@ -1018,12 +1018,13 @@ pub trait From: Sized { ### Formatting The string literals are format strings that accept parameters wrapped in braces -but positional and listed parameters and format specifiers are not accepted. +but positional and listed parameters are not accepted. The following parameter names are valid: - `Self` and all generic parameters of the trait. - `This`: the name of the trait the attribute is on, without generics. -- `Trait`: the name of the "sugared" trait. - See `TraitRefPrintSugared`. +- `This:path`: the full path of the trait the attribute is on, with unresolved generics. +- `This:resolved`: the full path of the trait the attribute is on, with resolved generics. +Additionally, this will "sugar" the `Fn(...)` traits. - `ItemContext`: the kind of `hir::Node` we're in, things like `"an async block"`, `"a function"`, `"an async function"`, etc. diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr index 983f871dde109..b193d4be83c8d 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/broken_format.stderr @@ -16,18 +16,18 @@ LL | #[diagnostic::on_unimplemented(message = "Test {}")] | = help: you can print empty braces by escaping them -warning: format specifiers are not permitted in diagnostic attributes - --> $DIR/broken_format.rs:10:50 - | -LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] - | ^ remove this format specifier - warning: indexed format arguments are not permitted in diagnostic attributes --> $DIR/broken_format.rs:10:49 | LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] | ^ remove this format argument +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/broken_format.rs:10:50 + | +LL | #[diagnostic::on_unimplemented(message = "Test {1:}")] + | ^ remove this format specifier + warning: format specifiers are not permitted in diagnostic attributes --> $DIR/broken_format.rs:15:53 | diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs index e3c38c7f55c95..aa4e14fb04f2f 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.rs @@ -35,13 +35,16 @@ impl Bar for i32 {} //~|WARN there is no parameter `r#struct` on trait `Baz` //~|WARN there is no parameter `r#enum` on trait `Baz` //~|WARN there is no parameter `union` on trait `Baz` - label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" + label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" //~^WARN there is no parameter `float` on trait `Baz` //~|WARN there is no parameter `_Self` on trait `Baz` //~|WARN there is no parameter `crate_local` on trait `Baz` - //~|WARN there is no parameter `Trait` on trait `Baz` //~|WARN there is no parameter `ItemContext` on trait `Baz` //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN there is no parameter `This` on trait `Baz` + //~|WARN format specifiers are not permitted in diagnostic attributes + //~|WARN format specifiers are not permitted in diagnostic attributes )] trait Baz {} diff --git a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr index 9ed72d334b271..7ab7f25ee6964 100644 --- a/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr +++ b/tests/ui/diagnostic_namespace/on_unimplemented/do_not_accept_options_of_the_internal_rustc_attribute.stderr @@ -66,7 +66,7 @@ LL | message = "{from_desugaring}{direct}{cause}{integral}{integer}{struct}{ warning: there is no parameter `float` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:15 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -74,7 +74,7 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" warning: there is no parameter `_Self` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:22 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -82,32 +82,40 @@ LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" warning: there is no parameter `crate_local` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:29 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument -warning: there is no parameter `Trait` on trait `Baz` +warning: there is no parameter `This` on trait `Baz` --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:42 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `ItemContext` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:49 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:48 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^^^^^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^^^^^^^ | = help: expect either a generic argument name or `{Self}` as format argument warning: there is no parameter `This` on trait `Baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:62 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:61 | -LL | label = "{float}{_Self}{crate_local}{Trait}{ItemContext}{This}" - | ^^^^ +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ + | + = help: expect either a generic argument name or `{Self}` as format argument + +warning: there is no parameter `This` on trait `Baz` + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:72 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^ | = help: expect either a generic argument name or `{Self}` as format argument @@ -152,8 +160,20 @@ LL | #[diagnostic::on_unimplemented(message = "Not allowed to apply it on a impl | = note: `#[warn(misplaced_diagnostic_attributes)]` (part of `#[warn(unknown_or_malformed_diagnostic_attributes)]`) on by default +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:65 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^ remove this format specifier + +warning: format specifiers are not permitted in diagnostic attributes + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:38:76 + | +LL | label = "{float}{_Self}{crate_local}{This}{ItemContext}{This:path}{This:resolved}" + | ^^^^^^^^^ remove this format specifier + error[E0277]: trait has `()` and `i32` as params - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:56:15 | LL | takes_foo(()); | --------- ^^ trait has `()` and `i32` as params @@ -168,13 +188,13 @@ help: this trait has no implementations, consider adding one LL | trait Foo {} | ^^^^^^^^^^^^ note: required by a bound in `takes_foo` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:48:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:51:22 | LL | fn takes_foo(_: impl Foo) {} | ^^^^^^^^ required by this bound in `takes_foo` error[E0277]: the trait bound `(): Bar` is not satisfied - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:55:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:58:15 | LL | takes_bar(()); | --------- ^^ the trait `Bar` is not implemented for `()` @@ -187,31 +207,31 @@ help: the trait `Bar` is implemented for `i32` LL | impl Bar for i32 {} | ^^^^^^^^^^^^^^^^ note: required by a bound in `takes_bar` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:52:22 | LL | fn takes_bar(_: impl Bar) {} | ^^^ required by this bound in `takes_bar` error[E0277]: {from_desugaring}{direct}{cause}{integral}{integer}{struct}{enum}{union} - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:57:15 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:60:15 | LL | takes_baz(()); - | --------- ^^ {float}{_Self}{crate_local}{Trait}{ItemContext}{This} + | --------- ^^ {float}{_Self}{crate_local}{This}{ItemContext}{This}{This} | | | required by a bound introduced by this call | = help: the trait `Baz` is not implemented for `()` help: this trait has no implementations, consider adding one - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:46:1 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:49:1 | LL | trait Baz {} | ^^^^^^^^^ note: required by a bound in `takes_baz` - --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:50:22 + --> $DIR/do_not_accept_options_of_the_internal_rustc_attribute.rs:53:22 | LL | fn takes_baz(_: impl Baz) {} | ^^^ required by this bound in `takes_baz` -error: aborting due to 3 previous errors; 19 warnings emitted +error: aborting due to 3 previous errors; 22 warnings emitted For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/on-unimplemented/format_specs.rs b/tests/ui/on-unimplemented/format_specs.rs new file mode 100644 index 0000000000000..f92674ab4a888 --- /dev/null +++ b/tests/ui/on-unimplemented/format_specs.rs @@ -0,0 +1,13 @@ +#![feature(rustc_attrs)] + +#[rustc_on_unimplemented( + message = "normal: {This}, path: {This:path}, resolved: {This:resolved}" +)] +pub trait Trait<'lifetime, const CONST_GENERIC: usize, A, B> where A: Send {} + +fn take_trait<'a, T: Trait<'a, 6, u8, U>, U>(_: T) {} + +fn main() { + take_trait(()); + //~^ERROR normal: Trait, path: Trait<'lifetime, CONST_GENERIC, A, B>, resolved: Trait<'_, 6, u8, _> +} diff --git a/tests/ui/on-unimplemented/format_specs.stderr b/tests/ui/on-unimplemented/format_specs.stderr new file mode 100644 index 0000000000000..3163323f34026 --- /dev/null +++ b/tests/ui/on-unimplemented/format_specs.stderr @@ -0,0 +1,22 @@ +error[E0277]: normal: Trait, path: Trait<'lifetime, CONST_GENERIC, A, B>, resolved: Trait<'_, 6, u8, _> + --> $DIR/format_specs.rs:11:16 + | +LL | take_trait(()); + | ---------- ^^ the trait `Trait<'_, 6, u8, _>` is not implemented for `()` + | | + | required by a bound introduced by this call + | +help: this trait has no implementations, consider adding one + --> $DIR/format_specs.rs:6:1 + | +LL | pub trait Trait<'lifetime, const CONST_GENERIC: usize, A, B> where A: Send {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +note: required by a bound in `take_trait` + --> $DIR/format_specs.rs:8:22 + | +LL | fn take_trait<'a, T: Trait<'a, 6, u8, U>, U>(_: T) {} + | ^^^^^^^^^^^^^^^^^^^ required by this bound in `take_trait` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 378a8c3a34cdeabe20f3a427c7d5268e16ec4589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Miku=C5=82a?= Date: Wed, 6 May 2026 11:48:59 +0200 Subject: [PATCH 004/174] Install additional LLVM DLL on Windows --- src/bootstrap/src/core/build_steps/dist.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs index 222b982073280..67cd58d72eb74 100644 --- a/src/bootstrap/src/core/build_steps/dist.rs +++ b/src/bootstrap/src/core/build_steps/dist.rs @@ -2569,6 +2569,12 @@ pub fn maybe_install_llvm_runtime(builder: &Builder<'_>, target: TargetSelection // statically. if builder.llvm_link_shared() { maybe_install_llvm(builder, target, &dst_libdir, false); + + // To workaround lack of rpath on Windows, we bundle another copy of + // the LLVM DLL to make rust-lld and llvm-tools work when `sysroot/bin` + // is missing from PATH, i.e. when they not launched by rustc. + let dst_libdir = sysroot.join("lib/rustlib").join(target).join("bin"); + maybe_install_llvm(builder, target, &dst_libdir, false); } } From 63d1985d38eb5bb135423514ef54e3052533172a Mon Sep 17 00:00:00 2001 From: Wesley Wiser Date: Tue, 27 Jan 2026 20:52:54 -0600 Subject: [PATCH 005/174] Allow forbidden target features to be hard errors --- compiler/rustc_codegen_ssa/src/errors.rs | 11 ++-- .../rustc_codegen_ssa/src/target_features.rs | 13 +++- compiler/rustc_target/src/target_features.rs | 62 +++++++++++++++---- 3 files changed, 66 insertions(+), 20 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/errors.rs b/compiler/rustc_codegen_ssa/src/errors.rs index cceda09c701cc..97884c202d302 100644 --- a/compiler/rustc_codegen_ssa/src/errors.rs +++ b/compiler/rustc_codegen_ssa/src/errors.rs @@ -1220,14 +1220,17 @@ pub(crate) struct UnstableCTargetFeature<'a> { #[derive(Diagnostic)] #[diag("target feature `{$feature}` cannot be {$enabled} with `-Ctarget-feature`: {$reason}")] -#[note( - "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 #116344 ")] pub(crate) struct ForbiddenCTargetFeature<'a> { pub feature: &'a str, pub enabled: &'a str, pub reason: &'a str, + #[note( + "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 #116344 " + )] + pub future_compat_note: bool, } pub(crate) struct TargetFeatureDisableOrEnable<'a> { diff --git a/compiler/rustc_codegen_ssa/src/target_features.rs b/compiler/rustc_codegen_ssa/src/target_features.rs index 2572d21608213..1310005437e77 100644 --- a/compiler/rustc_codegen_ssa/src/target_features.rs +++ b/compiler/rustc_codegen_ssa/src/target_features.rs @@ -308,12 +308,19 @@ pub fn cfg_target_feature<'a, const N: usize>( sess.dcx().emit_warn(unknown_feature); } Some((_, stability, _)) => { - if let Err(reason) = stability.toggle_allowed() { - sess.dcx().emit_warn(errors::ForbiddenCTargetFeature { + if let Stability::Forbidden { reason, hard_error } = stability { + let diag = errors::ForbiddenCTargetFeature { feature: base_feature, enabled: if enable { "enabled" } else { "disabled" }, reason, - }); + future_compat_note: !hard_error, + }; + + if *hard_error { + sess.dcx().emit_err(diag); + } else { + sess.dcx().emit_warn(diag); + } } else if stability.requires_nightly(/* in_cfg */ false).is_some() { // An unstable feature. Warn about using it. It makes little sense // to hard-error here since we just warn about fully unknown diff --git a/compiler/rustc_target/src/target_features.rs b/compiler/rustc_target/src/target_features.rs index e2bf1c48b7b47..b009c42eb2302 100644 --- a/compiler/rustc_target/src/target_features.rs +++ b/compiler/rustc_target/src/target_features.rs @@ -35,7 +35,12 @@ pub enum Stability { /// set in the target spec. It is never set in `cfg(target_feature)`. Used in /// particular for features are actually ABI configuration flags (not all targets are as nice as /// RISC-V and have an explicit way to set the ABI separate from target features). - Forbidden { reason: &'static str }, + Forbidden { + reason: &'static str, + /// True if this is always an error, false if this can be reported as a warning when set via + /// `-Ctarget-feature`. + hard_error: bool, + }, } use Stability::*; @@ -85,14 +90,14 @@ impl Stability { } /// Returns whether the feature may be toggled via `#[target_feature]` or `-Ctarget-feature`. - /// (It might still be nightly-only even if this returns `true`, so make sure to also check + /// (It might still be nightly-only even if this returns `Ok(())`, so make sure to also check /// `requires_nightly`.) pub fn toggle_allowed(&self) -> Result<(), &'static str> { match self { Stability::Unstable(_) | Stability::CfgStableToggleUnstable(_) | Stability::Stable { .. } => Ok(()), - Stability::Forbidden { reason } => Err(reason), + Stability::Forbidden { reason, hard_error: _ } => Err(reason), } } } @@ -149,7 +154,10 @@ static ARM_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("aes", Unstable(sym::arm_target_feature), &["neon"]), ( "atomics-32", - Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + Stability::Forbidden { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, &[], ), ("crc", Unstable(sym::arm_target_feature), &[]), @@ -225,7 +233,11 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ // FEAT_FLAGM2 ("flagm2", Unstable(sym::aarch64_unstable_target_feature), &[]), // We forbid directly toggling just `fp-armv8`; it must be toggled with `neon`. - ("fp-armv8", Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`" }, &[]), + ( + "fp-armv8", + Stability::Forbidden { reason: "Rust ties `fp-armv8` to `neon`", hard_error: false }, + &[], + ), // FEAT_FP8 ("fp8", Unstable(sym::aarch64_unstable_target_feature), &["faminmax", "lut", "bf16"]), // FEAT_FP8DOT2 @@ -288,7 +300,11 @@ static AARCH64_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rcpc3", Unstable(sym::aarch64_unstable_target_feature), &["rcpc2"]), // FEAT_RDM ("rdm", Stable, &["neon"]), - ("reserve-x18", Forbidden { reason: "use `-Zfixed-x18` compiler flag instead" }, &[]), + ( + "reserve-x18", + Forbidden { reason: "use `-Zfixed-x18` compiler flag instead", hard_error: false }, + &[], + ), // FEAT_SB ("sb", Stable, &[]), // FEAT_SHA1 & FEAT_SHA256 @@ -467,17 +483,26 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rdseed", Stable, &[]), ( "retpoline-external-thunk", - Stability::Forbidden { reason: "use `-Zretpoline-external-thunk` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline-external-thunk` compiler flag instead", + hard_error: false, + }, &[], ), ( "retpoline-indirect-branches", - Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, &[], ), ( "retpoline-indirect-calls", - Stability::Forbidden { reason: "use `-Zretpoline` compiler flag instead" }, + Stability::Forbidden { + reason: "use `-Zretpoline` compiler flag instead", + hard_error: false, + }, &[], ), ("rtm", Unstable(sym::rtm_target_feature), &[]), @@ -485,7 +510,11 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("sha512", Stable, &["avx2"]), ("sm3", Stable, &["avx"]), ("sm4", Stable, &["avx2"]), - ("soft-float", Stability::Forbidden { reason: "use a soft-float target instead" }, &[]), + ( + "soft-float", + Stability::Forbidden { reason: "use a soft-float target instead", hard_error: false }, + &[], + ), ("sse", Stable, &[]), ("sse2", Stable, &["sse"]), ("sse3", Stable, &["sse2"]), @@ -615,7 +644,10 @@ static RISCV_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("f", Unstable(sym::riscv_target_feature), &["zicsr"]), ( "forced-atomics", - Stability::Forbidden { reason: "unsound because it changes the ABI of atomic operations" }, + Stability::Forbidden { + reason: "unsound because it changes the ABI of atomic operations", + hard_error: false, + }, &[], ), ("m", Stable, &[]), @@ -872,7 +904,7 @@ const IBMZ_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("miscellaneous-extensions-3", Stable, &[]), ("miscellaneous-extensions-4", Stable, &[]), ("nnp-assist", Stable, &["vector"]), - ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature" }, &[]), + ("soft-float", Forbidden { reason: "unsupported ABI-configuration feature", hard_error: false }, &[]), ("transactional-execution", Unstable(sym::s390x_target_feature), &[]), ("vector", Stable, &[]), ("vector-enhancements-1", Stable, &["vector"]), @@ -924,7 +956,11 @@ static AVR_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[ ("rmw", Unstable(sym::avr_target_feature), &[]), ("spm", Unstable(sym::avr_target_feature), &[]), ("spmx", Unstable(sym::avr_target_feature), &[]), - ("sram", Forbidden { reason: "devices that have no SRAM are unsupported" }, &[]), + ( + "sram", + Forbidden { reason: "devices that have no SRAM are unsupported", hard_error: false }, + &[], + ), ("tinyencoding", Unstable(sym::avr_target_feature), &[]), // tidy-alphabetical-end ]; From c6d2da90b5bc06df958541c9909870672d08377e Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:26:11 +0100 Subject: [PATCH 006/174] Remove #[inline(always)] + #[target_feature(enable = "")] support, reinstating error message and providing a link to the tracking issue for the full rationale. --- compiler/rustc_codegen_llvm/src/attributes.rs | 19 ++-- compiler/rustc_codegen_llvm/src/builder.rs | 28 +----- .../rustc_codegen_ssa/src/codegen_attrs.rs | 16 ++-- compiler/rustc_feature/src/removed.rs | 3 + compiler/rustc_feature/src/unstable.rs | 2 - compiler/rustc_lint/src/lib.rs | 4 + compiler/rustc_lint_defs/src/builtin.rs | 56 ------------ .../check_inline_always_target_features.rs | 88 ------------------- compiler/rustc_mir_transform/src/errors.rs | 41 --------- compiler/rustc_mir_transform/src/lib.rs | 4 - 10 files changed, 24 insertions(+), 237 deletions(-) delete mode 100644 compiler/rustc_mir_transform/src/check_inline_always_target_features.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 1f59d250e08a0..0ea4bd60d69a9 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -41,7 +41,8 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { } /// Get LLVM attribute for the provided inline heuristic. -pub(crate) fn inline_attr<'ll, 'tcx>( +#[inline] +fn inline_attr<'ll, 'tcx>( cx: &SimpleCx<'ll>, tcx: TyCtxt<'tcx>, instance: ty::Instance<'tcx>, @@ -418,6 +419,12 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } + if let Some(instance) = instance + && let Some(inline_attr) = inline_attr(cx, tcx, instance) + { + to_add.push(inline_attr); + } + if sess.must_emit_unwind_tables() { to_add.push(uwtable_attr(cx.llcx, sess.opts.unstable_opts.use_sync_unwind)); } @@ -568,16 +575,6 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( let function_features = codegen_fn_attrs.target_features.iter().map(|f| f.name.as_str()).collect::>(); - // Apply function attributes as per usual if there are no user defined - // target features otherwise this will get applied at the callsite. - if function_features.is_empty() { - if let Some(instance) = instance - && let Some(inline_attr) = inline_attr(cx, tcx, instance) - { - to_add.push(inline_attr); - } - } - let function_features = function_features .iter() // Convert to LLVMFeatures and filter out unavailable ones diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 7e5f71209fbab..044011c4ea982 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -15,7 +15,7 @@ use rustc_codegen_ssa::mir::place::PlaceRef; use rustc_codegen_ssa::traits::*; use rustc_data_structures::small_c_str::SmallCStr; use rustc_hir::def_id::DefId; -use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrs, TargetFeature, TargetFeatureKind}; +use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrs; use rustc_middle::ty::layout::{ FnAbiError, FnAbiOfHelpers, FnAbiRequest, HasTypingEnv, LayoutError, LayoutOfHelpers, TyAndLayout, @@ -1419,32 +1419,6 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) }; - if let Some(callee_instance) = callee_instance { - // Attributes on the function definition being called - let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id()); - if let Some(caller_attrs) = caller_attrs - // If there is an inline attribute and a target feature that matches - // we will add the attribute to the callsite otherwise we'll omit - // this and not add the attribute to prevent soundness issues. - && let Some(inlining_rule) = attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance) - && self.cx.tcx.is_target_feature_call_safe( - &callee_attrs.target_features, - &caller_attrs.target_features.iter().cloned().chain( - self.cx.tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - }) - ).collect::>(), - ) - { - attributes::apply_to_callsite( - call, - llvm::AttributePlace::Function, - &[inlining_rule], - ); - } - } - if let Some(fn_abi) = fn_abi { fn_abi.apply_attrs_callsite(self, call); } diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 1e1be9d3a544f..2895b4718d7f3 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -419,16 +419,16 @@ fn check_result( // llvm/llvm-project#70563). if !codegen_fn_attrs.target_features.is_empty() && matches!(codegen_fn_attrs.inline, InlineAttr::Always) - && !tcx.features().target_feature_inline_always() && let Some(span) = interesting_spans.inline { - feature_err( - tcx.sess, - sym::target_feature_inline_always, - span, - "cannot use `#[inline(always)]` with `#[target_feature]`", - ) - .emit(); + let mut diag = tcx + .dcx() + .struct_span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); + diag.note( + "See issue here for full discussion; \ + https://github.com/rust-lang/rust/issues/145574", + ); + diag.emit(); } // warn that inline has no effect when no_sanitize is present diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index 7508fb7c250c7..dcfd8b71dde43 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -282,6 +282,9 @@ declare_features! ( /// Allows string patterns to dereference values to match them. (removed, string_deref_patterns, "1.94.0", Some(87121), Some("superseded by `deref_patterns`"), 150530), (removed, struct_inherit, "1.0.0", None, None), + /// Allows the use of target_feature when a function is marked inline(always). + (removed, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574), + Some("removed because `#[inline(always)]` with `#[target_feature]` is forbidden")), (removed, test_removed_feature, "1.0.0", None, None), /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None), diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 5af522486b1f6..41b7a28626f6b 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -725,8 +725,6 @@ declare_features! ( (unstable, super_let, "1.88.0", Some(139076)), /// Allows subtrait items to shadow supertrait items. (unstable, supertrait_item_shadowing, "1.86.0", Some(89151)), - /// Allows the use of target_feature when a function is marked inline(always). - (unstable, target_feature_inline_always, "1.91.0", Some(145574)), /// Allows using `#[thread_local]` on `static` items. (unstable, thread_local, "1.0.0", Some(29594)), /// Allows defining `trait X = A + B;` alias items. diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index ea0e657f7edef..b9332845edab4 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -398,6 +398,10 @@ fn register_builtins(store: &mut LintStore) { store.register_removed("rustdoc", "use `rustdoc::all` instead"); store.register_removed("unknown_features", "replaced by an error"); + store.register_removed( + "inline_always_mismatching_target_features", + "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", + ); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 4858e65992cd3..c367d6fee2b98 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -55,7 +55,6 @@ declare_lint_pass! { ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, INEFFECTIVE_UNSTABLE_TRAIT_IMPL, - INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, INLINE_NO_SANITIZE, INVALID_DOC_ATTRIBUTES, INVALID_MACRO_EXPORT_ARGUMENTS, @@ -5460,61 +5459,6 @@ declare_lint! { "detects tail calls of functions marked with `#[track_caller]`", @feature_gate = explicit_tail_calls; } -declare_lint! { - /// The `inline_always_mismatching_target_features` lint will trigger when a - /// function with the `#[inline(always)]` and `#[target_feature(enable = "...")]` - /// attributes is called and cannot be inlined due to missing target features in the caller. - /// - /// ### Example - /// - /// ```rust,ignore (fails on x86_64) - /// #[inline(always)] - /// #[target_feature(enable = "fp16")] - /// unsafe fn callee() { - /// // operations using fp16 types - /// } - /// - /// // Caller does not enable the required target feature - /// fn caller() { - /// unsafe { callee(); } - /// } - /// - /// fn main() { - /// caller(); - /// } - /// ``` - /// - /// This will produce: - /// - /// ```text - /// warning: call to `#[inline(always)]`-annotated `callee` requires the same target features. Function will not have `alwaysinline` attribute applied - /// --> $DIR/builtin.rs:5192:14 - /// | - /// 10 | unsafe { callee(); } - /// | ^^^^^^^^ - /// | - /// note: `fp16` target feature enabled in `callee` here but missing from `caller` - /// --> $DIR/builtin.rs:5185:1 - /// | - /// 3 | #[target_feature(enable = "fp16")] - /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - /// 4 | unsafe fn callee() { - /// | ------------------ - /// = note: `#[warn(inline_always_mismatching_target_features)]` on by default - /// warning: 1 warning emitted - /// ``` - /// - /// ### Explanation - /// - /// Inlining a function with a target feature attribute into a caller that - /// lacks the corresponding target feature can lead to unsound behavior. - /// LLVM may select the wrong instructions or registers, or reorder - /// operations, potentially resulting in runtime errors. - pub INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - Warn, - r#"detects when a function annotated with `#[inline(always)]` and `#[target_feature(enable = "..")]` is inlined into a caller without the required target feature"#, -} - declare_lint! { /// The `repr_c_enums_larger_than_int` lint detects `repr(C)` enums with discriminant /// values that do not fit into a C `int` or `unsigned int`. diff --git a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs b/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs deleted file mode 100644 index abad28f0a8f83..0000000000000 --- a/compiler/rustc_mir_transform/src/check_inline_always_target_features.rs +++ /dev/null @@ -1,88 +0,0 @@ -use rustc_hir::attrs::InlineAttr; -use rustc_middle::middle::codegen_fn_attrs::{TargetFeature, TargetFeatureKind}; -use rustc_middle::mir::{Body, TerminatorKind}; -use rustc_middle::ty::{self, TyCtxt}; - -use crate::pass_manager::MirLint; - -pub(super) struct CheckInlineAlwaysTargetFeature; - -impl<'tcx> MirLint<'tcx> for CheckInlineAlwaysTargetFeature { - fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - check_inline_always_target_features(tcx, body) - } -} - -/// `#[target_feature]`-annotated functions can be marked `#[inline]` and will only be inlined if -/// the target features match (as well as all of the other inlining heuristics). `#[inline(always)]` -/// will always inline regardless of matching target features, which can result in errors from LLVM. -/// However, it is desirable to be able to always annotate certain functions (e.g. SIMD intrinsics) -/// as `#[inline(always)]` but check the target features match in Rust to avoid the LLVM errors. -/// -/// We check the caller and callee target features to ensure that this can -/// be done or emit a lint. -#[inline] -fn check_inline_always_target_features<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) { - let caller_def_id = body.source.def_id().expect_local(); - if !tcx.def_kind(caller_def_id).has_codegen_attrs() { - return; - } - - let caller_codegen_fn_attrs = tcx.codegen_fn_attrs(caller_def_id); - - for bb in body.basic_blocks.iter() { - let terminator = bb.terminator(); - match &terminator.kind { - TerminatorKind::Call { func, .. } | TerminatorKind::TailCall { func, .. } => { - let fn_ty = func.ty(body, tcx); - let ty::FnDef(callee_def_id, _) = *fn_ty.kind() else { - continue; - }; - - if !tcx.def_kind(callee_def_id).has_codegen_attrs() { - continue; - } - let callee_codegen_fn_attrs = tcx.codegen_fn_attrs(callee_def_id); - if callee_codegen_fn_attrs.inline != InlineAttr::Always - || callee_codegen_fn_attrs.target_features.is_empty() - { - continue; - } - - // Scan the users defined target features and ensure they - // match the caller. - if tcx.is_target_feature_call_safe( - &callee_codegen_fn_attrs.target_features, - &caller_codegen_fn_attrs - .target_features - .iter() - .cloned() - .chain(tcx.sess.target_features.iter().map(|feat| TargetFeature { - name: *feat, - kind: TargetFeatureKind::Implied, - })) - .collect::>(), - ) { - continue; - } - - let callee_only: Vec<_> = callee_codegen_fn_attrs - .target_features - .iter() - .filter(|it| !caller_codegen_fn_attrs.target_features.contains(it)) - .filter(|it| !matches!(it.kind, TargetFeatureKind::Implied)) - .map(|it| it.name.as_str()) - .collect(); - - crate::errors::emit_inline_always_target_feature_diagnostic( - tcx, - terminator.source_info.span, - callee_def_id, - caller_def_id.into(), - &callee_only, - ); - } - _ => (), - } - } -} diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 39c85489f939a..523d7c9eeeda4 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -5,52 +5,11 @@ use rustc_errors::{ }; use rustc_macros::{Diagnostic, Subdiagnostic}; use rustc_middle::mir::AssertKind; -use rustc_middle::query::QueryKey; use rustc_middle::ty::TyCtxt; use rustc_session::lint::{self, Lint}; use rustc_span::def_id::DefId; use rustc_span::{Ident, Span, Symbol}; -/// Emit diagnostic for calls to `#[inline(always)]`-annotated functions with a -/// `#[target_feature]` attribute where the caller enables a different set of target features. -pub(crate) fn emit_inline_always_target_feature_diagnostic<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - call_span: Span, - callee_def_id: DefId, - caller_def_id: DefId, - callee_only: &[&'a str], -) { - tcx.emit_node_span_lint( - lint::builtin::INLINE_ALWAYS_MISMATCHING_TARGET_FEATURES, - tcx.local_def_id_to_hir_id(caller_def_id.as_local().unwrap()), - call_span, - rustc_errors::DiagDecorator(|lint| { - let callee = tcx.def_path_str(callee_def_id); - let caller = tcx.def_path_str(caller_def_id); - - lint.primary_message(format!( - "call to `#[inline(always)]`-annotated `{callee}` \ - requires the same target features to be inlined" - )); - lint.note("function will not be inlined"); - - lint.note(format!( - "the following target features are on `{callee}` but missing from `{caller}`: {}", - callee_only.join(", ") - )); - lint.span_note(callee_def_id.default_span(tcx), format!("`{callee}` is defined here")); - - let feats = callee_only.join(","); - lint.span_suggestion( - tcx.def_span(caller_def_id).shrink_to_lo(), - format!("add `#[target_feature]` attribute to `{caller}`"), - format!("#[target_feature(enable = \"{feats}\")]\n"), - lint::Applicability::MaybeIncorrect, - ); - }), - ); -} - #[derive(Diagnostic)] #[diag("function cannot return without recursing")] #[help("a `loop` may express intention better if this is on purpose")] diff --git a/compiler/rustc_mir_transform/src/lib.rs b/compiler/rustc_mir_transform/src/lib.rs index 91dfffcf1a6a5..db8f77ae929ac 100644 --- a/compiler/rustc_mir_transform/src/lib.rs +++ b/compiler/rustc_mir_transform/src/lib.rs @@ -125,7 +125,6 @@ declare_passes! { mod add_subtyping_projections : Subtyper; mod check_inline : CheckForceInline; mod check_call_recursion : CheckCallRecursion, CheckDropRecursion; - mod check_inline_always_target_features: CheckInlineAlwaysTargetFeature; mod check_alignment : CheckAlignment; mod check_enums : CheckEnums; mod check_const_item_mutation : CheckConstItemMutation; @@ -407,9 +406,6 @@ fn mir_built(tcx: TyCtxt<'_>, def: LocalDefId) -> &Steal> { // MIR-level lints. &Lint(check_inline::CheckForceInline), &Lint(check_call_recursion::CheckCallRecursion), - // Check callee's target features match callers target features when - // using `#[inline(always)]` - &Lint(check_inline_always_target_features::CheckInlineAlwaysTargetFeature), &Lint(check_packed_ref::CheckPackedRef), &Lint(check_const_item_mutation::CheckConstItemMutation), &Lint(function_item_references::FunctionItemReferences), From 52cc7ecf67f88a4887256bdb5ca885bd43b0bf1a Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:26:51 +0100 Subject: [PATCH 007/174] Remove `target_feature_inline_always` from core, std and stdarch `lib.rs`'s --- library/core/src/lib.rs | 1 - library/std/src/lib.rs | 1 - 2 files changed, 2 deletions(-) diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index 57ce51bb8c0ed..34a5ef66fdf4c 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -167,7 +167,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(trait_alias)] #![feature(transparent_unions)] #![feature(try_blocks)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index bb280f698b852..f59154a8913fa 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -303,7 +303,6 @@ #![feature(staged_api)] #![feature(stmt_expr_attributes)] #![feature(strict_provenance_lints)] -#![feature(target_feature_inline_always)] #![feature(thread_local)] #![feature(try_blocks)] #![feature(try_trait_v2)] From 311cda6e1f22bf5e8db2a92eff789ca1ad1ffd4f Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 7 May 2026 08:27:49 +0100 Subject: [PATCH 008/174] Update tests to reflect changes, deleted ones that are now irrelevant --- tests/codegen-llvm/inline-always-callsite.rs | 41 ------------- ...ature-gate-target-feature-inline-always.rs | 9 --- ...e-gate-target-feature-inline-always.stderr | 13 ---- .../inline-always.aarch64.stderr | 60 ------------------- tests/ui/target-feature/inline-always.rs | 55 ----------------- tests/ui/target-feature/invalid-attribute.rs | 3 +- .../target-feature/invalid-attribute.stderr | 32 +++++----- 7 files changed, 16 insertions(+), 197 deletions(-) delete mode 100644 tests/codegen-llvm/inline-always-callsite.rs delete mode 100644 tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs delete mode 100644 tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr delete mode 100644 tests/ui/target-feature/inline-always.aarch64.stderr delete mode 100644 tests/ui/target-feature/inline-always.rs diff --git a/tests/codegen-llvm/inline-always-callsite.rs b/tests/codegen-llvm/inline-always-callsite.rs deleted file mode 100644 index ad133391ce267..0000000000000 --- a/tests/codegen-llvm/inline-always-callsite.rs +++ /dev/null @@ -1,41 +0,0 @@ -//@ add-minicore -//@ compile-flags: --target aarch64-unknown-linux-gnu -Zinline-mir=no -C no-prepopulate-passes -//@ needs-llvm-components: aarch64 - -#![crate_type = "lib"] -#![feature(no_core, lang_items, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon")] -#[no_mangle] -pub fn single_target_feature() -> i32 { - 42 -} - -#[inline(always)] -#[target_feature(enable = "neon,i8mm")] -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @multiple_target_features() unnamed_addr #1 { -pub fn multiple_target_features() -> i32 { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() -} - -#[no_mangle] -// CHECK: define{{( noundef)?}} i32 @inherits_from_global() unnamed_addr #2 { -pub fn inherits_from_global() -> i32 { - unsafe { - // CHECK: %_0 = call{{( noundef)?}} i32 @single_target_feature() #3 - single_target_feature() - } -} - -// Attribute #3 requires the alwaysinline attribute, the alwaysinline attribute is not emitted on a -// function definition when target features are present, rather it will be moved onto the function -// call, if the features match up. -// -// CHECK: attributes #3 = { alwaysinline nounwind } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs deleted file mode 100644 index 181f9a210003f..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ only-aarch64 -#[inline(always)] -//~^ ERROR cannot use `#[inline(always)]` with `#[target_feature]` -#[target_feature(enable="fp16")] -fn test() { - -} - -fn main() { } diff --git a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr b/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr deleted file mode 100644 index de54844bc291d..0000000000000 --- a/tests/ui/feature-gates/feature-gate-target-feature-inline-always.stderr +++ /dev/null @@ -1,13 +0,0 @@ -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` - --> $DIR/feature-gate-target-feature-inline-always.rs:2:1 - | -LL | #[inline(always)] - | ^^^^^^^^^^^^^^^^^ - | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/target-feature/inline-always.aarch64.stderr b/tests/ui/target-feature/inline-always.aarch64.stderr deleted file mode 100644 index 8b58923f2170f..0000000000000 --- a/tests/ui/target-feature/inline-always.aarch64.stderr +++ /dev/null @@ -1,60 +0,0 @@ -warning: call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined - --> $DIR/inline-always.rs:20:5 - | -LL | target_feature_identity(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `target_feature_identity` but missing from `call_no_target_features`: neon, fp16 -note: `target_feature_identity` is defined here - --> $DIR/inline-always.rs:17:1 - | -LL | pub unsafe fn target_feature_identity() {} - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - = note: `#[warn(inline_always_mismatching_target_features)]` on by default -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "neon,fp16")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:23:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_no_target_features`: fp16, sve, rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_no_target_features` - | -LL + #[target_feature(enable = "fp16,sve,rdm")] -LL | unsafe fn call_no_target_features() { - | - -warning: call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined - --> $DIR/inline-always.rs:29:5 - | -LL | multiple_target_features(); - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: function will not be inlined - = note: the following target features are on `multiple_target_features` but missing from `call_to_first_set`: rdm -note: `multiple_target_features` is defined here - --> $DIR/inline-always.rs:53:1 - | -LL | fn multiple_target_features() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -help: add `#[target_feature]` attribute to `call_to_first_set` - | -LL + #[target_feature(enable = "rdm")] -LL | unsafe fn call_to_first_set() { - | - -warning: 3 warnings emitted - diff --git a/tests/ui/target-feature/inline-always.rs b/tests/ui/target-feature/inline-always.rs deleted file mode 100644 index c1334bb6016b2..0000000000000 --- a/tests/ui/target-feature/inline-always.rs +++ /dev/null @@ -1,55 +0,0 @@ -//@ add-minicore -//@ build-pass -//@ compile-flags: --crate-type=lib -//@ revisions: aarch64 -//@[aarch64] compile-flags: --target aarch64-unknown-linux-gnu -//@[aarch64] needs-llvm-components: aarch64 -//@ ignore-backends: gcc - -#![feature(no_core, target_feature_inline_always)] -#![no_core] - -extern crate minicore; -use minicore::*; - -#[inline(always)] -#[target_feature(enable = "neon,fp16")] -pub unsafe fn target_feature_identity() {} - -unsafe fn call_no_target_features() { - target_feature_identity(); - //~^ WARNING call to `#[inline(always)]`-annotated `target_feature_identity` requires the same target features to be inlined [inline_always_mismatching_target_features] - global_feature_enabled(); - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -#[target_feature(enable = "fp16,sve")] -unsafe fn call_to_first_set() { - multiple_target_features(); - //~^ WARNING call to `#[inline(always)]`-annotated `multiple_target_features` requires the same target features to be inlined [inline_always_mismatching_target_features] -} - -/* You can't have "fhm" without "fp16" */ -#[target_feature(enable = "fhm")] -unsafe fn mismatching_features() { - target_feature_identity() -} - -#[target_feature(enable = "fp16")] -unsafe fn matching_target_features() { - target_feature_identity() -} - -#[inline(always)] -#[target_feature(enable = "neon")] -unsafe fn global_feature_enabled() { - -} - -#[inline(always)] -#[target_feature(enable = "fp16,sve")] -#[target_feature(enable="rdm")] -fn multiple_target_features() { - - } diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index 0c380d69b1433..ee1e54ac239c0 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -67,8 +67,7 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` -//~| NOTE: see issue #145574 for more information -//~| NOTE: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +//~| NOTE: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index 6de12edb037fe..a4248f5ced9e0 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -121,7 +121,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on statics - --> $DIR/invalid-attribute.rs:75:1 + --> $DIR/invalid-attribute.rs:74:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -129,7 +129,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on trait impl blocks - --> $DIR/invalid-attribute.rs:79:1 + --> $DIR/invalid-attribute.rs:78:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -137,7 +137,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on inherent impl blocks - --> $DIR/invalid-attribute.rs:85:1 + --> $DIR/invalid-attribute.rs:84:1 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -145,7 +145,7 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on expressions - --> $DIR/invalid-attribute.rs:106:5 + --> $DIR/invalid-attribute.rs:105:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -153,22 +153,20 @@ LL | #[target_feature(enable = "sse2")] = help: `#[target_feature]` can only be applied to functions error: `#[target_feature]` attribute cannot be used on closures - --> $DIR/invalid-attribute.rs:112:5 + --> $DIR/invalid-attribute.rs:111:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: `#[target_feature]` can be applied to functions and methods -error[E0658]: cannot use `#[inline(always)]` with `#[target_feature]` +error: cannot use `#[inline(always)]` with `#[target_feature]` --> $DIR/invalid-attribute.rs:68:1 | LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ | - = note: see issue #145574 for more information - = help: add `#![feature(target_feature_inline_always)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = note: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:26:18 @@ -179,7 +177,7 @@ LL | #[target_feature(enable = "foo")] = help: valid names are: `fma`, `xop`, `adx`, `aes`, and `avx` and 75 more error[E0046]: not all trait items implemented, missing: `foo` - --> $DIR/invalid-attribute.rs:81:1 + --> $DIR/invalid-attribute.rs:80:1 | LL | impl Quux for u8 {} | ^^^^^^^^^^^^^^^^ missing `foo` in implementation @@ -188,7 +186,7 @@ LL | fn foo(); | --------- `foo` from trait error: `#[target_feature(..)]` cannot be applied to safe trait method - --> $DIR/invalid-attribute.rs:95:5 + --> $DIR/invalid-attribute.rs:94:5 | LL | #[target_feature(enable = "sse2")] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot be applied to safe trait method @@ -197,13 +195,13 @@ LL | fn foo() {} | -------- not an `unsafe` function error[E0053]: method `foo` has an incompatible type for trait - --> $DIR/invalid-attribute.rs:98:5 + --> $DIR/invalid-attribute.rs:97:5 | LL | fn foo() {} | ^^^^^^^^ expected safe fn, found unsafe fn | note: type in trait - --> $DIR/invalid-attribute.rs:90:5 + --> $DIR/invalid-attribute.rs:89:5 | LL | fn foo(); | ^^^^^^^^^ @@ -211,7 +209,7 @@ LL | fn foo(); found signature `#[target_features] fn()` error: the feature named `+sse2` is not valid for this target - --> $DIR/invalid-attribute.rs:117:18 + --> $DIR/invalid-attribute.rs:116:18 | LL | #[target_feature(enable = "+sse2")] | ^^^^^^^^^^^^^^^^ `+sse2` is not valid for this target @@ -223,7 +221,7 @@ LL + #[target_feature(enable = "sse2")] | error: the feature named `sse5` is not valid for this target - --> $DIR/invalid-attribute.rs:122:18 + --> $DIR/invalid-attribute.rs:121:18 | LL | #[target_feature(enable = "sse5")] | ^^^^^^^^^^^^^^^ `sse5` is not valid for this target @@ -231,7 +229,7 @@ LL | #[target_feature(enable = "sse5")] = help: valid names are: `sse`, `sse2`, `sse3`, `sse4a`, and `ssse3` and 75 more error: the feature named `avx512` is not valid for this target - --> $DIR/invalid-attribute.rs:127:18 + --> $DIR/invalid-attribute.rs:126:18 | LL | #[target_feature(enable = "avx512")] | ^^^^^^^^^^^^^^^^^ `avx512` is not valid for this target @@ -240,5 +238,5 @@ LL | #[target_feature(enable = "avx512")] error: aborting due to 26 previous errors -Some errors have detailed explanations: E0046, E0053, E0539, E0658. +Some errors have detailed explanations: E0046, E0053, E0539. For more information about an error, try `rustc --explain E0046`. From 766aa193e73fe5ca048b778d6eb94bbde3931f0a Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 10 Mar 2026 16:07:34 +1100 Subject: [PATCH 009/174] Rename some confusing bitset helper functions --- compiler/rustc_index/src/bit_set.rs | 50 ++++++++++++++--------------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index 02e5feb6c5f21..da69d81fb65ca 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -319,7 +319,7 @@ impl DenseBitSet { // quickly and accurately detect whether the update changed anything. // But that's only worth doing if there's an actual use-case. - bitwise(&mut self.words, &other.words, |a, b| a | !b); + update_words(&mut self.words, &other.words, |a, b| a | !b); // The bitwise update `a | !b` can result in the last word containing // out-of-domain bits, so we need to clear them. self.clear_excess_bits(); @@ -330,17 +330,17 @@ impl DenseBitSet { impl BitRelations> for DenseBitSet { fn union(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a | b) + update_words(&mut self.words, &other.words, |a, b| a | b) } fn subtract(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a & !b) + update_words(&mut self.words, &other.words, |a, b| a & !b) } fn intersect(&mut self, other: &DenseBitSet) -> bool { assert_eq!(self.domain_size, other.domain_size); - bitwise(&mut self.words, &other.words, |a, b| a & b) + update_words(&mut self.words, &other.words, |a, b| a & b) } } @@ -787,7 +787,7 @@ impl BitRelations> for ChunkedBitSet { // Do a more precise "will anything change?" test. Also a // performance win. let op = |a, b| a | b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -797,7 +797,7 @@ impl BitRelations> for ChunkedBitSet { // If we reach here, `self_chunk_words` is definitely changing. let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -865,7 +865,7 @@ impl BitRelations> for ChunkedBitSet { // See `ChunkedBitSet::union` for details on what is happening here. let num_words = num_words(*chunk_domain_size as usize); let op = |a: Word, b: Word| a & !b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -874,7 +874,7 @@ impl BitRelations> for ChunkedBitSet { } let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -914,7 +914,7 @@ impl BitRelations> for ChunkedBitSet { // See `ChunkedBitSet::union` for details on what is happening here. let num_words = num_words(*chunk_domain_size as usize); let op = |a, b| a & b; - if !bitwise_changes( + if !would_modify_words( &self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -923,7 +923,7 @@ impl BitRelations> for ChunkedBitSet { } let self_chunk_words = Rc::make_mut(self_chunk_words); - let has_changed = bitwise( + let has_changed = update_words( &mut self_chunk_words[0..num_words], &other_chunk_words[0..num_words], op, @@ -1052,10 +1052,10 @@ impl fmt::Debug for ChunkedBitSet { } } -/// Sets `out_vec[i] = op(out_vec[i], in_vec[i])` for each index `i` in both +/// Sets `lhs[i] = op(lhs[i], rhs[i])` for each index `i` in both /// slices. The slices must have the same length. /// -/// Returns true if at least one bit in `out_vec` was changed. +/// Returns true if at least one bit in `lhs` was changed. /// /// ## Warning /// Some bitwise operations (e.g. union-not, xor) can set output bits that were @@ -1065,16 +1065,16 @@ impl fmt::Debug for ChunkedBitSet { /// "changed" return value unreliable, because the change might have only /// affected excess bits. #[inline] -fn bitwise(out_vec: &mut [Word], in_vec: &[Word], op: Op) -> bool +fn update_words(lhs: &mut [Word], rhs: &[Word], op: Op) -> bool where Op: Fn(Word, Word) -> Word, { - assert_eq!(out_vec.len(), in_vec.len()); + assert_eq!(lhs.len(), rhs.len()); let mut changed = 0; - for (out_elem, in_elem) in iter::zip(out_vec, in_vec) { - let old_val = *out_elem; - let new_val = op(old_val, *in_elem); - *out_elem = new_val; + for (lhs_slot, &rhs_val) in iter::zip(lhs, rhs) { + let old_val = *lhs_slot; + let new_val = op(old_val, rhs_val); + *lhs_slot = new_val; // This is essentially equivalent to a != with changed being a bool, but // in practice this code gets auto-vectorized by the compiler for most // operators. Using != here causes us to generate quite poor code as the @@ -1084,16 +1084,16 @@ where changed != 0 } -/// Does this bitwise operation change `out_vec`? +/// Returns true if a call to [`update_words`] would modify `lhs`, i.e. +/// `lhs[i] != op(lhs[i], rhs[i])` for some `i`. #[inline] -fn bitwise_changes(out_vec: &[Word], in_vec: &[Word], op: Op) -> bool +fn would_modify_words(lhs: &[Word], rhs: &[Word], op: Op) -> bool where Op: Fn(Word, Word) -> Word, { - assert_eq!(out_vec.len(), in_vec.len()); - for (out_elem, in_elem) in iter::zip(out_vec, in_vec) { - let old_val = *out_elem; - let new_val = op(old_val, *in_elem); + assert_eq!(lhs.len(), rhs.len()); + for (&old_val, &rhs_val) in iter::zip(lhs, rhs) { + let new_val = op(old_val, rhs_val); if old_val != new_val { return true; } @@ -1499,7 +1499,7 @@ impl BitMatrix { assert!(write.index() < self.num_rows); assert_eq!(with.domain_size(), self.num_columns); let (write_start, write_end) = self.range(write); - bitwise(&mut self.words[write_start..write_end], &with.words, |a, b| a | b) + update_words(&mut self.words[write_start..write_end], &with.words, |a, b| a | b) } /// Sets every cell in `row` to true. From 3dc7e27a978888ac024939b1baa2049289569c9f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Tue, 10 Mar 2026 16:15:27 +1100 Subject: [PATCH 010/174] Make bitset `would_modify_words` more vectorizer-friendly --- compiler/rustc_index/src/bit_set.rs | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_index/src/bit_set.rs b/compiler/rustc_index/src/bit_set.rs index da69d81fb65ca..2910ba7c46851 100644 --- a/compiler/rustc_index/src/bit_set.rs +++ b/compiler/rustc_index/src/bit_set.rs @@ -1092,13 +1092,32 @@ where Op: Fn(Word, Word) -> Word, { assert_eq!(lhs.len(), rhs.len()); - for (&old_val, &rhs_val) in iter::zip(lhs, rhs) { - let new_val = op(old_val, rhs_val); - if old_val != new_val { + + // To make codegen more vectorizer-friendly, we traverse each slice in larger + // "subchunks", and only consider an early return at subchunk boundaries. + // These subchunks are smaller than full `ChunkedBitSet` chunks, so that + // we still have some chance of stopping early. + const SUBCHUNK_LEN: usize = 64 / size_of::(); + let (lhs_chunks, lhs_tail) = lhs.as_chunks::(); + let (rhs_chunks, rhs_tail) = rhs.as_chunks::(); + + let would_modify_subchunk = |lhs_chunk: &[Word], rhs_chunk: &[Word]| { + let mut changed = 0; + for (&old_val, &rhs_val) in iter::zip(lhs_chunk, rhs_chunk) { + let new_val = op(old_val, rhs_val); + // Set `changed` to a non-zero value if any bits changed. + // This gives better SIMD codegen than using an actual boolean. + changed |= old_val ^ new_val; + } + changed != 0 + }; + + for (lhs_chunk, rhs_chunk) in iter::zip(lhs_chunks, rhs_chunks) { + if would_modify_subchunk(lhs_chunk, rhs_chunk) { return true; } } - false + would_modify_subchunk(lhs_tail, rhs_tail) } /// A bitset with a mixed representation, using `DenseBitSet` for small and From e36f74d3b88290f43a9b906d5b4e5b601224c717 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Wed, 4 Feb 2026 16:22:15 +0100 Subject: [PATCH 011/174] Use strongly typed wrapped indices in `VecDeque` --- .../alloc/src/collections/vec_deque/drain.rs | 16 +- .../src/collections/vec_deque/extract_if.rs | 14 +- .../src/collections/vec_deque/into_iter.rs | 9 +- .../alloc/src/collections/vec_deque/mod.rs | 418 ++++++++++++------ .../src/collections/vec_deque/spec_extend.rs | 6 +- .../alloc/src/collections/vec_deque/splice.rs | 6 +- .../alloc/src/collections/vec_deque/tests.rs | 46 +- 7 files changed, 351 insertions(+), 164 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/drain.rs b/library/alloc/src/collections/vec_deque/drain.rs index a43853604a2d2..da4b803c64d56 100644 --- a/library/alloc/src/collections/vec_deque/drain.rs +++ b/library/alloc/src/collections/vec_deque/drain.rs @@ -5,6 +5,7 @@ use core::ptr::NonNull; use core::{fmt, ptr}; use super::VecDeque; +use super::index::WrappedIndex; use crate::alloc::{Allocator, Global}; /// A draining iterator over the elements of a `VecDeque`. @@ -203,11 +204,11 @@ impl Drop for Drain<'_, T, A> { let (src, dst, len); if head_len < tail_len { src = source_deque.head; - dst = source_deque.to_physical_idx(drain_len); + dst = source_deque.to_wrapped_index(drain_len); len = head_len; } else { - src = source_deque.to_physical_idx(head_len + drain_len); - dst = source_deque.to_physical_idx(head_len); + src = source_deque.to_wrapped_index(head_len + drain_len); + dst = source_deque.to_wrapped_index(head_len); len = tail_len; }; @@ -220,10 +221,10 @@ impl Drop for Drain<'_, T, A> { if new_len == 0 { // Special case: If the entire deque was drained, reset the head back to 0, // like `.clear()` does. - source_deque.head = 0; + source_deque.head = WrappedIndex::zero(); } else if head_len < tail_len { // If we moved the head above, then we need to adjust the head index here. - source_deque.head = source_deque.to_physical_idx(drain_len); + source_deque.head = source_deque.to_wrapped_index(drain_len); } source_deque.len = new_len; } @@ -240,7 +241,7 @@ impl Iterator for Drain<'_, T, A> { if self.remaining == 0 { return None; } - let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx) }; + let wrapped_idx = unsafe { self.deque.as_ref().to_wrapped_index(self.idx) }; self.idx += 1; self.remaining -= 1; Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) @@ -261,7 +262,8 @@ impl DoubleEndedIterator for Drain<'_, T, A> { return None; } self.remaining -= 1; - let wrapped_idx = unsafe { self.deque.as_ref().to_physical_idx(self.idx + self.remaining) }; + let wrapped_idx = + unsafe { self.deque.as_ref().to_wrapped_index(self.idx + self.remaining) }; Some(unsafe { self.deque.as_mut().buffer_read(wrapped_idx) }) } } diff --git a/library/alloc/src/collections/vec_deque/extract_if.rs b/library/alloc/src/collections/vec_deque/extract_if.rs index 437f0d6dd5eb3..19439dfb4f05d 100644 --- a/library/alloc/src/collections/vec_deque/extract_if.rs +++ b/library/alloc/src/collections/vec_deque/extract_if.rs @@ -83,8 +83,8 @@ where // // Note: we can't use `vec.get_mut(i).unwrap()` here since the precondition for that // function is that i < vec.len, but we've set vec's length to zero. - let idx = self.vec.to_physical_idx(i); - let cur = unsafe { &mut *self.vec.ptr().add(idx) }; + let idx = self.vec.to_wrapped_index(i); + let cur = unsafe { &mut *self.vec.ptr().add(idx.as_index()) }; let drained = (self.pred)(cur); // Update the index *after* the predicate is called. If the index // is updated prior and the predicate panics, the element at this @@ -95,7 +95,7 @@ where // SAFETY: We never touch this element again after returning it. return Some(unsafe { ptr::read(cur) }); } else if self.del > 0 { - let hole_slot = self.vec.to_physical_idx(i - self.del); + let hole_slot = self.vec.to_wrapped_index(i - self.del); // SAFETY: `self.del` > 0, so the hole slot must not overlap with current element. // We use copy for move, and never touch this element again. unsafe { self.vec.wrap_copy(idx, hole_slot, 1) }; @@ -113,8 +113,8 @@ where impl Drop for ExtractIf<'_, T, F, A> { fn drop(&mut self) { if self.del > 0 { - let src = self.vec.to_physical_idx(self.idx); - let dst = self.vec.to_physical_idx(self.idx - self.del); + let src = self.vec.to_wrapped_index(self.idx); + let dst = self.vec.to_wrapped_index(self.idx - self.del); let len = self.old_len - self.idx; // SAFETY: Trailing unchecked items must be valid since we never touch them. unsafe { self.vec.wrap_copy(src, dst, len) }; @@ -131,7 +131,7 @@ where { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let peek = if self.idx < self.end { - let idx = self.vec.to_physical_idx(self.idx); + let idx = self.vec.to_wrapped_index(self.idx); // This has to use pointer arithmetic as `self.vec[self.idx]` or // `self.vec.get_unchecked(self.idx)` wouldn't work since we // temporarily set the length of `self.vec` to zero. @@ -141,7 +141,7 @@ where // smaller than `self.old_len`, `idx` is valid for indexing the // buffer. Also, per the invariant of `self.idx`, this element // has not been inspected/moved out yet. - Some(unsafe { &*self.vec.ptr().add(idx) }) + Some(unsafe { &*self.vec.ptr().add(idx.as_index()) }) } else { None }; diff --git a/library/alloc/src/collections/vec_deque/into_iter.rs b/library/alloc/src/collections/vec_deque/into_iter.rs index 2b09a5e7ddc58..e18b85dd4b694 100644 --- a/library/alloc/src/collections/vec_deque/into_iter.rs +++ b/library/alloc/src/collections/vec_deque/into_iter.rs @@ -5,6 +5,7 @@ use core::ops::Try; use core::{array, fmt, ptr}; use super::VecDeque; +use super::index::WrappedIndex; use crate::alloc::{Allocator, Global}; /// An owning iterator over the elements of a `VecDeque`. @@ -86,7 +87,7 @@ impl Iterator for IntoIter { impl<'a, T, A: Allocator> Drop for Guard<'a, T, A> { fn drop(&mut self) { self.deque.len -= self.consumed; - self.deque.head = self.deque.to_physical_idx(self.consumed); + self.deque.head = self.deque.to_wrapped_index(self.consumed); } } @@ -140,7 +141,7 @@ impl Iterator for IntoIter { // SAFETY: By manually adjusting the head and length of the deque, we effectively // make it forget the first `N` elements, so taking ownership of them is safe. unsafe { ptr::copy_nonoverlapping(head.as_ptr(), raw_arr_ptr, N) }; - self.inner.head = self.inner.to_physical_idx(N); + self.inner.head = self.inner.to_wrapped_index(N); self.inner.len -= N; // SAFETY: We initialized the entire array with items from `head` return Ok(unsafe { raw_arr.transpose().assume_init() }); @@ -155,7 +156,7 @@ impl Iterator for IntoIter { unsafe { ptr::copy_nonoverlapping(tail.as_ptr(), raw_arr_ptr.add(head.len()), remaining) }; - self.inner.head = self.inner.to_physical_idx(N); + self.inner.head = self.inner.to_wrapped_index(N); self.inner.len -= N; // SAFETY: We initialized the entire array with items from `head` and `tail` Ok(unsafe { raw_arr.transpose().assume_init() }) @@ -166,7 +167,7 @@ impl Iterator for IntoIter { }; let init = head.len() + tail.len(); // We completely drained all the deques elements. - self.inner.head = 0; + self.inner.head = WrappedIndex::zero(); self.inner.len = 0; // SAFETY: We copied all elements from both slices to the beginning of the array, so // the given range is initialized. diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 43a6df7ddf753..a090038491528 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -107,7 +107,7 @@ pub struct VecDeque< > { // `self[0]`, if it exists, is `buf[head]`. // `head < buf.capacity()`, unless `buf.capacity() == 0` when `head == 0`. - head: usize, + head: WrappedIndex, // the number of initialized elements, starting from the one at `head` and potentially wrapping around. // if `len == 0`, the exact value of `head` is unimportant. // if `T` is zero-Sized, then `self.len <= usize::MAX`, otherwise `self.len <= isize::MAX as usize`. @@ -183,7 +183,7 @@ impl VecDeque { unsafe fn push_unchecked(&mut self, element: T) { // SAFETY: Because of the precondition, it's guaranteed that there is space // in the logical array after the last element. - unsafe { self.buffer_write(self.to_physical_idx(self.len), element) }; + unsafe { self.buffer_write(self.to_wrapped_index(self.len), element) }; // This can't overflow because `deque.len() < deque.capacity() <= usize::MAX`. self.len += 1; } @@ -205,8 +205,8 @@ impl VecDeque { /// Moves an element out of the buffer #[inline] - unsafe fn buffer_read(&mut self, off: usize) -> T { - unsafe { ptr::read(self.ptr().add(off)) } + unsafe fn buffer_read(&mut self, off: WrappedIndex) -> T { + unsafe { ptr::read(self.ptr().add(off.as_index())) } } /// Writes an element into the buffer, moving it and returning a pointer to it. @@ -214,9 +214,9 @@ impl VecDeque { /// /// May only be called if `off < self.capacity()`. #[inline] - unsafe fn buffer_write(&mut self, off: usize, value: T) -> &mut T { + unsafe fn buffer_write(&mut self, off: WrappedIndex, value: T) -> &mut T { unsafe { - let ptr = self.ptr().add(off); + let ptr = self.ptr().add(off.as_index()); ptr::write(ptr, value); &mut *ptr } @@ -240,20 +240,23 @@ impl VecDeque { /// Returns the index in the underlying buffer for a given logical element /// index + addend. #[inline] - fn wrap_add(&self, idx: usize, addend: usize) -> usize { - wrap_index(idx.wrapping_add(addend), self.capacity()) + fn wrap_add(&self, idx: WrappedIndex, addend: usize) -> WrappedIndex { + wrap_index(idx.as_index().wrapping_add(addend), self.capacity()) } #[inline] - fn to_physical_idx(&self, idx: usize) -> usize { + fn to_wrapped_index(&self, idx: usize) -> WrappedIndex { self.wrap_add(self.head, idx) } /// Returns the index in the underlying buffer for a given logical element /// index - subtrahend. #[inline] - fn wrap_sub(&self, idx: usize, subtrahend: usize) -> usize { - wrap_index(idx.wrapping_sub(subtrahend).wrapping_add(self.capacity()), self.capacity()) + fn wrap_sub(&self, idx: WrappedIndex, subtrahend: usize) -> WrappedIndex { + wrap_index( + idx.as_index().wrapping_sub(subtrahend).wrapping_add(self.capacity()), + self.capacity(), + ) } /// Get source, destination and count (like the arguments to [`ptr::copy_nonoverlapping`]) @@ -273,7 +276,7 @@ impl VecDeque { src: usize, dst: usize, count: usize, - head: usize, + head: WrappedIndex, ) -> [(*const T, *mut T, usize); 2] { // "`src` and `dst` must be at least as far apart as `count`" debug_assert!( @@ -289,8 +292,8 @@ impl VecDeque { let wrapped_src = self.wrap_add(head, src); let wrapped_dst = self.wrap_add(head, dst); - let room_after_src = self.capacity() - wrapped_src; - let room_after_dst = self.capacity() - wrapped_dst; + let room_after_src = self.capacity() - wrapped_src.as_index(); + let room_after_dst = self.capacity() - wrapped_dst.as_index(); let src_wraps = room_after_src < count; let dst_wraps = room_after_dst < count; @@ -305,8 +308,8 @@ impl VecDeque { unsafe { let ptr = self.ptr(); - let src_ptr = ptr.add(wrapped_src); - let dst_ptr = ptr.add(wrapped_dst); + let src_ptr = ptr.add(wrapped_src.as_index()); + let dst_ptr = ptr.add(wrapped_dst.as_index()); if src_wraps { [ @@ -330,7 +333,7 @@ impl VecDeque { /// Copies a contiguous block of memory len long from src to dst #[inline] - unsafe fn copy(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn copy(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( dst + len <= self.capacity(), "cpy dst={} src={} len={} cap={}", @@ -348,13 +351,13 @@ impl VecDeque { self.capacity() ); unsafe { - ptr::copy(self.ptr().add(src), self.ptr().add(dst), len); + ptr::copy(self.ptr().add(src.as_index()), self.ptr().add(dst.as_index()), len); } } /// Copies a contiguous block of memory len long from src to dst #[inline] - unsafe fn copy_nonoverlapping(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn copy_nonoverlapping(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( dst + len <= self.capacity(), "cno dst={} src={} len={} cap={}", @@ -372,16 +375,21 @@ impl VecDeque { self.capacity() ); unsafe { - ptr::copy_nonoverlapping(self.ptr().add(src), self.ptr().add(dst), len); + ptr::copy_nonoverlapping( + self.ptr().add(src.as_index()), + self.ptr().add(dst.as_index()), + len, + ); } } /// Copies a potentially wrapping block of memory len long from src to dest. /// (abs(dst - src) + len) must be no larger than capacity() (There must be at /// most one continuous overlapping region between src and dest). - unsafe fn wrap_copy(&mut self, src: usize, dst: usize, len: usize) { + unsafe fn wrap_copy(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( - cmp::min(src.abs_diff(dst), self.capacity() - src.abs_diff(dst)) + len + cmp::min(src.abs_diff(dst).as_index(), self.capacity() - src.abs_diff(dst).as_index()) + + len <= self.capacity(), "wrc dst={} src={} len={} cap={}", dst, @@ -395,10 +403,10 @@ impl VecDeque { return; } - let dst_after_src = self.wrap_sub(dst, src) < len; + let dst_after_src = self.wrap_sub(dst, src.as_index()) < len; - let src_pre_wrap_len = self.capacity() - src; - let dst_pre_wrap_len = self.capacity() - dst; + let src_pre_wrap_len = self.capacity() - src.as_index(); + let dst_pre_wrap_len = self.capacity() - dst.as_index(); let src_wraps = src_pre_wrap_len < len; let dst_wraps = dst_pre_wrap_len < len; @@ -426,7 +434,11 @@ impl VecDeque { // unsafe { self.copy(src, dst, dst_pre_wrap_len); - self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len); + self.copy( + src.add(dst_pre_wrap_len), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); } } (true, false, true) => { @@ -439,7 +451,11 @@ impl VecDeque { // . . D . // unsafe { - self.copy(src + dst_pre_wrap_len, 0, len - dst_pre_wrap_len); + self.copy( + src.add(dst_pre_wrap_len), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); self.copy(src, dst, dst_pre_wrap_len); } } @@ -454,7 +470,11 @@ impl VecDeque { // unsafe { self.copy(src, dst, src_pre_wrap_len); - self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len); + self.copy( + WrappedIndex::zero(), + dst.add(src_pre_wrap_len), + len - src_pre_wrap_len, + ); } } (true, true, false) => { @@ -467,7 +487,11 @@ impl VecDeque { // D . . . // unsafe { - self.copy(0, dst + src_pre_wrap_len, len - src_pre_wrap_len); + self.copy( + WrappedIndex::zero(), + dst.add(src_pre_wrap_len), + len - src_pre_wrap_len, + ); self.copy(src, dst, src_pre_wrap_len); } } @@ -485,8 +509,12 @@ impl VecDeque { let delta = dst_pre_wrap_len - src_pre_wrap_len; unsafe { self.copy(src, dst, src_pre_wrap_len); - self.copy(0, dst + src_pre_wrap_len, delta); - self.copy(delta, 0, len - dst_pre_wrap_len); + self.copy(WrappedIndex::zero(), dst.add(src_pre_wrap_len), delta); + self.copy( + WrappedIndex::new(delta), + WrappedIndex::zero(), + len - dst_pre_wrap_len, + ); } } (true, true, true) => { @@ -502,8 +530,16 @@ impl VecDeque { debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); let delta = src_pre_wrap_len - dst_pre_wrap_len; unsafe { - self.copy(0, delta, len - src_pre_wrap_len); - self.copy(self.capacity() - delta, 0, delta); + self.copy( + WrappedIndex::zero(), + WrappedIndex::new(delta), + len - src_pre_wrap_len, + ); + self.copy( + WrappedIndex::new(self.capacity() - delta), + WrappedIndex::zero(), + delta, + ); self.copy(src, dst, dst_pre_wrap_len); } } @@ -513,17 +549,17 @@ impl VecDeque { /// Copies all values from `src` to `dst`, wrapping around if needed. /// Assumes capacity is sufficient. #[inline] - unsafe fn copy_slice(&mut self, dst: usize, src: &[T]) { + unsafe fn copy_slice(&mut self, dst: WrappedIndex, src: &[T]) { debug_assert!(src.len() <= self.capacity()); - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { unsafe { - ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst), src.len()); + ptr::copy_nonoverlapping(src.as_ptr(), self.ptr().add(dst.as_index()), src.len()); } } else { let (left, right) = src.split_at(head_room); unsafe { - ptr::copy_nonoverlapping(left.as_ptr(), self.ptr().add(dst), left.len()); + ptr::copy_nonoverlapping(left.as_ptr(), self.ptr().add(dst.as_index()), left.len()); ptr::copy_nonoverlapping(right.as_ptr(), self.ptr(), right.len()); } } @@ -533,7 +569,7 @@ impl VecDeque { /// Assumes capacity is sufficient. /// Equivalent to calling [`VecDeque::copy_slice`] with a [reversed](https://doc.rust-lang.org/std/primitive.slice.html#method.reverse) slice. #[inline] - unsafe fn copy_slice_reversed(&mut self, dst: usize, src: &[T]) { + unsafe fn copy_slice_reversed(&mut self, dst: WrappedIndex, src: &[T]) { /// # Safety /// /// See [`ptr::copy_nonoverlapping`]. @@ -544,15 +580,23 @@ impl VecDeque { } debug_assert!(src.len() <= self.capacity()); - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); if src.len() <= head_room { unsafe { - copy_nonoverlapping_reversed(src.as_ptr(), self.ptr().add(dst), src.len()); + copy_nonoverlapping_reversed( + src.as_ptr(), + self.ptr().add(dst.as_index()), + src.len(), + ); } } else { let (left, right) = src.split_at(src.len() - head_room); unsafe { - copy_nonoverlapping_reversed(right.as_ptr(), self.ptr().add(dst), right.len()); + copy_nonoverlapping_reversed( + right.as_ptr(), + self.ptr().add(dst.as_index()), + right.len(), + ); copy_nonoverlapping_reversed(left.as_ptr(), self.ptr(), left.len()); } } @@ -567,12 +611,12 @@ impl VecDeque { #[inline] unsafe fn write_iter( &mut self, - dst: usize, + dst: WrappedIndex, iter: impl Iterator, written: &mut usize, ) { iter.enumerate().for_each(|(i, element)| unsafe { - self.buffer_write(dst + i, element); + self.buffer_write(dst.add(i), element); *written += 1; }); } @@ -587,7 +631,7 @@ impl VecDeque { /// Assumes capacity is sufficient. unsafe fn write_iter_wrapping( &mut self, - dst: usize, + dst: WrappedIndex, mut iter: impl Iterator, len: usize, ) -> usize { @@ -602,7 +646,7 @@ impl VecDeque { } } - let head_room = self.capacity() - dst; + let head_room = self.capacity() - dst.as_index(); let mut guard = Guard { deque: self, written: 0 }; @@ -615,7 +659,7 @@ impl VecDeque { ByRefSized(&mut iter).take(head_room), &mut guard.written, ); - guard.deque.write_iter(0, iter, &mut guard.written) + guard.deque.write_iter(WrappedIndex::zero(), iter, &mut guard.written) }; } @@ -652,16 +696,20 @@ impl VecDeque { // A // Nop } else { - let head_len = old_capacity - self.head; + let head_len = old_capacity - self.head.as_index(); let tail_len = self.len - head_len; if head_len > tail_len && new_capacity - old_capacity >= tail_len { // B unsafe { - self.copy_nonoverlapping(0, old_capacity, tail_len); + self.copy_nonoverlapping( + WrappedIndex::zero(), + WrappedIndex::new(old_capacity), + tail_len, + ); } } else { // C - let new_head = new_capacity - head_len; + let new_head = unsafe { WrappedIndex::new(new_capacity - head_len) }; unsafe { // can't use copy_nonoverlapping here, because if e.g. head_len = 2 // and new_capacity = old_capacity + 1, then the heads overlap. @@ -780,7 +828,7 @@ impl VecDeque { #[must_use] pub const fn new() -> VecDeque { // FIXME(const-hack): This should just be `VecDeque::new_in(Global)` once that hits stable. - VecDeque { head: 0, len: 0, buf: RawVec::new() } + VecDeque { head: WrappedIndex::zero(), len: 0, buf: RawVec::new() } } /// Creates an empty deque with space for at least `capacity` elements. @@ -820,7 +868,11 @@ impl VecDeque { #[inline] #[unstable(feature = "try_with_capacity", issue = "91913")] pub fn try_with_capacity(capacity: usize) -> Result, TryReserveError> { - Ok(VecDeque { head: 0, len: 0, buf: RawVec::try_with_capacity_in(capacity, Global)? }) + Ok(VecDeque { + head: WrappedIndex::zero(), + len: 0, + buf: RawVec::try_with_capacity_in(capacity, Global)?, + }) } } @@ -840,7 +892,7 @@ impl VecDeque { #[inline] #[unstable(feature = "allocator_api", issue = "32838")] pub const fn new_in(alloc: A) -> VecDeque { - VecDeque { head: 0, len: 0, buf: RawVec::new_in(alloc) } + VecDeque { head: WrappedIndex::zero(), len: 0, buf: RawVec::new_in(alloc) } } /// Creates an empty deque with space for at least `capacity` elements. @@ -857,7 +909,11 @@ impl VecDeque { /// ``` #[unstable(feature = "allocator_api", issue = "32838")] pub fn with_capacity_in(capacity: usize, alloc: A) -> VecDeque { - VecDeque { head: 0, len: 0, buf: RawVec::with_capacity_in(capacity, alloc) } + VecDeque { + head: WrappedIndex::zero(), + len: 0, + buf: RawVec::with_capacity_in(capacity, alloc), + } } /// Creates a `VecDeque` from a raw allocation, when the initialized @@ -886,7 +942,7 @@ impl VecDeque { // and that the allocation is valid for use in `RawVec`. unsafe { VecDeque { - head: initialized.start, + head: WrappedIndex::new(initialized.start), len: initialized.end.unchecked_sub(initialized.start), buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), } @@ -912,8 +968,8 @@ impl VecDeque { #[stable(feature = "rust1", since = "1.0.0")] pub fn get(&self, index: usize) -> Option<&T> { if index < self.len { - let idx = self.to_physical_idx(index); - unsafe { Some(&*self.ptr().add(idx)) } + let idx = self.to_wrapped_index(index); + unsafe { Some(&*self.ptr().add(idx.as_index())) } } else { None } @@ -942,8 +998,8 @@ impl VecDeque { #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { if index < self.len { - let idx = self.to_physical_idx(index); - unsafe { Some(&mut *self.ptr().add(idx)) } + let idx = self.to_wrapped_index(index); + unsafe { Some(&mut *self.ptr().add(idx.as_index())) } } else { None } @@ -976,9 +1032,9 @@ impl VecDeque { pub fn swap(&mut self, i: usize, j: usize) { assert!(i < self.len()); assert!(j < self.len()); - let ri = self.to_physical_idx(i); - let rj = self.to_physical_idx(j); - unsafe { ptr::swap(self.ptr().add(ri), self.ptr().add(rj)) } + let ri = self.to_wrapped_index(i); + let rj = self.to_wrapped_index(j); + unsafe { ptr::swap(self.ptr().add(ri.as_index()), self.ptr().add(rj.as_index())) } } /// Returns the number of elements the deque can hold without @@ -1229,8 +1285,8 @@ impl VecDeque { let old_head = self.head; if self.len == 0 { - self.head = 0; - } else if self.head >= target_cap && tail_outside { + self.head = WrappedIndex::zero(); + } else if self.head.as_index() >= target_cap && tail_outside { // Head and tail are both out of bounds, so copy all of them to the front. // // H := head @@ -1241,9 +1297,9 @@ impl VecDeque { // [o o o o o o o . ] unsafe { // nonoverlapping because `self.head >= target_cap >= self.len`. - self.copy_nonoverlapping(self.head, 0, self.len); + self.copy_nonoverlapping(self.head, WrappedIndex::zero(), self.len); } - self.head = 0; + self.head = WrappedIndex::zero(); } else if self.head < target_cap && tail_outside { // Head is in bounds, tail is out of bounds. // Copy the overflowing part to the beginning of the @@ -1256,8 +1312,9 @@ impl VecDeque { // L H // [o o . o o o o o ] let len = self.head + self.len - target_cap; + // Safety: head is < target_cap, so the index is wrapped unsafe { - self.copy_nonoverlapping(target_cap, 0, len); + self.copy_nonoverlapping(WrappedIndex::new(target_cap), WrappedIndex::zero(), len); } } else if !self.is_contiguous() { // The head slice is at least partially out of bounds, tail is in bounds. @@ -1270,8 +1327,10 @@ impl VecDeque { // [o o o o o . . . . . . . . . o o ] // L H // [o o o o o . o o ] - let head_len = self.capacity() - self.head; - let new_head = target_cap - head_len; + let head_len = self.capacity() - self.head.as_index(); + + // Safety: head_len is at least one, so new_head will be < target_cap + let new_head = unsafe { WrappedIndex::new(target_cap - head_len) }; unsafe { // can't use `copy_nonoverlapping()` here because the new and old // regions for the head might overlap. @@ -1282,7 +1341,7 @@ impl VecDeque { struct Guard<'a, T, A: Allocator> { deque: &'a mut VecDeque, - old_head: usize, + old_head: WrappedIndex, target_cap: usize, } @@ -1314,7 +1373,7 @@ impl VecDeque { /// /// `old_head` refers to the head index before `shrink_to` was called. `target_cap` /// is the capacity that it was trying to shrink to. - unsafe fn abort_shrink(&mut self, old_head: usize, target_cap: usize) { + unsafe fn abort_shrink(&mut self, old_head: WrappedIndex, target_cap: usize) { // Moral equivalent of self.head + self.len <= target_cap. Won't overflow // because `self.len <= target_cap`. if self.head <= target_cap - self.len { @@ -1323,7 +1382,7 @@ impl VecDeque { } // `shrink_to` already copied the head to fit into the new capacity, so this won't overflow. - let head_len = target_cap - self.head; + let head_len = target_cap - self.head.as_index(); // `self.head > target_cap - self.len` => `self.len > target_cap - self.head =: head_len` so this must be positive. let tail_len = self.len - head_len; @@ -1334,7 +1393,11 @@ impl VecDeque { unsafe { // The old tail and the new tail can't overlap because the head slice lies between them. The // head slice ends at `target_cap`, so that's where we copy to. - self.copy_nonoverlapping(0, target_cap, tail_len); + self.copy_nonoverlapping( + WrappedIndex::zero(), + WrappedIndex::new(target_cap), + tail_len, + ); } } else { // Either there's not enough spare capacity to make the deque contiguous, or the head is shorter than the tail @@ -1459,7 +1522,7 @@ impl VecDeque { // and end < front.len() let end = front.len() - (len - back.len()); let drop_front = front.get_unchecked_mut(..end) as *mut _; - self.head += end; + self.head = self.head.add(end); self.len = len; ptr::drop_in_place(drop_front); } else { @@ -1467,7 +1530,7 @@ impl VecDeque { // 'end' is non-negative by the condition above let end = back.len() - len; let drop_back = back.get_unchecked_mut(..end) as *mut _; - self.head = self.to_physical_idx(self.len - len); + self.head = self.to_wrapped_index(self.len - len); self.len = len; // Make sure the second half is dropped even when a destructor @@ -1682,20 +1745,20 @@ impl VecDeque { // `slice::range` guarantees that `start <= end <= len`. // because `len != 0`, we know that `start < end`, so `start < len` // and the indexing is valid. - let wrapped_start = self.to_physical_idx(start); + let wrapped_start = self.to_wrapped_index(start); // this subtraction can never overflow because `wrapped_start` is // at most `self.capacity()` (and if `self.capacity != 0`, then `wrapped_start` is strictly less // than `self.capacity`). - let head_len = self.capacity() - wrapped_start; + let head_len = self.capacity() - wrapped_start.as_index(); if head_len >= len { // we know that `len + wrapped_start <= self.capacity <= usize::MAX`, so this addition can't overflow - (wrapped_start..wrapped_start + len, 0..0) + (wrapped_start.as_index()..wrapped_start + len, 0..0) } else { // can't overflow because of the if condition let tail_len = len - head_len; - (wrapped_start..self.capacity(), 0..tail_len) + (wrapped_start.as_index()..self.capacity(), 0..tail_len) } } } @@ -1926,7 +1989,7 @@ impl VecDeque { pub fn clear(&mut self) { self.truncate(0); // Not strictly necessary, but leaves things in a more consistent/predictable state. - self.head = 0; + self.head = WrappedIndex::zero(); } /// Returns `true` if the deque contains an element equal to the @@ -2072,7 +2135,7 @@ impl VecDeque { None } else { let old_head = self.head; - self.head = self.to_physical_idx(1); + self.head = self.to_wrapped_index(1); self.len -= 1; unsafe { core::hint::assert_unchecked(self.len < self.capacity()); @@ -2103,7 +2166,7 @@ impl VecDeque { self.len -= 1; unsafe { core::hint::assert_unchecked(self.len < self.capacity()); - Some(self.buffer_read(self.to_physical_idx(self.len))) + Some(self.buffer_read(self.to_wrapped_index(self.len))) } } } @@ -2233,7 +2296,7 @@ impl VecDeque { let len = self.len; self.len += 1; - unsafe { self.buffer_write(self.to_physical_idx(len), value) } + unsafe { self.buffer_write(self.to_wrapped_index(len), value) } } /// Prepends all contents of the iterator to the front of the deque. @@ -2447,9 +2510,9 @@ impl VecDeque { // and panicked. unsafe { // see `remove()` for explanation why this wrap_copy() call is safe. - self.wrap_copy(self.to_physical_idx(index), self.to_physical_idx(index + 1), k); + self.wrap_copy(self.to_wrapped_index(index), self.to_wrapped_index(index + 1), k); self.len += 1; - self.buffer_write(self.to_physical_idx(index), value) + self.buffer_write(self.to_wrapped_index(index), value) } } else { let old_head = self.head; @@ -2457,7 +2520,7 @@ impl VecDeque { unsafe { self.wrap_copy(old_head, self.head, index); self.len += 1; - self.buffer_write(self.to_physical_idx(index), value) + self.buffer_write(self.to_wrapped_index(index), value) } } } @@ -2490,7 +2553,7 @@ impl VecDeque { return None; } - let wrapped_idx = self.to_physical_idx(index); + let wrapped_idx = self.to_wrapped_index(index); let elem = unsafe { Some(self.buffer_read(wrapped_idx)) }; @@ -2503,7 +2566,7 @@ impl VecDeque { self.len -= 1; } else { let old_head = self.head; - self.head = self.to_physical_idx(1); + self.head = self.to_wrapped_index(1); unsafe { self.wrap_copy(old_head, self.head, index) }; self.len -= 1; } @@ -2607,23 +2670,23 @@ impl VecDeque { if T::IS_ZST { self.len = self.len.checked_add(other.len).expect("capacity overflow"); other.len = 0; - other.head = 0; + other.head = WrappedIndex::zero(); return; } self.reserve(other.len); unsafe { let (left, right) = other.as_slices(); - self.copy_slice(self.to_physical_idx(self.len), left); + self.copy_slice(self.to_wrapped_index(self.len), left); // no overflow, because self.capacity() >= old_cap + left.len() >= self.len + left.len() - self.copy_slice(self.to_physical_idx(self.len + left.len()), right); + self.copy_slice(self.to_wrapped_index(self.len + left.len()), right); } // SAFETY: Update pointers after copying to avoid leaving doppelganger // in case of panics. self.len += other.len; // Now that we own its values, forget everything in `other`. other.len = 0; - other.head = 0; + other.head = WrappedIndex::zero(); } /// Retains only the elements specified by the predicate. @@ -2831,11 +2894,13 @@ impl VecDeque { #[stable(feature = "deque_make_contiguous", since = "1.48.0")] pub fn make_contiguous(&mut self) -> &mut [T] { if T::IS_ZST { - self.head = 0; + self.head = WrappedIndex::zero(); } if self.is_contiguous() { - unsafe { return slice::from_raw_parts_mut(self.ptr().add(self.head), self.len) } + unsafe { + return slice::from_raw_parts_mut(self.ptr().add(self.head.as_index()), self.len); + } } let &mut Self { head, len, .. } = self; @@ -2843,9 +2908,11 @@ impl VecDeque { let cap = self.capacity(); let free = cap - len; - let head_len = cap - head; - let tail = len - head_len; - let tail_len = tail; + let head_len = cap - head.as_index(); + + // Safety: tail <= head_len <= len <= capacity + let tail = unsafe { WrappedIndex::new(len - head_len) }; + let tail_len = tail.as_index(); if free >= head_len { // there is enough free space to copy the head in one go, @@ -2855,13 +2922,13 @@ impl VecDeque { // from: DEFGH....ABC // to: ABCDEFGH.... unsafe { - self.copy(0, head_len, tail_len); + self.copy(WrappedIndex::zero(), WrappedIndex::new(head_len), tail_len); // ...DEFGH.ABC - self.copy_nonoverlapping(head, 0, head_len); + self.copy_nonoverlapping(head, WrappedIndex::zero(), head_len); // ABCDEFGH.... } - self.head = 0; + self.head = WrappedIndex::zero(); } else if free >= tail_len { // there is enough free space to copy the tail in one go, // this means that we first shift the head forwards, and then @@ -2872,7 +2939,7 @@ impl VecDeque { unsafe { self.copy(head, tail, head_len); // FGHABCDE.... - self.copy_nonoverlapping(0, tail + head_len, tail_len); + self.copy_nonoverlapping(WrappedIndex::zero(), tail.add(head_len), tail_len); // ...ABCDEFGH. } @@ -2908,7 +2975,7 @@ impl VecDeque { // because we only move the tail forward as much as there's free space // behind it, we don't overwrite any elements of the head slice, and // the slices end up right next to each other. - self.copy(0, free, tail_len); + self.copy(WrappedIndex::zero(), WrappedIndex::new(free), tail_len); } // We just copied the tail right next to the head slice, @@ -2921,7 +2988,7 @@ impl VecDeque { // the used part of the buffer now is `free..self.capacity()`, so set // `head` to the beginning of that range. - self.head = free; + self.head = WrappedIndex::new(free); } } else { // head is shorter so: @@ -2934,7 +3001,7 @@ impl VecDeque { // right next to each other and we don't need to move any memory. if free != 0 { // copy the head slice to lie right behind the tail slice. - self.copy(self.head, tail_len, head_len); + self.copy(self.head, WrappedIndex::new(tail_len), head_len); } // because we copied the head slice so that both slices lie right @@ -2947,12 +3014,12 @@ impl VecDeque { // the used part of the buffer now is `0..self.len`, so set // `head` to the beginning of that range. - self.head = 0; + self.head = WrappedIndex::zero(); } } } - unsafe { slice::from_raw_parts_mut(ptr.add(self.head), self.len) } + unsafe { slice::from_raw_parts_mut(ptr.add(self.head.as_index()), self.len) } } /// Rotates the double-ended queue `n` places to the left. @@ -3052,16 +3119,16 @@ impl VecDeque { unsafe fn rotate_left_inner(&mut self, mid: usize) { debug_assert!(mid * 2 <= self.len()); unsafe { - self.wrap_copy(self.head, self.to_physical_idx(self.len), mid); + self.wrap_copy(self.head, self.to_wrapped_index(self.len), mid); } - self.head = self.to_physical_idx(mid); + self.head = self.to_wrapped_index(mid); } unsafe fn rotate_right_inner(&mut self, k: usize) { debug_assert!(k * 2 <= self.len()); self.head = self.wrap_sub(self.head, k); unsafe { - self.wrap_copy(self.to_physical_idx(self.len), self.head, k); + self.wrap_copy(self.to_wrapped_index(self.len), self.head, k); } } @@ -3462,7 +3529,7 @@ impl SpecExtendFromWithin for VecDeque { let (src, dst, count) = ranges[1]; for offset in (0..count).rev() { dst.add(offset).write((*src.add(offset)).clone()); - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; } @@ -3472,16 +3539,18 @@ impl SpecExtendFromWithin for VecDeque { if let Some(offset) = iter.next() { dst.add(offset).write((*src.add(offset)).clone()); // After the first clone of the second range, wrap `head` around - if self.head == 0 { - self.head = cap; + if self.head.is_zero() { + // SAFETY: the wrapped index may be temporarily equal to the capacity even if it + // is not zero, because we subtract it one line below. + self.head = WrappedIndex::new(cap); } - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; // Continue like normal for offset in iter { dst.add(offset).write((*src.add(offset)).clone()); - self.head -= 1; + self.head = self.head.sub(1); self.len += 1; } } @@ -3535,15 +3604,108 @@ impl SpecExtendFromWithin for VecDeque { } } -/// Returns the index in the underlying buffer for a given logical element index. -#[inline] -fn wrap_index(logical_index: usize, capacity: usize) -> usize { - debug_assert!( - (logical_index == 0 && capacity == 0) - || logical_index < capacity - || (logical_index - capacity) < capacity - ); - if logical_index >= capacity { logical_index - capacity } else { logical_index } +use index::{WrappedIndex, wrap_index}; + +// The code is separated into a module to make it harder to construct a BufferIndex without +// going through wrapping. +mod index { + use core::cmp::Ordering; + + /// Returns the index in the underlying buffer for a given logical element index. + #[inline] + pub(super) fn wrap_index(logical_index: usize, capacity: usize) -> WrappedIndex { + debug_assert!( + (logical_index == 0 && capacity == 0) + || logical_index < capacity + || (logical_index - capacity) < capacity + ); + if logical_index >= capacity { + WrappedIndex(logical_index - capacity) + } else { + WrappedIndex(logical_index) + } + } + + /// Represents an index that can be safely used to index the VecDeque buffer. + /// It exists as a separate type to avoid passing logical (unwrapped) indices to various + /// VecDeque functions by accident. + /// + /// The invariant of this index is that it is always < VecDeque capacity, unless the VecDeque + /// is empty (in that case the index can be 0 when the capacity is 0). + #[derive(Copy, Clone, Debug, PartialOrd, Ord, PartialEq, Eq)] + #[repr(transparent)] + pub(super) struct WrappedIndex(usize); + + impl WrappedIndex { + /// Safety invariant: the newly constructed index must be in-bounds for the VecDeque + #[inline(always)] + pub(super) unsafe fn new(index: usize) -> Self { + Self(index) + } + + /// Safety invariant: the newly constructed index must still be in-bounds for the VecDeque + #[inline(always)] + pub(super) unsafe fn add(self, offset: usize) -> Self { + Self(self.0 + offset) + } + + /// Safety invariant: the newly constructed index must still be in-bounds for the VecDeque + #[inline(always)] + pub(super) unsafe fn sub(self, offset: usize) -> Self { + debug_assert!(self.0 >= offset); + Self(self.0 - offset) + } + + #[inline(always)] + pub(super) const fn zero() -> Self { + Self(0) + } + + #[inline(always)] + pub(super) fn abs_diff(self, other: Self) -> Self { + Self(self.0.abs_diff(other.0)) + } + + #[inline(always)] + pub(super) fn as_index(self) -> usize { + self.0 + } + + #[inline(always)] + pub(super) fn is_zero(self) -> bool { + self.0 == 0 + } + } + + impl core::ops::Add for WrappedIndex { + // The output might not be wrapped anymore + type Output = usize; + + #[inline(always)] + fn add(self, rhs: usize) -> Self::Output { + self.0 + rhs + } + } + + impl core::fmt::Display for WrappedIndex { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + self.0.fmt(f) + } + } + + impl core::cmp::PartialEq for WrappedIndex { + #[inline(always)] + fn eq(&self, other: &usize) -> bool { + self.0.eq(other) + } + } + + impl core::cmp::PartialOrd for WrappedIndex { + #[inline(always)] + fn partial_cmp(&self, other: &usize) -> Option { + self.0.partial_cmp(other) + } + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -3751,7 +3913,11 @@ impl From> for VecDeque { #[inline] fn from(other: Vec) -> Self { let (ptr, len, cap, alloc) = other.into_raw_parts_with_alloc(); - Self { head: 0, len, buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) } } + Self { + head: WrappedIndex::zero(), + len, + buf: unsafe { RawVec::from_raw_parts_in(ptr, cap, alloc) }, + } } } @@ -3796,8 +3962,8 @@ impl From> for Vec { let cap = other.capacity(); let alloc = ptr::read(other.allocator()); - if other.head != 0 { - ptr::copy(buf.add(other.head), buf, len); + if !other.head.is_zero() { + ptr::copy(buf.add(other.head.as_index()), buf, len); } Vec::from_raw_parts_in(buf, len, cap, alloc) } @@ -3824,7 +3990,7 @@ impl From<[T; N]> for VecDeque { ptr::copy_nonoverlapping(arr.as_ptr(), deq.ptr(), N); } } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = N; deq } diff --git a/library/alloc/src/collections/vec_deque/spec_extend.rs b/library/alloc/src/collections/vec_deque/spec_extend.rs index ae48d0ea46789..0699d403d9de5 100644 --- a/library/alloc/src/collections/vec_deque/spec_extend.rs +++ b/library/alloc/src/collections/vec_deque/spec_extend.rs @@ -58,7 +58,7 @@ where self.reserve(additional); let written = unsafe { - self.write_iter_wrapping(self.to_physical_idx(self.len), iter, additional) + self.write_iter_wrapping(self.to_wrapped_index(self.len), iter, additional) }; debug_assert_eq!( @@ -83,7 +83,7 @@ impl SpecExtend> for Ve self.reserve(slice.len()); unsafe { - self.copy_slice(self.to_physical_idx(self.len), slice); + self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); } iterator.forget_remaining_elements_and_dealloc(); @@ -109,7 +109,7 @@ where self.reserve(slice.len()); unsafe { - self.copy_slice(self.to_physical_idx(self.len), slice); + self.copy_slice(self.to_wrapped_index(self.len), slice); self.len += slice.len(); } } diff --git a/library/alloc/src/collections/vec_deque/splice.rs b/library/alloc/src/collections/vec_deque/splice.rs index b82f9fba7ceb3..a29e9c3742564 100644 --- a/library/alloc/src/collections/vec_deque/splice.rs +++ b/library/alloc/src/collections/vec_deque/splice.rs @@ -120,7 +120,7 @@ impl Drain<'_, T, A> { for idx in range_start..range_end { if let Some(new_item) = replace_with.next() { - let index = deque.to_physical_idx(idx); + let index = deque.to_wrapped_index(idx); unsafe { deque.buffer_write(index, new_item) }; deque.len += 1; self.drain_len -= 1; @@ -184,8 +184,8 @@ impl Drain<'_, T, A> { let new_tail_start = tail_start + additional; unsafe { deque.wrap_copy( - deque.to_physical_idx(tail_start), - deque.to_physical_idx(new_tail_start), + deque.to_wrapped_index(tail_start), + deque.to_wrapped_index(new_tail_start), self.tail_len, ); } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index dc50cc34d9dac..6566598bc3648 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -12,7 +12,7 @@ fn bench_push_back_100(b: &mut test::Bencher) { for i in 0..100 { deq.push_back(i); } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 0; }) } @@ -24,7 +24,7 @@ fn bench_push_front_100(b: &mut test::Bencher) { for i in 0..100 { deq.push_front(i); } - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 0; }) } @@ -38,7 +38,7 @@ fn bench_pop_back_100(b: &mut test::Bencher) { unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 100; while !deq.is_empty() { test::black_box(deq.pop_back()); @@ -88,7 +88,7 @@ fn bench_pop_front_100(b: &mut test::Bencher) { unsafe { deq.ptr().write_bytes(0u8, size + 1) }; b.iter(|| { - deq.head = 0; + deq.head = WrappedIndex::zero(); deq.len = 100; while !deq.is_empty() { test::black_box(deq.pop_front()); @@ -109,7 +109,9 @@ fn test_swap_front_back_remove() { let expected: VecDeque<_> = if back { (0..len).collect() } else { (0..len).rev().collect() }; for head_pos in 0..usable_cap { - tester.head = head_pos; + unsafe { + tester.head = WrappedIndex::new(head_pos); + } tester.len = 0; if back { for i in 0..len * 2 { @@ -127,7 +129,7 @@ fn test_swap_front_back_remove() { assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); } } - assert!(tester.head <= tester.capacity()); + assert!(tester.head.as_index() <= tester.capacity()); assert!(tester.len <= tester.capacity()); assert_eq!(tester, expected); } @@ -155,7 +157,9 @@ fn test_insert() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_insert in 0..len { - tester.head = head_pos; + unsafe { + tester.head = WrappedIndex::new(head_pos); + } tester.len = 0; for i in 0..len { if i != to_insert { @@ -636,7 +640,9 @@ fn test_remove() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_remove in 0..=len { - tester.head = head_pos; + unsafe { + tester.head = WrappedIndex::new(head_pos); + } tester.len = 0; for i in 0..len { if i == to_remove { @@ -666,7 +672,9 @@ fn test_range() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - tester.head = head; + unsafe { + tester.head = WrappedIndex::new(head); + } tester.len = 0; for i in 0..len { tester.push_back(i); @@ -691,7 +699,9 @@ fn test_range_mut() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - tester.head = head; + unsafe { + tester.head = WrappedIndex::new(head); + } tester.len = 0; for i in 0..len { tester.push_back(i); @@ -725,7 +735,9 @@ fn test_drain() { for head in 0..cap { for drain_start in 0..=len { for drain_end in drain_start..=len { - tester.head = head; + unsafe { + tester.head = WrappedIndex::new(head); + } tester.len = 0; for i in 0..len { tester.push_back(i); @@ -782,7 +794,9 @@ fn test_shrink_to() { assert_eq!(deque.capacity(), cap); // we can let the head point anywhere in the buffer since the deque is empty. - deque.head = head; + unsafe { + deque.head = WrappedIndex::new(head); + } deque.extend(1..=len); deque.shrink_to(target_cap); @@ -811,7 +825,9 @@ fn test_shrink_to_fit() { let expected = (0..).take(len).collect::>(); for head_pos in 0..=max_cap { tester.reserve(head_pos); - tester.head = head_pos; + unsafe { + tester.head = WrappedIndex::new(head_pos); + } tester.len = 0; tester.reserve(63); for i in 0..len { @@ -848,7 +864,9 @@ fn test_split_off() { let expected_other = (at..).take(len - at).collect::>(); for head_pos in 0..cap { - tester.head = head_pos; + unsafe { + tester.head = WrappedIndex::new(head_pos); + } tester.len = 0; for i in 0..len { tester.push_back(i); From a8e2a7415bdb35f11a0fe628a1bfb59655236650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Fri, 6 Feb 2026 20:27:35 +0100 Subject: [PATCH 012/174] Update debugger visualizers --- src/etc/gdb_providers.py | 8 +++++++- src/etc/lldb_providers.py | 7 +++++-- src/etc/natvis/liballoc.natvis | 2 +- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/etc/gdb_providers.py b/src/etc/gdb_providers.py index bd27998b37706..f361fc38eeaba 100644 --- a/src/etc/gdb_providers.py +++ b/src/etc/gdb_providers.py @@ -169,7 +169,13 @@ def display_hint(): class StdVecDequeProvider(printer_base): def __init__(self, valobj): self._valobj = valobj - self._head = int(valobj["head"]) + + head = valobj["head"] + + # BACKCOMPAT: rust 1.95 + if head.type.code != gdb.TYPE_CODE_INT: + head = head[ZERO_FIELD] + self._head = int(head) self._size = int(valobj["len"]) # BACKCOMPAT: rust 1.75 cap = valobj["buf"]["inner"]["cap"] diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 65f20210323eb..64a058114f6b9 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -1032,7 +1032,7 @@ def StdSliceSummaryProvider(valobj, dict): class StdVecDequeSyntheticProvider: """Pretty-printer for alloc::collections::vec_deque::VecDeque - struct VecDeque { head: usize, len: usize, buf: RawVec } + struct VecDeque { head: BufferIndex, len: usize, buf: RawVec } """ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): @@ -1061,7 +1061,10 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: return element def update(self): - self.head = self.valobj.GetChildMemberWithName("head").GetValueAsUnsigned() + head = self.valobj.GetChildMemberWithName("head") + if head.GetType().num_fields == 1: + head = head.GetChildAtIndex(0) + self.head = head.GetValueAsUnsigned() self.size = self.valobj.GetChildMemberWithName("len").GetValueAsUnsigned() self.buf = self.valobj.GetChildMemberWithName("buf").GetChildMemberWithName( "inner" diff --git a/src/etc/natvis/liballoc.natvis b/src/etc/natvis/liballoc.natvis index 1528a8b1226ca..dade85dafe738 100644 --- a/src/etc/natvis/liballoc.natvis +++ b/src/etc/natvis/liballoc.natvis @@ -23,7 +23,7 @@ - (($T1*)buf.inner.ptr.pointer.pointer)[(i + head) % buf.inner.cap.__0] + (($T1*)buf.inner.ptr.pointer.pointer)[(i + head.__0) % buf.inner.cap.__0] i = i + 1 From 207d3bd5f0ae7b7300869449f7926996a76cbbe1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 7 Feb 2026 21:20:03 +0100 Subject: [PATCH 013/174] Change `abs_diff` return type to `usize` --- library/alloc/src/collections/vec_deque/mod.rs | 7 +++---- src/etc/lldb_providers.py | 1 + 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index a090038491528..2c7bf5d27a690 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -388,8 +388,7 @@ impl VecDeque { /// most one continuous overlapping region between src and dest). unsafe fn wrap_copy(&mut self, src: WrappedIndex, dst: WrappedIndex, len: usize) { debug_assert!( - cmp::min(src.abs_diff(dst).as_index(), self.capacity() - src.abs_diff(dst).as_index()) - + len + cmp::min(src.abs_diff(dst), self.capacity() - src.abs_diff(dst)) + len <= self.capacity(), "wrc dst={} src={} len={} cap={}", dst, @@ -3662,8 +3661,8 @@ mod index { } #[inline(always)] - pub(super) fn abs_diff(self, other: Self) -> Self { - Self(self.0.abs_diff(other.0)) + pub(super) fn abs_diff(self, other: Self) -> usize { + self.0.abs_diff(other.0) } #[inline(always)] diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 64a058114f6b9..6749e75a55238 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -1062,6 +1062,7 @@ def get_child_at_index(self, index: int) -> Optional[SBValue]: def update(self): head = self.valobj.GetChildMemberWithName("head") + # BACKCOMPAT: rust 1.95 if head.GetType().num_fields == 1: head = head.GetChildAtIndex(0) self.head = head.GetValueAsUnsigned() From 23bd862a63c5c5398e96902d1960a79084107aad Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Tue, 28 Apr 2026 00:12:47 -0500 Subject: [PATCH 014/174] move string read logic to standalone function --- src/etc/lldb_providers.py | 46 +++++++++++++++++++++++---------------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index e2d4ac5286ccd..1cab5393eaa15 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -15,7 +15,7 @@ from rust_types import is_tuple_fields if TYPE_CHECKING: - from lldb import SBValue, SBType, SBTypeStaticField, SBTarget + from lldb import SBValue, SBType, SBTypeStaticField, SBTarget, SBProcess # from lldb.formatters import Logger @@ -289,6 +289,28 @@ def vec_to_string(vec: SBValue) -> str: ) +def read_string( + process: SBProcess, address: int, length: int, error: Optional[SBError] = None +) -> str: + """Reads a string from running process's memory. If `error` is passed in, it will be passed + to the `SBProcess.ReadMemory` call, and will reflect any errors after the function is called. + + If any error or exception occurs, a placeholder byte array of the form "" will + be returned instead.""" + + if error is None: + error = SBError() + try: + data = process.ReadMemory(address, length, error) + if error.Success(): + return '"' + data.decode("utf-8", "replace") + '"' + else: + return f"" + except Exception as e: + print(f"Unable to generate String summary: {e.__cause__}") + return "" + + def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): inner_vec = ( valobj.GetNonSyntheticValue() @@ -321,17 +343,9 @@ def StdStringSummaryProvider(valobj: SBValue, dict: LLDBOpaque): if pointer.GetValueAsUnsigned() == 0: return "" - error = SBError() process = pointer.GetProcess() - try: - data = process.ReadMemory(pointer.GetValueAsUnsigned(), length, error) - if error.Success(): - return '"' + data.decode("utf8", "replace") + '"' - else: - return f"" - except Exception as e: - print(f"Unable to generate String summary: {e.__cause__}") - return "" + + return read_string(process, pointer.GetValueAsAddress(), length) def StdOsStringSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: @@ -380,15 +394,9 @@ def StdPathSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str: data_ptr = valobj.GetChildMemberWithName("data_ptr") start = data_ptr.GetValueAsUnsigned() - error = SBError() process = data_ptr.GetProcess() - data = process.ReadMemory(start, length, error) - if PY3: - try: - data = data.decode(encoding="UTF-8") - except UnicodeDecodeError: - return "%r" % data - return '"%s"' % data + + return read_string(process, start, length) def sequence_formatter(output: str, valobj: SBValue, _dict: LLDBOpaque): From aa9f27e865529da9c55e98190a052b81c4485ec7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ber=C3=A1nek?= Date: Sat, 9 May 2026 12:55:16 +0200 Subject: [PATCH 015/174] Rename `WrappedIndex::new` to `from_arbitrary_number` and resolve review remarks --- .../alloc/src/collections/vec_deque/mod.rs | 57 ++++++++++++------- .../alloc/src/collections/vec_deque/tests.rs | 36 +++--------- src/etc/lldb_providers.py | 2 +- 3 files changed, 48 insertions(+), 47 deletions(-) diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 2c7bf5d27a690..d91b35c1f6077 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -510,7 +510,7 @@ impl VecDeque { self.copy(src, dst, src_pre_wrap_len); self.copy(WrappedIndex::zero(), dst.add(src_pre_wrap_len), delta); self.copy( - WrappedIndex::new(delta), + WrappedIndex::from_arbitrary_number(delta), WrappedIndex::zero(), len - dst_pre_wrap_len, ); @@ -531,11 +531,11 @@ impl VecDeque { unsafe { self.copy( WrappedIndex::zero(), - WrappedIndex::new(delta), + WrappedIndex::from_arbitrary_number(delta), len - src_pre_wrap_len, ); self.copy( - WrappedIndex::new(self.capacity() - delta), + WrappedIndex::from_arbitrary_number(self.capacity() - delta), WrappedIndex::zero(), delta, ); @@ -702,13 +702,13 @@ impl VecDeque { unsafe { self.copy_nonoverlapping( WrappedIndex::zero(), - WrappedIndex::new(old_capacity), + WrappedIndex::from_arbitrary_number(old_capacity), tail_len, ); } } else { // C - let new_head = unsafe { WrappedIndex::new(new_capacity - head_len) }; + let new_head = WrappedIndex::from_arbitrary_number(new_capacity - head_len); unsafe { // can't use copy_nonoverlapping here, because if e.g. head_len = 2 // and new_capacity = old_capacity + 1, then the heads overlap. @@ -941,7 +941,7 @@ impl VecDeque { // and that the allocation is valid for use in `RawVec`. unsafe { VecDeque { - head: WrappedIndex::new(initialized.start), + head: WrappedIndex::from_arbitrary_number(initialized.start), len: initialized.end.unchecked_sub(initialized.start), buf: RawVec::from_raw_parts_in(ptr, capacity, alloc), } @@ -1313,7 +1313,11 @@ impl VecDeque { let len = self.head + self.len - target_cap; // Safety: head is < target_cap, so the index is wrapped unsafe { - self.copy_nonoverlapping(WrappedIndex::new(target_cap), WrappedIndex::zero(), len); + self.copy_nonoverlapping( + WrappedIndex::from_arbitrary_number(target_cap), + WrappedIndex::zero(), + len, + ); } } else if !self.is_contiguous() { // The head slice is at least partially out of bounds, tail is in bounds. @@ -1328,8 +1332,8 @@ impl VecDeque { // [o o o o o . o o ] let head_len = self.capacity() - self.head.as_index(); - // Safety: head_len is at least one, so new_head will be < target_cap - let new_head = unsafe { WrappedIndex::new(target_cap - head_len) }; + // head_len is at least one, so new_head will be < target_cap + let new_head = WrappedIndex::from_arbitrary_number(target_cap - head_len); unsafe { // can't use `copy_nonoverlapping()` here because the new and old // regions for the head might overlap. @@ -1394,7 +1398,7 @@ impl VecDeque { // head slice ends at `target_cap`, so that's where we copy to. self.copy_nonoverlapping( WrappedIndex::zero(), - WrappedIndex::new(target_cap), + WrappedIndex::from_arbitrary_number(target_cap), tail_len, ); } @@ -2909,8 +2913,10 @@ impl VecDeque { let free = cap - len; let head_len = cap - head.as_index(); - // Safety: tail <= head_len <= len <= capacity - let tail = unsafe { WrappedIndex::new(len - head_len) }; + // tail <= head < capacity + // head cannot be <= capacity, because we know that VecDeque is non-empty, since it is not + // contiguous at this point + let tail = WrappedIndex::from_arbitrary_number(len - head_len); let tail_len = tail.as_index(); if free >= head_len { @@ -2921,7 +2927,11 @@ impl VecDeque { // from: DEFGH....ABC // to: ABCDEFGH.... unsafe { - self.copy(WrappedIndex::zero(), WrappedIndex::new(head_len), tail_len); + self.copy( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(head_len), + tail_len, + ); // ...DEFGH.ABC self.copy_nonoverlapping(head, WrappedIndex::zero(), head_len); // ABCDEFGH.... @@ -2974,7 +2984,11 @@ impl VecDeque { // because we only move the tail forward as much as there's free space // behind it, we don't overwrite any elements of the head slice, and // the slices end up right next to each other. - self.copy(WrappedIndex::zero(), WrappedIndex::new(free), tail_len); + self.copy( + WrappedIndex::zero(), + WrappedIndex::from_arbitrary_number(free), + tail_len, + ); } // We just copied the tail right next to the head slice, @@ -2987,7 +3001,7 @@ impl VecDeque { // the used part of the buffer now is `free..self.capacity()`, so set // `head` to the beginning of that range. - self.head = WrappedIndex::new(free); + self.head = WrappedIndex::from_arbitrary_number(free); } } else { // head is shorter so: @@ -3000,7 +3014,11 @@ impl VecDeque { // right next to each other and we don't need to move any memory. if free != 0 { // copy the head slice to lie right behind the tail slice. - self.copy(self.head, WrappedIndex::new(tail_len), head_len); + self.copy( + self.head, + WrappedIndex::from_arbitrary_number(tail_len), + head_len, + ); } // because we copied the head slice so that both slices lie right @@ -3541,7 +3559,7 @@ impl SpecExtendFromWithin for VecDeque { if self.head.is_zero() { // SAFETY: the wrapped index may be temporarily equal to the capacity even if it // is not zero, because we subtract it one line below. - self.head = WrappedIndex::new(cap); + self.head = WrappedIndex::from_arbitrary_number(cap); } self.head = self.head.sub(1); self.len += 1; @@ -3636,9 +3654,10 @@ mod index { pub(super) struct WrappedIndex(usize); impl WrappedIndex { - /// Safety invariant: the newly constructed index must be in-bounds for the VecDeque + /// The newly constructed index has to be in-bounds for the VecDeque + /// that uses the index. #[inline(always)] - pub(super) unsafe fn new(index: usize) -> Self { + pub(super) fn from_arbitrary_number(index: usize) -> Self { Self(index) } diff --git a/library/alloc/src/collections/vec_deque/tests.rs b/library/alloc/src/collections/vec_deque/tests.rs index 6566598bc3648..d9cf56de7bb6b 100644 --- a/library/alloc/src/collections/vec_deque/tests.rs +++ b/library/alloc/src/collections/vec_deque/tests.rs @@ -109,9 +109,7 @@ fn test_swap_front_back_remove() { let expected: VecDeque<_> = if back { (0..len).collect() } else { (0..len).rev().collect() }; for head_pos in 0..usable_cap { - unsafe { - tester.head = WrappedIndex::new(head_pos); - } + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; if back { for i in 0..len * 2 { @@ -157,9 +155,7 @@ fn test_insert() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_insert in 0..len { - unsafe { - tester.head = WrappedIndex::new(head_pos); - } + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { if i != to_insert { @@ -640,9 +636,7 @@ fn test_remove() { let expected = (0..).take(len).collect::>(); for head_pos in 0..cap { for to_remove in 0..=len { - unsafe { - tester.head = WrappedIndex::new(head_pos); - } + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { if i == to_remove { @@ -672,9 +666,7 @@ fn test_range() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - unsafe { - tester.head = WrappedIndex::new(head); - } + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -699,9 +691,7 @@ fn test_range_mut() { for head in 0..=cap { for start in 0..=len { for end in start..=len { - unsafe { - tester.head = WrappedIndex::new(head); - } + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -735,9 +725,7 @@ fn test_drain() { for head in 0..cap { for drain_start in 0..=len { for drain_end in drain_start..=len { - unsafe { - tester.head = WrappedIndex::new(head); - } + tester.head = WrappedIndex::from_arbitrary_number(head); tester.len = 0; for i in 0..len { tester.push_back(i); @@ -794,9 +782,7 @@ fn test_shrink_to() { assert_eq!(deque.capacity(), cap); // we can let the head point anywhere in the buffer since the deque is empty. - unsafe { - deque.head = WrappedIndex::new(head); - } + deque.head = WrappedIndex::from_arbitrary_number(head); deque.extend(1..=len); deque.shrink_to(target_cap); @@ -825,9 +811,7 @@ fn test_shrink_to_fit() { let expected = (0..).take(len).collect::>(); for head_pos in 0..=max_cap { tester.reserve(head_pos); - unsafe { - tester.head = WrappedIndex::new(head_pos); - } + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; tester.reserve(63); for i in 0..len { @@ -864,9 +848,7 @@ fn test_split_off() { let expected_other = (at..).take(len - at).collect::>(); for head_pos in 0..cap { - unsafe { - tester.head = WrappedIndex::new(head_pos); - } + tester.head = WrappedIndex::from_arbitrary_number(head_pos); tester.len = 0; for i in 0..len { tester.push_back(i); diff --git a/src/etc/lldb_providers.py b/src/etc/lldb_providers.py index 6749e75a55238..e299a64f2c9a9 100644 --- a/src/etc/lldb_providers.py +++ b/src/etc/lldb_providers.py @@ -1032,7 +1032,7 @@ def StdSliceSummaryProvider(valobj, dict): class StdVecDequeSyntheticProvider: """Pretty-printer for alloc::collections::vec_deque::VecDeque - struct VecDeque { head: BufferIndex, len: usize, buf: RawVec } + struct VecDeque { head: WrappedIndex, len: usize, buf: RawVec } """ def __init__(self, valobj: SBValue, _dict: LLDBOpaque): From c45fc2b842ff499915213f7f10675d68fb31d273 Mon Sep 17 00:00:00 2001 From: odlot <131015417+odlot@users.noreply.github.com> Date: Thu, 4 Dec 2025 21:19:17 +0100 Subject: [PATCH 016/174] Add support for xray in aarch64 unknown none target --- compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs index 13d3b77588a0e..297bd0abc864e 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none.rs @@ -30,6 +30,7 @@ pub(crate) fn target() -> Target { stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, default_uwtable: true, + supports_xray: true, ..Default::default() }; Target { From 689279eb04576a0635ff0180f3db8374352302b6 Mon Sep 17 00:00:00 2001 From: odlot <131015417+odlot@users.noreply.github.com> Date: Mon, 11 May 2026 21:41:50 +0200 Subject: [PATCH 017/174] Add support for xray in aarch64 unknown none softfloat target --- .../src/spec/targets/aarch64_unknown_none_softfloat.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs index 05876891ebd7d..f6527d236d10c 100644 --- a/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs +++ b/compiler/rustc_target/src/spec/targets/aarch64_unknown_none_softfloat.rs @@ -27,6 +27,7 @@ pub(crate) fn target() -> Target { stack_probes: StackProbeType::Inline, panic_strategy: PanicStrategy::Abort, default_uwtable: true, + supports_xray: true, ..Default::default() }; Target { From 743c6a670d4354aee5b6f7235c7a66cdd8f80199 Mon Sep 17 00:00:00 2001 From: odlot <131015417+odlot@users.noreply.github.com> Date: Mon, 11 May 2026 21:59:26 +0200 Subject: [PATCH 018/174] docs: Add remark to bring your own runtime if none available --- src/doc/unstable-book/src/compiler-flags/instrument-xray.md | 1 + 1 file changed, 1 insertion(+) diff --git a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md index 7fb33cd68b4a3..68b881df47385 100644 --- a/src/doc/unstable-book/src/compiler-flags/instrument-xray.md +++ b/src/doc/unstable-book/src/compiler-flags/instrument-xray.md @@ -37,3 +37,4 @@ which on their own don't do anything useful. In order to actually trace the functions, you will need to link a separate runtime library of your choice, such as Clang's [XRay Runtime Library](https://www.llvm.org/docs/XRay.html#xray-runtime-library). +On targets where such a runtime is not available but instrumentation is supported, you must supply and link your own runtime library. From da87ea7514f139ab75f0f621daea9ace2c2d3430 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sun, 10 May 2026 10:51:18 +0200 Subject: [PATCH 019/174] std::net::tcp: let the OS pick the port numbers --- library/std/src/net/tcp/tests.rs | 189 +++++++++++++++---------------- library/std/src/net/test.rs | 7 ++ 2 files changed, 100 insertions(+), 96 deletions(-) diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index f6fe8e1b2353b..8a3a7ee7c5cba 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -1,17 +1,14 @@ +use rand::Rng; + use crate::io::prelude::*; use crate::io::{BorrowedBuf, ErrorKind, IoSlice, IoSliceMut}; use crate::mem::MaybeUninit; -use crate::net::test::{next_test_ip4, next_test_ip6}; +use crate::net::test::{LOCALHOST_IP4, LOCALHOST_IP6}; use crate::net::*; use crate::sync::mpsc::channel; use crate::time::{Duration, Instant}; use crate::{fmt, thread}; -fn each_ip(f: &mut dyn FnMut(SocketAddr)) { - f(next_test_ip4()); - f(next_test_ip6()); -} - macro_rules! t { ($e:expr) => { match $e { @@ -21,6 +18,11 @@ macro_rules! t { }; } +fn each_ip(f: &mut dyn FnMut(TcpListener)) { + f(t!(TcpListener::bind(LOCALHOST_IP4))); + f(t!(TcpListener::bind(LOCALHOST_IP6))); +} + #[test] fn bind_error() { match TcpListener::bind("1.1.1.1:9999") { @@ -46,10 +48,18 @@ fn connect_error() { } } +/// Regression test for `Duration::MAX` timeout issue +/// (https://github.com/rust-lang/rust/issues/112405). #[test] #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 fn connect_timeout_error() { - let socket_addr = next_test_ip4(); + // Pick a random port, but not in the range usually used by the OS's automatic port assignment + // so we cannot conflict with the other tests. + let mut rng = crate::test_helpers::test_rng(); + let port = rng.random_range((16 * 1024)..(48 * 1024)); + let socket_addr = SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)); + + // Ensure we error immediately rather than timing out. let result = TcpStream::connect_timeout(&socket_addr, Duration::MAX); assert!(!matches!(result, Err(e) if e.kind() == ErrorKind::TimedOut)); @@ -60,11 +70,11 @@ fn connect_timeout_error() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn listen_localhost() { - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let addr = t!(listener.local_addr()); let _t = thread::spawn(move || { - let mut stream = t!(TcpStream::connect(&("localhost", socket_addr.port()))); + let mut stream = t!(TcpStream::connect(addr)); t!(stream.write(&[144])); }); @@ -77,15 +87,11 @@ fn listen_localhost() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn connect_loopback() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { - let host = match addr { - SocketAddr::V4(..) => "127.0.0.1", - SocketAddr::V6(..) => "::1", - }; - let mut stream = t!(TcpStream::connect(&(host, addr.port()))); + let mut stream = t!(TcpStream::connect(addr)); t!(stream.write(&[66])); }); @@ -99,8 +105,8 @@ fn connect_loopback() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn smoke_test() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx, rx) = channel(); let _t = thread::spawn(move || { @@ -120,8 +126,8 @@ fn smoke_test() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn read_eof() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let _stream = t!(TcpStream::connect(&addr)); @@ -140,8 +146,8 @@ fn read_eof() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn write_close() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx, rx) = channel(); let _t = thread::spawn(move || { @@ -169,9 +175,9 @@ fn write_close() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_serial() { - each_ip(&mut |addr| { + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let max = 10; - let acceptor = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { for _ in 0..max { @@ -193,8 +199,8 @@ fn multiple_connect_serial() { #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_interleaved_greedy_schedule() { const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let acceptor = acceptor; @@ -231,8 +237,8 @@ fn multiple_connect_interleaved_greedy_schedule() { #[cfg_attr(target_os = "wasi", ignore)] // no threads fn multiple_connect_interleaved_lazy_schedule() { const MAX: usize = 10; - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { for stream in acceptor.incoming().take(MAX) { @@ -266,25 +272,25 @@ fn multiple_connect_interleaved_lazy_schedule() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn socket_and_peer_name() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); - let so_name = t!(listener.local_addr()); - assert_eq!(addr, so_name); - let _t = thread::spawn(move || { - t!(listener.accept()); - }); + each_ip(&mut |listener| { + let addr = t!(listener.local_addr()); + let other_stream = thread::spawn(move || t!(listener.accept())); let stream = t!(TcpStream::connect(&addr)); assert_eq!(addr, t!(stream.peer_addr())); + let (other_stream, other_peer) = other_stream.join().unwrap(); + assert_eq!(addr, t!(other_stream.local_addr())); + assert_eq!(other_peer, t!(other_stream.peer_addr())); + assert_eq!(other_peer, t!(stream.local_addr())); }) } #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn partial_read() { - each_ip(&mut |addr| { + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let (tx, rx) = channel(); - let srv = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { let mut cl = t!(srv.accept()).0; cl.write(&[10]).unwrap(); @@ -304,8 +310,8 @@ fn partial_read() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn read_buf() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); s.write_all(&[1, 2, 3, 4]).unwrap(); @@ -325,8 +331,8 @@ fn read_buf() { #[test] fn read_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let mut s1 = t!(TcpStream::connect(&addr)); let mut s2 = t!(srv.accept()).0; @@ -350,8 +356,8 @@ fn read_vectored() { #[test] fn write_vectored() { - each_ip(&mut |addr| { - let srv = t!(TcpListener::bind(&addr)); + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); let mut s1 = t!(TcpStream::connect(&addr)); let mut s2 = t!(srv.accept()).0; @@ -374,8 +380,8 @@ fn write_vectored() { #[test] fn double_bind() { - each_ip(&mut |addr| { - let listener1 = t!(TcpListener::bind(&addr)); + each_ip(&mut |listener1| { + let addr = t!(listener1.local_addr()); match TcpListener::bind(&addr) { Ok(listener2) => panic!( "This system (perhaps due to options set by TcpListener::bind) \ @@ -399,8 +405,8 @@ fn double_bind() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_smoke() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); @@ -431,8 +437,8 @@ fn tcp_clone_smoke() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_two_read() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let (tx1, rx) = channel(); let tx2 = tx1.clone(); @@ -466,8 +472,8 @@ fn tcp_clone_two_read() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn tcp_clone_two_write() { - each_ip(&mut |addr| { - let acceptor = t!(TcpListener::bind(&addr)); + each_ip(&mut |acceptor| { + let addr = t!(acceptor.local_addr()); let _t = thread::spawn(move || { let mut s = t!(TcpStream::connect(&addr)); @@ -496,8 +502,8 @@ fn tcp_clone_two_write() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn shutdown_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let _t = thread::spawn(move || { let mut c = t!(a.accept()).0; let mut b = [0]; @@ -519,8 +525,8 @@ fn shutdown_smoke() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn close_readwrite_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let (tx, rx) = channel::<()>(); let _t = thread::spawn(move || { let _s = t!(a.accept()); @@ -562,8 +568,8 @@ fn close_readwrite_smoke() { #[cfg_attr(windows, ignore)] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn close_read_wakes_up() { - each_ip(&mut |addr| { - let listener = t!(TcpListener::bind(&addr)); + each_ip(&mut |listener| { + let addr = t!(listener.local_addr()); let _t = thread::spawn(move || { let (stream, _) = t!(listener.accept()); stream @@ -590,8 +596,8 @@ fn close_read_wakes_up() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_while_reading() { - each_ip(&mut |addr| { - let accept = t!(TcpListener::bind(&addr)); + each_ip(&mut |accept| { + let addr = t!(accept.local_addr()); // Enqueue a thread to write to a socket let (tx, rx) = channel(); @@ -631,8 +637,8 @@ fn clone_while_reading() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_accept_smoke() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let a2 = t!(a.try_clone()); let _t = thread::spawn(move || { @@ -650,8 +656,8 @@ fn clone_accept_smoke() { #[test] #[cfg_attr(target_os = "wasi", ignore)] // no threads fn clone_accept_concurrent() { - each_ip(&mut |addr| { - let a = t!(TcpListener::bind(&addr)); + each_ip(&mut |a| { + let addr = t!(a.local_addr()); let a2 = t!(a.try_clone()); let (tx, rx) = channel(); @@ -701,9 +707,9 @@ fn debug() { } let inner_name = if cfg!(windows) { "socket" } else { "fd" }; - let socket_addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&socket_addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let socket_addr = t!(listener.local_addr()); let compare = format!( "TcpListener {{ addr: {:?}, {}: {:?} }}", render_socket_addr(&socket_addr), @@ -734,10 +740,9 @@ fn debug() { #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported #[test] fn timeouts() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); let dur = Duration::new(15410, 0); assert_eq!(None, t!(stream.read_timeout())); @@ -762,10 +767,9 @@ fn timeouts() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported fn test_read_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut buf = [0; 10]; @@ -784,10 +788,9 @@ fn test_read_timeout() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // timeout not supported fn test_read_with_timeout() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_read_timeout(Some(Duration::from_millis(1000)))); let mut other_end = t!(listener.accept()).0; @@ -812,10 +815,8 @@ fn test_read_with_timeout() { // when passed zero Durations #[test] fn test_timeout_zero_duration() { - let addr = next_test_ip4(); - - let listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); let result = stream.set_write_timeout(Some(Duration::new(0, 0))); let err = result.unwrap_err(); @@ -832,10 +833,9 @@ fn test_timeout_zero_duration() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] // linger not supported fn linger() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(None, t!(stream.linger())); t!(stream.set_linger(Some(Duration::from_secs(1)))); @@ -848,9 +848,8 @@ fn linger() { #[cfg_attr(target_env = "sgx", ignore)] #[cfg_attr(target_os = "wasi", ignore)] fn keepalive() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); - let stream = t!(TcpStream::connect(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(false, t!(stream.keepalive())); t!(stream.set_keepalive(true)); @@ -862,10 +861,9 @@ fn keepalive() { #[test] #[cfg_attr(target_env = "sgx", ignore)] fn nodelay() { - let addr = next_test_ip4(); - let _listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); assert_eq!(false, t!(stream.nodelay())); t!(stream.set_nodelay(true)); @@ -879,13 +877,12 @@ fn nodelay() { fn ttl() { let ttl = 100; - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); t!(listener.set_ttl(ttl)); assert_eq!(ttl, t!(listener.ttl())); - let stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_ttl(ttl)); assert_eq!(ttl, t!(stream.ttl())); @@ -894,13 +891,12 @@ fn ttl() { #[test] #[cfg_attr(target_env = "sgx", ignore)] fn set_nonblocking() { - let addr = next_test_ip4(); - let listener = t!(TcpListener::bind(&addr)); + let listener = t!(TcpListener::bind(LOCALHOST_IP4)); t!(listener.set_nonblocking(true)); t!(listener.set_nonblocking(false)); - let mut stream = t!(TcpStream::connect(&("localhost", addr.port()))); + let mut stream = t!(TcpStream::connect(t!(listener.local_addr()))); t!(stream.set_nonblocking(false)); t!(stream.set_nonblocking(true)); @@ -917,10 +913,11 @@ fn set_nonblocking() { #[cfg_attr(target_env = "sgx", ignore)] // FIXME: https://github.com/fortanix/rust-sgx/issues/31 #[cfg_attr(target_os = "wasi", ignore)] // no threads fn peek() { - each_ip(&mut |addr| { + each_ip(&mut |srv| { + let addr = t!(srv.local_addr()); + let (txdone, rxdone) = channel(); - let srv = t!(TcpListener::bind(&addr)); let _t = thread::spawn(move || { let mut cl = t!(srv.accept()).0; cl.write(&[1, 3, 3, 7]).unwrap(); diff --git a/library/std/src/net/test.rs b/library/std/src/net/test.rs index df48b2f2420c3..68cbf7b669232 100644 --- a/library/std/src/net/test.rs +++ b/library/std/src/net/test.rs @@ -7,6 +7,13 @@ use crate::sync::atomic::{AtomicUsize, Ordering}; static PORT: AtomicUsize = AtomicUsize::new(0); const BASE_PORT: u16 = 19600; +/// A localhost address whose port will be picked automatically by the OS. +pub const LOCALHOST_IP4: SocketAddr = + SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 0)); +/// A localhost address whose port will be picked automatically by the OS. +pub const LOCALHOST_IP6: SocketAddr = + SocketAddr::V6(SocketAddrV6::new(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1), 0, 0, 0)); + pub fn next_test_ip4() -> SocketAddr { let port = PORT.fetch_add(1, Ordering::Relaxed) as u16 + BASE_PORT; SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), port)) From ee5f63f88885ce7b585c1d016a81f0e78d57646b Mon Sep 17 00:00:00 2001 From: lokirithm Date: Wed, 13 May 2026 01:15:59 +0530 Subject: [PATCH 020/174] Move test files from issues/ to appropriate subdirectories --- .../sized-recursive-type-via-associated-type.rs} | 0 .../correctly-monomorphize-generic-drop-impl.rs} | 0 .../dont-roundup-dst-prefix-size-to-alignment.rs} | 0 .../size_of-requires-type-annotation-in-const.rs} | 0 .../size_of-requires-type-annotation-in-const.stderr} | 0 .../issue-46855.rs => mir/dont-use-operand-as-place-for-zst.rs} | 0 .../validate-various-comparison-behavior.rs} | 0 7 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-31299.rs => associated-types/sized-recursive-type-via-associated-type.rs} (100%) rename tests/ui/{issues/issue-27997.rs => codegen/correctly-monomorphize-generic-drop-impl.rs} (100%) rename tests/ui/{issues/issue-35815.rs => codegen/dont-roundup-dst-prefix-size-to-alignment.rs} (100%) rename tests/ui/{issues/issue-47486.rs => consts/size_of-requires-type-annotation-in-const.rs} (100%) rename tests/ui/{issues/issue-47486.stderr => consts/size_of-requires-type-annotation-in-const.stderr} (100%) rename tests/ui/{issues/issue-46855.rs => mir/dont-use-operand-as-place-for-zst.rs} (100%) rename tests/ui/{issues/issue-50811.rs => mir/validate-various-comparison-behavior.rs} (100%) diff --git a/tests/ui/issues/issue-31299.rs b/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs similarity index 100% rename from tests/ui/issues/issue-31299.rs rename to tests/ui/associated-types/sized-recursive-type-via-associated-type.rs diff --git a/tests/ui/issues/issue-27997.rs b/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs similarity index 100% rename from tests/ui/issues/issue-27997.rs rename to tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs diff --git a/tests/ui/issues/issue-35815.rs b/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs similarity index 100% rename from tests/ui/issues/issue-35815.rs rename to tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs diff --git a/tests/ui/issues/issue-47486.rs b/tests/ui/consts/size_of-requires-type-annotation-in-const.rs similarity index 100% rename from tests/ui/issues/issue-47486.rs rename to tests/ui/consts/size_of-requires-type-annotation-in-const.rs diff --git a/tests/ui/issues/issue-47486.stderr b/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr similarity index 100% rename from tests/ui/issues/issue-47486.stderr rename to tests/ui/consts/size_of-requires-type-annotation-in-const.stderr diff --git a/tests/ui/issues/issue-46855.rs b/tests/ui/mir/dont-use-operand-as-place-for-zst.rs similarity index 100% rename from tests/ui/issues/issue-46855.rs rename to tests/ui/mir/dont-use-operand-as-place-for-zst.rs diff --git a/tests/ui/issues/issue-50811.rs b/tests/ui/mir/validate-various-comparison-behavior.rs similarity index 100% rename from tests/ui/issues/issue-50811.rs rename to tests/ui/mir/validate-various-comparison-behavior.rs From 664af6990b226f1193e9b7d207baa721042d451c Mon Sep 17 00:00:00 2001 From: lokirithm Date: Wed, 13 May 2026 01:26:32 +0530 Subject: [PATCH 021/174] Update files --- .../sized-recursive-type-via-associated-type.rs | 4 ++-- tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs | 1 + tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs | 1 + tests/ui/consts/size_of-requires-type-annotation-in-const.rs | 1 + .../consts/size_of-requires-type-annotation-in-const.stderr | 4 ++-- tests/ui/mir/dont-use-operand-as-place-for-zst.rs | 1 + tests/ui/mir/validate-various-comparison-behavior.rs | 1 + 7 files changed, 9 insertions(+), 4 deletions(-) diff --git a/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs b/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs index b01b73bf373e8..2c2a3820d42d0 100644 --- a/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs +++ b/tests/ui/associated-types/sized-recursive-type-via-associated-type.rs @@ -1,6 +1,6 @@ +//! Regression test for //@ run-pass -// Regression test for #31299. This was generating an overflow error -// because of eager normalization: +// This was generating an overflow error because of eager normalization: // // proving `M: Sized` requires // - proving `PtrBack>: Sized` requires diff --git a/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs b/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs index 85317cec061ad..1a81632970c97 100644 --- a/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs +++ b/tests/ui/codegen/correctly-monomorphize-generic-drop-impl.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass use std::sync::atomic::{Ordering, AtomicUsize}; diff --git a/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs b/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs index 1a09d8041e459..19bf627e6ddf2 100644 --- a/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs +++ b/tests/ui/codegen/dont-roundup-dst-prefix-size-to-alignment.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![allow(dead_code)] use std::mem; diff --git a/tests/ui/consts/size_of-requires-type-annotation-in-const.rs b/tests/ui/consts/size_of-requires-type-annotation-in-const.rs index d686f02a9fe39..3e4a9fd076b47 100644 --- a/tests/ui/consts/size_of-requires-type-annotation-in-const.rs +++ b/tests/ui/consts/size_of-requires-type-annotation-in-const.rs @@ -1,3 +1,4 @@ +//! Regression test for fn main() { () < std::mem::size_of::<_>(); //~ ERROR: mismatched types [0u8; std::mem::size_of::<_>()]; //~ ERROR: type annotations needed diff --git a/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr b/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr index c7e9af70e64a7..3ca15af5e1782 100644 --- a/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr +++ b/tests/ui/consts/size_of-requires-type-annotation-in-const.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-47486.rs:2:10 + --> $DIR/size_of-requires-type-annotation-in-const.rs:3:10 | LL | () < std::mem::size_of::<_>(); | -- ^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `usize` @@ -7,7 +7,7 @@ LL | () < std::mem::size_of::<_>(); | expected because this is `()` error[E0282]: type annotations needed - --> $DIR/issue-47486.rs:3:11 + --> $DIR/size_of-requires-type-annotation-in-const.rs:4:11 | LL | [0u8; std::mem::size_of::<_>()]; | ^^^^^^^^^^^^^^^^^^^^^^ cannot infer type of the type parameter `T` declared on the function `size_of` diff --git a/tests/ui/mir/dont-use-operand-as-place-for-zst.rs b/tests/ui/mir/dont-use-operand-as-place-for-zst.rs index acea242046fde..0d1d12d5b404c 100644 --- a/tests/ui/mir/dont-use-operand-as-place-for-zst.rs +++ b/tests/ui/mir/dont-use-operand-as-place-for-zst.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![allow(dead_code)] //@ compile-flags: -Zmir-opt-level=1 diff --git a/tests/ui/mir/validate-various-comparison-behavior.rs b/tests/ui/mir/validate-various-comparison-behavior.rs index aaf1c17f59b5f..c1aaa4c35ae15 100644 --- a/tests/ui/mir/validate-various-comparison-behavior.rs +++ b/tests/ui/mir/validate-various-comparison-behavior.rs @@ -1,3 +1,4 @@ +//! Regression test for //@ run-pass #![feature(test)] #![allow(invalid_nan_comparisons)] From 2e70d68b630954afc27032570e0389af39868203 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 13 May 2026 06:03:39 +0000 Subject: [PATCH 022/174] Prepare for merging from rust-lang/rust This updates the rust-version file to c8c4c83d57d3b4cf72a7306cd77bb8b5c0f03434. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 4bb9513424511..6e25d8deaf25d 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -63b1dfc0e00fd6f8ad7cd8817fc712e7d9b7be59 +c8c4c83d57d3b4cf72a7306cd77bb8b5c0f03434 From 0fbd5a7f2ce1f1484a43f297f86e9959226fde71 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Wed, 13 May 2026 06:12:09 +0000 Subject: [PATCH 023/174] fmt --- src/tools/miri/src/shims/native_lib/mod.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/src/shims/native_lib/mod.rs b/src/tools/miri/src/shims/native_lib/mod.rs index c66082583b3e4..27571a9eb7c87 100644 --- a/src/tools/miri/src/shims/native_lib/mod.rs +++ b/src/tools/miri/src/shims/native_lib/mod.rs @@ -381,7 +381,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { let mut fields = vec![]; for field in &adt_def.non_enum_variant().fields { - let layout = this.layout_of(field.ty(*this.tcx, args).skip_norm_wip()).map_err(|_err| orig_ty)?; + let layout = this + .layout_of(field.ty(*this.tcx, args).skip_norm_wip()) + .map_err(|_err| orig_ty)?; fields.push(this.ty_to_ffitype(layout)?); } From c07430cbe35247e002f770f0f9ce55c2546fff08 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Tue, 12 May 2026 13:44:03 +0200 Subject: [PATCH 024/174] feat: finish TTL socket option, add nodelay socket option --- src/tools/miri/src/shims/unix/socket.rs | 102 ++++++++++++++++++++-- src/tools/miri/tests/pass/shims/socket.rs | 25 +++++- 2 files changed, 118 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index f1aa8e6bfce23..4673a40d86d3d 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -895,6 +895,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let socket = this.read_scalar(socket)?.to_i32()?; let level = this.read_scalar(level)?.to_i32()?; let option_name = this.read_scalar(option_name)?.to_i32()?; + let option_value_ptr = this.read_pointer(option_value)?; let socklen_layout = this.libc_ty_layout("socklen_t"); let option_len = this.read_scalar(option_len)?.to_int(socklen_layout.size)?; @@ -903,7 +904,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EBADF")); }; - let Some(_socket) = fd.downcast::() else { + let Some(socket) = fd.downcast::() else { // Man page specifies to return ENOTSOCK if `fd` is not a socket. return this.set_last_error_and_return_i32(LibcError("ENOTSOCK")); }; @@ -921,7 +922,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EINVAL")); } let option_value = - this.deref_pointer_as(option_value, this.machine.layouts.i32)?; + this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); let _val = this.read_scalar(&option_value)?.to_i32()?; // We entirely ignore this value since we do not support signals anyway. @@ -934,7 +935,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Option value should be C-int which is usually 4 bytes. return this.set_last_error_and_return_i32(LibcError("EINVAL")); } - let option_value = this.deref_pointer_as(option_value, this.machine.layouts.i32)?; + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); let _val = this.read_scalar(&option_value)?.to_i32()?; // We entirely ignore this: std always sets REUSEADDR for us, and in the end it's more of a // hint to bypass some arbitrary timeout anyway. @@ -944,10 +945,72 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "setsockopt: option {option_name:#x} is unsupported for level SOL_SOCKET", ); } + } else if level == this.eval_libc_i32("IPPROTO_IP") { + let opt_ip_ttl = this.eval_libc_i32("IP_TTL"); + + if option_name == opt_ip_ttl { + if option_len != 4 { + // Option value should be C-uint which is usually 4 bytes. + return this.set_last_error_and_return_i32(LibcError("EINVAL")); + } + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.u32); + let ttl = this.read_scalar(&option_value)?.to_u32()?; + + let result = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) => + throw_unsup_format!( + "setsockopt: setting option IP_TTL on level IPPROTO_IP is only supported \ + on connected and listening sockets" + ), + SocketState::Listening(listener) => listener.set_ttl(ttl), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.set_ttl(ttl), + }; + + return match result { + Ok(_) => interp_ok(Scalar::from_i32(0)), + Err(e) => this.set_last_error_and_return_i32(e), + }; + } else { + throw_unsup_format!( + "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP", + ); + } + } else if level == this.eval_libc_i32("IPPROTO_TCP") { + let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY"); + + if option_name == opt_tcp_nodelay { + if option_len != 4 { + // Option value should be C-int which is usually 4 bytes. + return this.set_last_error_and_return_i32(LibcError("EINVAL")); + } + let option_value = this.ptr_to_mplace(option_value_ptr, this.machine.layouts.i32); + let nodelay = this.read_scalar(&option_value)?.to_i32()? != 0; + + let result = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => + throw_unsup_format!( + "setsockopt: setting option TCP_NODELAY on level IPPROTO_TCP is only supported \ + on connected sockets" + ), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.set_nodelay(nodelay), + }; + + return match result { + Ok(_) => interp_ok(Scalar::from_i32(0)), + Err(e) => this.set_last_error_and_return_i32(e), + }; + } else { + throw_unsup_format!( + "setsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP" + ); + } } throw_unsup_format!( - "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed" + "setsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \ + and IPPROTO_TCP are allowed" ); } @@ -1065,9 +1128,38 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_IP", ); } + } else if level == this.eval_libc_i32("IPPROTO_TCP") { + let opt_tcp_nodelay = this.eval_libc_i32("TCP_NODELAY"); + + if option_name == opt_tcp_nodelay { + let nodelay = match &*socket.state.borrow() { + SocketState::Initial | SocketState::Bound(_) | SocketState::Listening(_) => + throw_unsup_format!( + "getsockopt: reading option TCP_NODELAY on level IPPROTO_TCP is only supported \ + on connected sockets" + ), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.nodelay(), + }; + + let nodelay = match nodelay { + Ok(nodelay) => nodelay, + Err(e) => return this.set_last_error_and_return_i32(e), + }; + + // Allocate new buffer on the stack with the `i32` layout. + let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?; + this.write_int(i32::from(nodelay), &value_buffer)?; + value_buffer + } else { + throw_unsup_format!( + "getsockopt: option {option_name:#x} is unsupported for level IPPROTO_TCP" + ); + } } else { throw_unsup_format!( - "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET is allowed" + "getsockopt: level {level:#x} is unsupported, only SOL_SOCKET, IPPROTO_IP \ + and IPPROTO_TCP are allowed" ) }; diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 4f448fa44780b..4655579619f2e 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -16,6 +16,7 @@ fn main() { test_peer_addr(); test_shutdown(); test_sockopt_ttl(); + test_sockopt_nodelay(); } fn test_create_ipv4_listener() { @@ -119,8 +120,6 @@ fn test_peer_addr() { /// Test shutting down TCP streams. fn test_shutdown() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - // Get local address with randomized port to know where - // we need to connect to. let address = listener.local_addr().unwrap(); // Start server thread. @@ -157,7 +156,25 @@ fn test_shutdown() { /// Test setting and reading the TTL socket option. fn test_sockopt_ttl() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); - listener.ttl().unwrap(); + listener.set_ttl(16).unwrap(); + assert_eq!(listener.ttl().unwrap(), 16); +} - // TODO: Once we support setting the TTL we should also test it here. +/// Test setting and reading the TCP nodelay socket option. +fn test_sockopt_nodelay() { + let listener = TcpListener::bind("127.0.0.1:0").unwrap(); + let address = listener.local_addr().unwrap(); + + // Start server thread. + let handle = thread::spawn(move || { + listener.accept().unwrap(); + }); + + let stream = TcpStream::connect(address).unwrap(); + stream.set_nodelay(true).unwrap(); + assert_eq!(stream.nodelay().unwrap(), true); + stream.set_nodelay(false).unwrap(); + assert_eq!(stream.nodelay().unwrap(), false); + + handle.join().unwrap(); } From 716b62ded326d479d882944ee23dd2f5b7442de5 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 May 2026 08:58:06 +0200 Subject: [PATCH 025/174] turns out we can do 'connect; accept' in a single thread --- .../miri/tests/pass-dep/libc/libc-socket.rs | 18 +++--------------- src/tools/miri/tests/pass/shims/socket.rs | 19 ++++--------------- 2 files changed, 7 insertions(+), 30 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index d9200c818608c..e067f006325d9 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -425,10 +425,8 @@ fn test_getsockname_ipv4_connect() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); - net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); let (_, sock_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getsockname(client_sockfd, storage, len) @@ -443,8 +441,6 @@ fn test_getsockname_ipv4_connect() { assert_eq!(addr.sin_family, sock_addr.sin_family); assert_ne!(addr.sin_addr.s_addr, sock_addr.sin_addr.s_addr); assert!(sock_addr.sin_port > 0); - - server_thread.join().unwrap(); } /// Test the `getsockname` syscall on an IPv6 socket which is bound. @@ -485,10 +481,8 @@ fn test_getpeername_ipv4() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv4(server_sockfd).unwrap()); - net::connect_ipv4(client_sockfd, addr).unwrap(); + net::accept_ipv4(server_sockfd).unwrap(); let (_, peer_addr) = net::sockname_ipv4(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -498,8 +492,6 @@ fn test_getpeername_ipv4() { assert_eq!(addr.sin_family, peer_addr.sin_family); assert_eq!(addr.sin_port, peer_addr.sin_port); assert_eq!(addr.sin_addr.s_addr, peer_addr.sin_addr.s_addr); - - server_thread.join().unwrap(); } /// Test the `getpeername` syscall on an IPv6 socket. @@ -510,10 +502,8 @@ fn test_getpeername_ipv6() { let client_sockfd = unsafe { errno_result(libc::socket(libc::AF_INET6, libc::SOCK_STREAM, 0)).unwrap() }; - // Spawn the server thread. - let server_thread = thread::spawn(move || net::accept_ipv6(server_sockfd).unwrap()); - net::connect_ipv6(client_sockfd, addr).unwrap(); + net::accept_ipv6(server_sockfd).unwrap(); let (_, peer_addr) = net::sockname_ipv6(|storage, len| unsafe { libc::getpeername(client_sockfd, storage, len) @@ -525,8 +515,6 @@ fn test_getpeername_ipv6() { assert_eq!(addr.sin6_flowinfo, peer_addr.sin6_flowinfo); assert_eq!(addr.sin6_scope_id, peer_addr.sin6_scope_id); assert_eq!(addr.sin6_addr.s6_addr, peer_addr.sin6_addr.s6_addr); - - server_thread.join().unwrap(); } /// Test shutting down TCP streams. diff --git a/src/tools/miri/tests/pass/shims/socket.rs b/src/tools/miri/tests/pass/shims/socket.rs index 4655579619f2e..335df02b54f82 100644 --- a/src/tools/miri/tests/pass/shims/socket.rs +++ b/src/tools/miri/tests/pass/shims/socket.rs @@ -27,21 +27,15 @@ fn test_create_ipv6_listener() { let _listener_ipv6 = TcpListener::bind("[::1]:0").unwrap(); } -/// Try to connect to a TCP listener running in a separate thread and -/// accepting connections. +/// Try to connect to a TCP listener and accepting connections. fn test_accept_and_connect() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); // Get local address with randomized port to know where // we need to connect to. let address = listener.local_addr().unwrap(); - let handle = thread::spawn(move || { - let (_stream, _addr) = listener.accept().unwrap(); - }); - let _stream = TcpStream::connect(address).unwrap(); - - handle.join().unwrap(); + let (_other_stream, _addr) = listener.accept().unwrap(); } /// Test reading and writing into two connected sockets and ensuring @@ -165,16 +159,11 @@ fn test_sockopt_nodelay() { let listener = TcpListener::bind("127.0.0.1:0").unwrap(); let address = listener.local_addr().unwrap(); - // Start server thread. - let handle = thread::spawn(move || { - listener.accept().unwrap(); - }); - let stream = TcpStream::connect(address).unwrap(); + let _other_end = listener.accept().unwrap(); + stream.set_nodelay(true).unwrap(); assert_eq!(stream.nodelay().unwrap(), true); stream.set_nodelay(false).unwrap(); assert_eq!(stream.nodelay().unwrap(), false); - - handle.join().unwrap(); } From a0095ae6fda2e698b16cbce7d141669246e3eb62 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Wed, 13 May 2026 12:17:06 +0200 Subject: [PATCH 026/174] Fix last remnants of TB's Active -> Unique renaming --- .../src/borrow_tracker/tree_borrows/diagnostics.rs | 12 ++++++------ .../miri/src/borrow_tracker/tree_borrows/perms.rs | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs index 16e89ee460de0..9d4c446b14f85 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/diagnostics.rs @@ -456,7 +456,7 @@ struct DisplayFmtWrapper { /// will show each permission line as /// ```text /// 0.. 1.. 2.. 3.. 4.. 5 -/// [Act|Res|Frz|Dis|___] +/// [Unq|Res|Frz|Dis|___] /// ``` struct DisplayFmtPermission { /// Text that starts the permission block. @@ -467,7 +467,7 @@ struct DisplayFmtPermission { close: S, /// Text to show when a permission is not initialized. /// Should have the same width as a `Permission`'s `.short_name()`, i.e. - /// 3 if using the `Res/Act/Frz/Dis` notation. + /// 3 if using the `Res/Unq/Frz/Dis` notation. uninit: S, /// Text to separate the `start` and `end` values of a range. range_sep: S, @@ -525,7 +525,7 @@ struct DisplayFmtPadding { /// ``` /// will show states as /// ```text -/// Act +/// Unq /// ?Res /// ____ /// ``` @@ -549,8 +549,8 @@ struct DisplayFmt { } impl DisplayFmt { /// Print the permission with the format - /// ` Res`/` Re*`/` Act`/` Frz`/` Dis` for accessed locations - /// and `?Res`/`?Re*`/`?Act`/`?Frz`/`?Dis` for unaccessed locations. + /// ` Res`/` Re*`/` Unq`/` Frz`/` Dis` for accessed locations + /// and `?Res`/`?Re*`/`?Unq`/`?Frz`/`?Dis` for unaccessed locations. fn print_perm(&self, perm: Option) -> String { if let Some(perm) = perm { format!( @@ -801,7 +801,7 @@ impl DisplayRepr { ) { let mut line = String::new(); // Format the permissions on each range. - // Looks like `| Act| Res| Res| Act|`. + // Looks like `| Unq| Res| Res| Unq|`. line.push_str(fmt.perm.open); for (i, (perm, &pad)) in tree.rperm.iter().zip(padding.iter()).enumerate() { if i > 0 { diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs index aa15b9e1b1188..0dd4712b85c7d 100644 --- a/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs +++ b/src/tools/miri/src/borrow_tracker/tree_borrows/perms.rs @@ -462,7 +462,7 @@ pub mod diagnostics { ReservedFrz { conflicted: false } => "Res ", ReservedFrz { conflicted: true } => "ResC", ReservedIM => "ReIM", - Unique => "Act ", + Unique => "Unq ", Frozen => "Frz ", Disabled => "Dis ", } From 0a9758702b180b63b00ac0551250bc01662dd185 Mon Sep 17 00:00:00 2001 From: Johannes Hostert Date: Wed, 13 May 2026 12:37:38 +0200 Subject: [PATCH 027/174] Adjust tests --- .../cell-inside-struct.tree.stderr | 2 +- ...-inside-struct.tree_implicit_writes.stderr | 2 +- .../reserved/cell-protected-write.stderr | 2 +- .../reserved/int-protected-write.stderr | 2 +- .../cell-alternate-writes.tree.stderr | 6 ++--- ...ternate-writes.tree_implicit_writes.stderr | 6 ++--- .../tree_borrows/cell-inside-box.tree.stderr | 4 ++-- ...ell-inside-box.tree_implicit_writes.stderr | 4 ++-- .../cell-inside-struct.tree.stderr | 2 +- ...-inside-struct.tree_implicit_writes.stderr | 2 +- .../tree_borrows/end-of-protector.tree.stderr | 12 +++++----- ...d-of-protector.tree_implicit_writes.stderr | 22 +++++++++---------- .../tests/pass/tree_borrows/formatting.stderr | 12 +++++----- .../implicit-writes-permissions.stderr | 8 +++---- .../tree_borrows/reborrow-is-read.tree.stderr | 10 ++++----- ...borrow-is-read.tree_implicit_writes.stderr | 10 ++++----- .../tests/pass/tree_borrows/reserved.stderr | 20 ++++++++--------- .../tree_borrows/wildcard/formatting.stderr | 2 +- 18 files changed, 64 insertions(+), 64 deletions(-) diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr index 282dc19eea791..bdd2241c70707 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4.. 8 -| Act | Act | └─┬── +| Unq | Unq | └─┬── | Frz |?Cel | └──── ────────────────────────────────────────────────── error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden diff --git a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr index 282dc19eea791..bdd2241c70707 100644 --- a/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4.. 8 -| Act | Act | └─┬── +| Unq | Unq | └─┬── | Frz |?Cel | └──── ────────────────────────────────────────────────── error: Undefined Behavior: write access through (a) at ALLOC[0x0] is forbidden diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr index 29397a8fd0bc7..f1bb93354ff90 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/cell-protected-write.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├─┬── | ReIM| │ └─┬── diff --git a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr index 3bddd2ce1de63..c86d70bcba287 100644 --- a/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr +++ b/src/tools/miri/tests/fail/tree_borrows/reserved/int-protected-write.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr index e09aed2cf5d01..6edb1f91c90c2 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── |?Cel | ├──── |?Cel | └──── @@ -9,8 +9,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Cel | ├──── | Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr index e09aed2cf5d01..6edb1f91c90c2 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-alternate-writes.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── |?Cel | ├──── |?Cel | └──── @@ -9,8 +9,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Cel | ├──── | Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr index 5dbfff718b1e6..3a7c55a8dab43 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr index 5dbfff718b1e6..3a7c55a8dab43 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-box.tree_implicit_writes.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 4 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr index 1d939329040fa..c7f7397593d33 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree.stderr @@ -1,6 +1,6 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── |?Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr index 1d939329040fa..c7f7397593d33 100644 --- a/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/cell-inside-struct.tree_implicit_writes.stderr @@ -1,6 +1,6 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── |?Cel | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr index 4d77d96776d31..74abcef5870b1 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree.stderr @@ -1,14 +1,14 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └─┬── | Res | └─┬── @@ -17,7 +17,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── @@ -27,10 +27,10 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├─┬── | Dis | │ └─┬── | Dis | │ └──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr index d33bfcdc0097a..7ab96e3f378d9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/end-of-protector.tree_implicit_writes.stderr @@ -1,24 +1,24 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └──── Strongly protected +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── Strongly protected ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├─┬── | Frz | │ └─┬── | Frz | │ └──── @@ -27,10 +27,10 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├─┬── | Dis | │ └─┬── | Dis | │ └──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/formatting.stderr b/src/tools/miri/tests/pass/tree_borrows/formatting.stderr index 29f99034bab5c..5e4cc7be56bc0 100644 --- a/src/tools/miri/tests/pass/tree_borrows/formatting.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/formatting.stderr @@ -1,17 +1,17 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1.. 2.. 10.. 11.. 100.. 101..1000..1001..1024 -| Act | Act | Act | Act | Act | Act | Act | Act | Act | └─┬── -| Res | Act | Res | Act | Res | Act | Res | Act | Res | └─┬── -|-----| Act |-----|?Dis |-----|?Dis |-----|?Dis |-----| ├──── -|-----|-----|-----| Act |-----|?Dis |-----|?Dis |-----| ├──── +| Unq | Unq | Unq | Unq | Unq | Unq | Unq | Unq | Unq | └─┬── +| Res | Unq | Res | Unq | Res | Unq | Res | Unq | Res | └─┬── +|-----| Unq |-----|?Dis |-----|?Dis |-----|?Dis |-----| ├──── +|-----|-----|-----| Unq |-----|?Dis |-----|?Dis |-----| ├──── |-----|-----|-----|-----|-----| Frz |-----|?Dis |-----| ├──── -|-----|-----|-----|-----|-----|-----|-----| Act |-----| └──── +|-----|-----|-----|-----|-----|-----|-----| Unq |-----| └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Frz | └─┬── | Frz | ├─┬── | Frz | │ ├──── diff --git a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr index 1dd871530caad..faccba6b0eead 100644 --- a/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/implicit-writes-permissions.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────── 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └─┬── -| Act | └──── Strongly protected +| Unq | └─┬── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── Strongly protected ────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr index d589a06211182..a1f485208dfa9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree.stderr @@ -1,15 +1,15 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └──── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├──── | Res | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr index d589a06211182..a1f485208dfa9 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reborrow-is-read.tree_implicit_writes.stderr @@ -1,15 +1,15 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── -| Act | └──── +| Unq | └─┬── +| Unq | └─┬── +| Unq | └──── ────────────────────────────────────────────────── ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Frz | ├──── | Res | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr index be90382640b4a..ab173919abf5a 100644 --- a/src/tools/miri/tests/pass/tree_borrows/reserved.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/reserved.stderr @@ -2,7 +2,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├─┬── | ReIM| │ └─┬── @@ -13,7 +13,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── +| Unq | └─┬── | ReIM| └─┬── | ReIM| ├──── | ReIM| └──── @@ -22,16 +22,16 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 8 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | ReIM| ├──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── [protected] Foreign Read: Res -> Frz ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├─┬── | Res | │ └─┬── @@ -42,7 +42,7 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Res | └─┬── | Res | ├──── | Res | └──── @@ -51,8 +51,8 @@ Warning: this tree is indicative only. Some tags may have been hidden. ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── -| Act | └─┬── +| Unq | └─┬── +| Unq | └─┬── | Dis | ├──── -| Act | └──── +| Unq | └──── ────────────────────────────────────────────────── diff --git a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr index 583c84534395c..d671f31d5a802 100644 --- a/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr +++ b/src/tools/miri/tests/pass/tree_borrows/wildcard/formatting.stderr @@ -1,7 +1,7 @@ ────────────────────────────────────────────────── Warning: this tree is indicative only. Some tags may have been hidden. 0.. 1 -| Act | └─┬── +| Unq | └─┬── | Frz | └─┬── | Frz | ├──── | Frz | └──── (exposed) From cbcae88d9f81596961726b0c01f293e750e14f0f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Wed, 13 May 2026 14:08:37 +0200 Subject: [PATCH 028/174] update test_flock comments --- src/tools/miri/tests/pass/shims/fs.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index e0da9f63876f1..cd3ae9f7c3287 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -400,7 +400,10 @@ fn test_pread_pwrite() { assert_eq!(&buf1, b" m"); } -// The standard library does not support this operation on Solaris, Android +// The standard library does not support this operation on Android +// (https://github.com/rust-lang/rust/issues/148325). +// Miri does not support the way this is implemented on Solaris +// (https://github.com/rust-lang/miri/issues/5038). #[cfg(not(any(target_os = "solaris", target_os = "android")))] fn test_flock() { let bytes = b"Hello, World!\n"; From b502d55288e4f8898fb851bd279c9797b4171004 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Wed, 13 May 2026 14:03:45 +0100 Subject: [PATCH 029/174] move closures related ui tests into its folder --- .../boxed-closure-captures-fnmut-with-ref.rs | 21 +++++++++++++++++++ .../boxed-closure-sent-through-thread.rs} | 0 .../boxed-closure-with-borrowed-param.rs} | 0 .../ui/closures/call-boxed-closure-no-args.rs | 9 ++++++++ ...closure-through-lifetime-generic-struct.rs | 10 +++++++++ .../call-fnmut-trait-object-via-ref-mut.rs} | 0 ...re-kind-caching-during-upvar-inference.rs} | 0 ...ind-caching-during-upvar-inference.stderr} | 0 .../closure-to-fn-pointer-lifetime-error.rs} | 0 ...osure-to-fn-pointer-lifetime-error.stderr} | 0 .../closure-with-fixed-size-array-param.rs} | 0 .../debug-info-for-closure-capture.rs} | 0 .../closures/destructure-newtype-closure.rs | 7 +++++++ .../drop-glue-for-closure-with-captures.rs} | 0 ...explicit-return-in-closure-and-outer-fn.rs | 10 +++++++++ .../fn-sugar-in-boxed-trait-object.rs | 8 +++++++ ...tb-closure-in-recursive-enum-type-error.rs | 20 ++++++++++++++++++ ...losure-in-recursive-enum-type-error.stderr | 14 +++++++++++++ .../module-path-not-confused-with-upvar.rs} | 0 .../move-closure-can-call-captured-fnmut.rs | 20 ++++++++++++++++++ tests/ui/closures/move-closure-is-send.rs | 18 ++++++++++++++++ .../no-unused-mut-warning-for-move-closure.rs | 12 +++++++++++ .../pass-lifetime-fn-to-generic-fnonce.rs | 9 ++++++++ ...ref-binding-lifetime-in-closure-pattern.rs | 9 ++++++++ ...ion-obligations-closure-and-projection.rs} | 0 .../unique-closure-type-mismatch.rs} | 0 .../unique-closure-type-mismatch.stderr} | 0 27 files changed, 167 insertions(+) create mode 100644 tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs rename tests/ui/{issues/issue-3609.rs => closures/boxed-closure-sent-through-thread.rs} (100%) rename tests/ui/{issues/issue-3424.rs => closures/boxed-closure-with-borrowed-param.rs} (100%) create mode 100644 tests/ui/closures/call-boxed-closure-no-args.rs create mode 100644 tests/ui/closures/call-closure-through-lifetime-generic-struct.rs rename tests/ui/{issues/issue-32389.rs => closures/call-fnmut-trait-object-via-ref-mut.rs} (100%) rename tests/ui/{issues/issue-34349.rs => closures/closure-kind-caching-during-upvar-inference.rs} (100%) rename tests/ui/{issues/issue-34349.stderr => closures/closure-kind-caching-during-upvar-inference.stderr} (100%) rename tests/ui/{issues/issue-40000.rs => closures/closure-to-fn-pointer-lifetime-error.rs} (100%) rename tests/ui/{issues/issue-40000.stderr => closures/closure-to-fn-pointer-lifetime-error.stderr} (100%) rename tests/ui/{issues/issue-28181.rs => closures/closure-with-fixed-size-array-param.rs} (100%) rename tests/ui/{issues/issue-26484.rs => closures/debug-info-for-closure-capture.rs} (100%) create mode 100644 tests/ui/closures/destructure-newtype-closure.rs rename tests/ui/{issues/issue-36260.rs => closures/drop-glue-for-closure-with-captures.rs} (100%) create mode 100644 tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs create mode 100644 tests/ui/closures/fn-sugar-in-boxed-trait-object.rs create mode 100644 tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs create mode 100644 tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr rename tests/ui/{issues/issue-29522.rs => closures/module-path-not-confused-with-upvar.rs} (100%) create mode 100644 tests/ui/closures/move-closure-can-call-captured-fnmut.rs create mode 100644 tests/ui/closures/move-closure-is-send.rs create mode 100644 tests/ui/closures/no-unused-mut-warning-for-move-closure.rs create mode 100644 tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs create mode 100644 tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs rename tests/ui/{issues/issue-46069.rs => closures/region-obligations-closure-and-projection.rs} (100%) rename tests/ui/{issues/issue-24036.rs => closures/unique-closure-type-mismatch.rs} (100%) rename tests/ui/{issues/issue-24036.stderr => closures/unique-closure-type-mismatch.stderr} (100%) diff --git a/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs new file mode 100644 index 0000000000000..b3b008229a53a --- /dev/null +++ b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs @@ -0,0 +1,21 @@ +//@ check-pass + +pub trait Promisable: Send + Sync {} +impl Promisable for T {} + +pub fn propagate<'a, T, E, F, G>(mut action: F) + -> Box) -> Result + 'a> + where + T: Promisable + Clone + 'a, + E: Promisable + Clone + 'a, + F: FnMut(&T) -> Result + Send + 'a, + G: FnMut(Result) -> Result + 'a { + Box::new(move |result: Result| { + match result { + Ok(ref t) => action(t), + Err(ref e) => Err(e.clone()), + } + }) +} + +fn main() {} diff --git a/tests/ui/issues/issue-3609.rs b/tests/ui/closures/boxed-closure-sent-through-thread.rs similarity index 100% rename from tests/ui/issues/issue-3609.rs rename to tests/ui/closures/boxed-closure-sent-through-thread.rs diff --git a/tests/ui/issues/issue-3424.rs b/tests/ui/closures/boxed-closure-with-borrowed-param.rs similarity index 100% rename from tests/ui/issues/issue-3424.rs rename to tests/ui/closures/boxed-closure-with-borrowed-param.rs diff --git a/tests/ui/closures/call-boxed-closure-no-args.rs b/tests/ui/closures/call-boxed-closure-no-args.rs new file mode 100644 index 0000000000000..b213b79d37cab --- /dev/null +++ b/tests/ui/closures/call-boxed-closure-no-args.rs @@ -0,0 +1,9 @@ +//@ run-pass +// Test that overloaded calls work with zero arity closures + + +fn main() { + let functions: [Box Option<()>>; 1] = [Box::new(|| None)]; + + let _: Option> = functions.iter().map(|f| (*f)()).collect(); +} diff --git a/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs new file mode 100644 index 0000000000000..da28a14685f6c --- /dev/null +++ b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs @@ -0,0 +1,10 @@ +//@ run-pass +#![allow(unused_variables)] +use std::marker::PhantomData; + +fn main() { + struct Symbol<'a, F: Fn(Vec<&'a str>) -> &'a str> { function: F, marker: PhantomData<&'a ()> } + let f = |x: Vec<&str>| -> &str { "foobar" }; + let sym = Symbol { function: f, marker: PhantomData }; + (sym.function)(vec![]); +} diff --git a/tests/ui/issues/issue-32389.rs b/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs similarity index 100% rename from tests/ui/issues/issue-32389.rs rename to tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs diff --git a/tests/ui/issues/issue-34349.rs b/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs similarity index 100% rename from tests/ui/issues/issue-34349.rs rename to tests/ui/closures/closure-kind-caching-during-upvar-inference.rs diff --git a/tests/ui/issues/issue-34349.stderr b/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr similarity index 100% rename from tests/ui/issues/issue-34349.stderr rename to tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr diff --git a/tests/ui/issues/issue-40000.rs b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs similarity index 100% rename from tests/ui/issues/issue-40000.rs rename to tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs diff --git a/tests/ui/issues/issue-40000.stderr b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr similarity index 100% rename from tests/ui/issues/issue-40000.stderr rename to tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr diff --git a/tests/ui/issues/issue-28181.rs b/tests/ui/closures/closure-with-fixed-size-array-param.rs similarity index 100% rename from tests/ui/issues/issue-28181.rs rename to tests/ui/closures/closure-with-fixed-size-array-param.rs diff --git a/tests/ui/issues/issue-26484.rs b/tests/ui/closures/debug-info-for-closure-capture.rs similarity index 100% rename from tests/ui/issues/issue-26484.rs rename to tests/ui/closures/debug-info-for-closure-capture.rs diff --git a/tests/ui/closures/destructure-newtype-closure.rs b/tests/ui/closures/destructure-newtype-closure.rs new file mode 100644 index 0000000000000..7b49fe8c7b472 --- /dev/null +++ b/tests/ui/closures/destructure-newtype-closure.rs @@ -0,0 +1,7 @@ +//@ run-pass +struct GradFn usize>(F); + +fn main() { + let GradFn(x_squared) : GradFn<_> = GradFn(|| -> usize { 2 }); + let _ = x_squared(); +} diff --git a/tests/ui/issues/issue-36260.rs b/tests/ui/closures/drop-glue-for-closure-with-captures.rs similarity index 100% rename from tests/ui/issues/issue-36260.rs rename to tests/ui/closures/drop-glue-for-closure-with-captures.rs diff --git a/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs new file mode 100644 index 0000000000000..710dc0acda7e9 --- /dev/null +++ b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs @@ -0,0 +1,10 @@ +//@ run-pass +#![allow(dead_code)] + +// This used to cause an ICE because the retslot for the "return" had the wrong type +fn testcase<'a>() -> Box + 'a> { + return Box::new((0..3).map(|i| { return i; })); +} + +fn main() { +} diff --git a/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs new file mode 100644 index 0000000000000..dbb560a199bf7 --- /dev/null +++ b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs @@ -0,0 +1,8 @@ +//@ run-pass +fn action(mut cb: Box usize>) -> usize { + cb(1) +} + +pub fn main() { + println!("num: {}", action(Box::new(move |u| u))); +} diff --git a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs new file mode 100644 index 0000000000000..a68369616d8b6 --- /dev/null +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs @@ -0,0 +1,20 @@ +pub enum Expr<'var, VAR> { + Let(Box>, + Box Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>) +} + +pub fn add<'var, VAR> + (a: Expr<'var, VAR>, b: Expr<'var, VAR>) -> Expr<'var, VAR> { + loop {} +} + +pub fn let_<'var, VAR, F: for<'v> Fn(Expr<'v, VAR>) -> Expr<'v, VAR>> + (a: Expr<'var, VAR>, b: F) -> Expr<'var, VAR> { + loop {} +} + +fn main() { + let ex = |x| { //~ ERROR type annotations needed + let_(add(x,x), |y| { + let_(add(x, x), |x|x)})}; +} diff --git a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr new file mode 100644 index 0000000000000..f70ac0c9f388a --- /dev/null +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr @@ -0,0 +1,14 @@ +error[E0282]: type annotations needed for `Expr<'_, _>` + --> $DIR/issue-23046.rs:17:15 + | +LL | let ex = |x| { + | ^ + | +help: consider giving this closure parameter an explicit type, where the type for type parameter `VAR` is specified + | +LL | let ex = |x: Expr<'_, VAR>| { + | +++++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`. diff --git a/tests/ui/issues/issue-29522.rs b/tests/ui/closures/module-path-not-confused-with-upvar.rs similarity index 100% rename from tests/ui/issues/issue-29522.rs rename to tests/ui/closures/module-path-not-confused-with-upvar.rs diff --git a/tests/ui/closures/move-closure-can-call-captured-fnmut.rs b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs new file mode 100644 index 0000000000000..2b2370b0e2b4c --- /dev/null +++ b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs @@ -0,0 +1,20 @@ +//@ check-pass +#![allow(dead_code)] +struct Parser<'a, I, O> { + parse: Box Result + 'a> +} + +impl<'a, I: 'a, O: 'a> Parser<'a, I, O> { + fn compose(mut self, mut rhs: Parser<'a, O, K>) -> Parser<'a, I, K> { + Parser { + parse: Box::new(move |x: I| { + match (self.parse)(x) { + Ok(r) => (rhs.parse)(r), + Err(e) => Err(e) + } + }) + } + } +} + +fn main() {} diff --git a/tests/ui/closures/move-closure-is-send.rs b/tests/ui/closures/move-closure-is-send.rs new file mode 100644 index 0000000000000..d9a7aa9101d3f --- /dev/null +++ b/tests/ui/closures/move-closure-is-send.rs @@ -0,0 +1,18 @@ +//@ run-pass +#![allow(unused_variables)] +//@ needs-threads + +use std::thread; +use std::mem; + +fn main() { + let y = 0u8; + let closure = move |x: u8| y + x; + + // Check that both closures are capturing by value + assert_eq!(1, mem::size_of_val(&closure)); + + thread::spawn(move|| { + let ok = closure; + }).join().ok().unwrap(); +} diff --git a/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs new file mode 100644 index 0000000000000..f7f4f4348afab --- /dev/null +++ b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs @@ -0,0 +1,12 @@ +//@ run-pass + +#![deny(warnings)] + +fn foo(_f: F) { } + +fn main() { + let mut var = Vec::new(); + foo(move|| { + var.push(1); + }); +} diff --git a/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs new file mode 100644 index 0000000000000..2172c631b841d --- /dev/null +++ b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs @@ -0,0 +1,9 @@ +//@ run-pass +#![allow(unused_variables)] + +fn foo T>(f: F) {} +fn id<'a>(input: &'a u8) -> &'a u8 { input } + +fn main() { + foo(id); +} diff --git a/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs new file mode 100644 index 0000000000000..fa3988e099945 --- /dev/null +++ b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs @@ -0,0 +1,9 @@ +//@ check-pass + +fn cb<'a,T>(_x: Box, bool))) -> T>) -> T { + panic!() +} + +fn main() { + cb(Box::new(|(k, &(ref v, b))| (*k, v.clone(), b))); +} diff --git a/tests/ui/issues/issue-46069.rs b/tests/ui/closures/region-obligations-closure-and-projection.rs similarity index 100% rename from tests/ui/issues/issue-46069.rs rename to tests/ui/closures/region-obligations-closure-and-projection.rs diff --git a/tests/ui/issues/issue-24036.rs b/tests/ui/closures/unique-closure-type-mismatch.rs similarity index 100% rename from tests/ui/issues/issue-24036.rs rename to tests/ui/closures/unique-closure-type-mismatch.rs diff --git a/tests/ui/issues/issue-24036.stderr b/tests/ui/closures/unique-closure-type-mismatch.stderr similarity index 100% rename from tests/ui/issues/issue-24036.stderr rename to tests/ui/closures/unique-closure-type-mismatch.stderr From 3429bdf127dd9cb5a8a068f547c24ee62dc855f3 Mon Sep 17 00:00:00 2001 From: danieljofficial Date: Wed, 13 May 2026 15:03:47 +0100 Subject: [PATCH 030/174] add issue links and bless --- .../boxed-closure-captures-fnmut-with-ref.rs | 2 ++ .../boxed-closure-sent-through-thread.rs | 2 ++ .../boxed-closure-with-borrowed-param.rs | 2 ++ .../ui/closures/call-boxed-closure-no-args.rs | 2 ++ ...closure-through-lifetime-generic-struct.rs | 2 ++ .../call-fnmut-trait-object-via-ref-mut.rs | 2 ++ ...ure-kind-caching-during-upvar-inference.rs | 2 ++ ...kind-caching-during-upvar-inference.stderr | 4 ++-- .../closure-to-fn-pointer-lifetime-error.rs | 2 ++ ...losure-to-fn-pointer-lifetime-error.stderr | 4 ++-- .../closure-with-fixed-size-array-param.rs | 2 ++ .../debug-info-for-closure-capture.rs | 2 ++ .../closures/destructure-newtype-closure.rs | 2 ++ .../drop-glue-for-closure-with-captures.rs | 2 ++ ...explicit-return-in-closure-and-outer-fn.rs | 2 ++ .../fn-sugar-in-boxed-trait-object.rs | 2 ++ ...tb-closure-in-recursive-enum-type-error.rs | 2 ++ ...losure-in-recursive-enum-type-error.stderr | 2 +- .../module-path-not-confused-with-upvar.rs | 2 ++ .../move-closure-can-call-captured-fnmut.rs | 2 ++ tests/ui/closures/move-closure-is-send.rs | 2 ++ .../no-unused-mut-warning-for-move-closure.rs | 2 ++ .../pass-lifetime-fn-to-generic-fnonce.rs | 2 ++ ...ref-binding-lifetime-in-closure-pattern.rs | 2 ++ ...gion-obligations-closure-and-projection.rs | 2 ++ .../closures/unique-closure-type-mismatch.rs | 2 ++ .../unique-closure-type-mismatch.stderr | 8 +++---- tests/ui/issues/issue-16560.rs | 18 ---------------- tests/ui/issues/issue-16668.rs | 20 ------------------ tests/ui/issues/issue-16671.rs | 12 ----------- tests/ui/issues/issue-16994.rs | 9 -------- tests/ui/issues/issue-17816.rs | 10 --------- tests/ui/issues/issue-17897.rs | 8 ------- tests/ui/issues/issue-18188.rs | 21 ------------------- tests/ui/issues/issue-19127.rs | 9 -------- tests/ui/issues/issue-20174.rs | 7 ------- tests/ui/issues/issue-20575.rs | 9 -------- tests/ui/issues/issue-22346.rs | 10 --------- tests/ui/issues/issue-23046.rs | 20 ------------------ tests/ui/issues/issue-23046.stderr | 14 ------------- 40 files changed, 55 insertions(+), 176 deletions(-) delete mode 100644 tests/ui/issues/issue-16560.rs delete mode 100644 tests/ui/issues/issue-16668.rs delete mode 100644 tests/ui/issues/issue-16671.rs delete mode 100644 tests/ui/issues/issue-16994.rs delete mode 100644 tests/ui/issues/issue-17816.rs delete mode 100644 tests/ui/issues/issue-17897.rs delete mode 100644 tests/ui/issues/issue-18188.rs delete mode 100644 tests/ui/issues/issue-19127.rs delete mode 100644 tests/ui/issues/issue-20174.rs delete mode 100644 tests/ui/issues/issue-20575.rs delete mode 100644 tests/ui/issues/issue-22346.rs delete mode 100644 tests/ui/issues/issue-23046.rs delete mode 100644 tests/ui/issues/issue-23046.stderr diff --git a/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs index b3b008229a53a..6f2c38843d754 100644 --- a/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs +++ b/tests/ui/closures/boxed-closure-captures-fnmut-with-ref.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/18188 + //@ check-pass pub trait Promisable: Send + Sync {} diff --git a/tests/ui/closures/boxed-closure-sent-through-thread.rs b/tests/ui/closures/boxed-closure-sent-through-thread.rs index a226e5b01362a..14320e0c4ab3c 100644 --- a/tests/ui/closures/boxed-closure-sent-through-thread.rs +++ b/tests/ui/closures/boxed-closure-sent-through-thread.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/3609 + //@ check-pass #![allow(unused_must_use)] #![allow(dead_code)] diff --git a/tests/ui/closures/boxed-closure-with-borrowed-param.rs b/tests/ui/closures/boxed-closure-with-borrowed-param.rs index 4d1a652142032..2d8dd4d800b85 100644 --- a/tests/ui/closures/boxed-closure-with-borrowed-param.rs +++ b/tests/ui/closures/boxed-closure-with-borrowed-param.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/3424 + //@ check-pass #![allow(dead_code)] #![allow(non_camel_case_types)] diff --git a/tests/ui/closures/call-boxed-closure-no-args.rs b/tests/ui/closures/call-boxed-closure-no-args.rs index b213b79d37cab..bf6b73ee6fd57 100644 --- a/tests/ui/closures/call-boxed-closure-no-args.rs +++ b/tests/ui/closures/call-boxed-closure-no-args.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/20575 + //@ run-pass // Test that overloaded calls work with zero arity closures diff --git a/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs index da28a14685f6c..aa2180e3c7f09 100644 --- a/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs +++ b/tests/ui/closures/call-closure-through-lifetime-generic-struct.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/17816 + //@ run-pass #![allow(unused_variables)] use std::marker::PhantomData; diff --git a/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs b/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs index 683c4874e8c20..2f1d29f77f27c 100644 --- a/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs +++ b/tests/ui/closures/call-fnmut-trait-object-via-ref-mut.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/32389 + //@ run-pass fn foo() -> T { loop {} } diff --git a/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs b/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs index d861802610aac..e24e23d49d555 100644 --- a/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs +++ b/tests/ui/closures/closure-kind-caching-during-upvar-inference.rs @@ -1,3 +1,5 @@ +//! Issue: https://github.com/rust-lang/rust/issues/34349 + // This is a regression test for a problem encountered around upvar // inference and trait caching: in particular, we were entering a // temporary closure kind during inference, and then caching results diff --git a/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr b/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr index 6a6188f10c8f8..291ad5960bc21 100644 --- a/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr +++ b/tests/ui/closures/closure-kind-caching-during-upvar-inference.stderr @@ -1,5 +1,5 @@ error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut` - --> $DIR/issue-34349.rs:16:17 + --> $DIR/closure-kind-caching-during-upvar-inference.rs:18:17 | LL | let diary = || { | ^^ this closure implements `FnMut`, not `Fn` @@ -12,7 +12,7 @@ LL | apply(diary); | required by a bound introduced by this call | note: required by a bound in `apply` - --> $DIR/issue-34349.rs:11:32 + --> $DIR/closure-kind-caching-during-upvar-inference.rs:13:32 | LL | fn apply(f: F) where F: Fn() { | ^^^^ required by this bound in `apply` diff --git a/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs index a6e05e7ba02a3..74d5470f89665 100644 --- a/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs +++ b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/40000 + fn main() { let bar: fn(&mut u32) = |_| {}; diff --git a/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr index 0737a9610e294..7466751e43b51 100644 --- a/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr +++ b/tests/ui/closures/closure-to-fn-pointer-lifetime-error.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-40000.rs:6:9 + --> $DIR/closure-to-fn-pointer-lifetime-error.rs:8:9 | LL | foo(bar); | ^^^ one type is more general than the other @@ -8,7 +8,7 @@ LL | foo(bar); found trait object `dyn Fn(&i32)` error[E0308]: mismatched types - --> $DIR/issue-40000.rs:6:9 + --> $DIR/closure-to-fn-pointer-lifetime-error.rs:8:9 | LL | foo(bar); | ^^^ one type is more general than the other diff --git a/tests/ui/closures/closure-with-fixed-size-array-param.rs b/tests/ui/closures/closure-with-fixed-size-array-param.rs index e1cb5ba1c8828..f12df2e5f628a 100644 --- a/tests/ui/closures/closure-with-fixed-size-array-param.rs +++ b/tests/ui/closures/closure-with-fixed-size-array-param.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/28181 + //@ run-pass fn bar(f: F) -> usize where F: Fn([usize; 1]) -> usize { f([2]) } diff --git a/tests/ui/closures/debug-info-for-closure-capture.rs b/tests/ui/closures/debug-info-for-closure-capture.rs index c7053505567ab..2a8db27bda805 100644 --- a/tests/ui/closures/debug-info-for-closure-capture.rs +++ b/tests/ui/closures/debug-info-for-closure-capture.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/26484 + //@ run-pass //@ compile-flags:-g diff --git a/tests/ui/closures/destructure-newtype-closure.rs b/tests/ui/closures/destructure-newtype-closure.rs index 7b49fe8c7b472..f8543e64cb353 100644 --- a/tests/ui/closures/destructure-newtype-closure.rs +++ b/tests/ui/closures/destructure-newtype-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/20174 + //@ run-pass struct GradFn usize>(F); diff --git a/tests/ui/closures/drop-glue-for-closure-with-captures.rs b/tests/ui/closures/drop-glue-for-closure-with-captures.rs index 265b0d2f80217..2ccf4c31a995e 100644 --- a/tests/ui/closures/drop-glue-for-closure-with-captures.rs +++ b/tests/ui/closures/drop-glue-for-closure-with-captures.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/36260 + //@ run-pass // Make sure this compiles without getting a linker error because of missing // drop-glue because the collector missed adding drop-glue for the closure: diff --git a/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs index 710dc0acda7e9..74e0ea345dd62 100644 --- a/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs +++ b/tests/ui/closures/explicit-return-in-closure-and-outer-fn.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/22346 + //@ run-pass #![allow(dead_code)] diff --git a/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs index dbb560a199bf7..8f475057f376d 100644 --- a/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs +++ b/tests/ui/closures/fn-sugar-in-boxed-trait-object.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/17897 + //@ run-pass fn action(mut cb: Box usize>) -> usize { cb(1) diff --git a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs index a68369616d8b6..e66603a1f7dc6 100644 --- a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/23046 + pub enum Expr<'var, VAR> { Let(Box>, Box Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>) diff --git a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr index f70ac0c9f388a..56ebb0a339a50 100644 --- a/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr +++ b/tests/ui/closures/hrtb-closure-in-recursive-enum-type-error.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed for `Expr<'_, _>` - --> $DIR/issue-23046.rs:17:15 + --> $DIR/hrtb-closure-in-recursive-enum-type-error.rs:19:15 | LL | let ex = |x| { | ^ diff --git a/tests/ui/closures/module-path-not-confused-with-upvar.rs b/tests/ui/closures/module-path-not-confused-with-upvar.rs index 2a39ef28bdbbc..9eb56bee2f33c 100644 --- a/tests/ui/closures/module-path-not-confused-with-upvar.rs +++ b/tests/ui/closures/module-path-not-confused-with-upvar.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/29522 + //@ run-pass #![allow(unused_variables)] // check that we don't accidentally capture upvars just because their name diff --git a/tests/ui/closures/move-closure-can-call-captured-fnmut.rs b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs index 2b2370b0e2b4c..8267dd513417a 100644 --- a/tests/ui/closures/move-closure-can-call-captured-fnmut.rs +++ b/tests/ui/closures/move-closure-can-call-captured-fnmut.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16668 + //@ check-pass #![allow(dead_code)] struct Parser<'a, I, O> { diff --git a/tests/ui/closures/move-closure-is-send.rs b/tests/ui/closures/move-closure-is-send.rs index d9a7aa9101d3f..ace163a37d839 100644 --- a/tests/ui/closures/move-closure-is-send.rs +++ b/tests/ui/closures/move-closure-is-send.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16560 + //@ run-pass #![allow(unused_variables)] //@ needs-threads diff --git a/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs index f7f4f4348afab..a8c8aae3bb299 100644 --- a/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs +++ b/tests/ui/closures/no-unused-mut-warning-for-move-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16671 + //@ run-pass #![deny(warnings)] diff --git a/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs index 2172c631b841d..2da3ac24eb5b0 100644 --- a/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs +++ b/tests/ui/closures/pass-lifetime-fn-to-generic-fnonce.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/19127 + //@ run-pass #![allow(unused_variables)] diff --git a/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs index fa3988e099945..72648a92decf3 100644 --- a/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs +++ b/tests/ui/closures/ref-binding-lifetime-in-closure-pattern.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/16994 + //@ check-pass fn cb<'a,T>(_x: Box, bool))) -> T>) -> T { diff --git a/tests/ui/closures/region-obligations-closure-and-projection.rs b/tests/ui/closures/region-obligations-closure-and-projection.rs index adfb567d7dd32..9010b7c8c6bed 100644 --- a/tests/ui/closures/region-obligations-closure-and-projection.rs +++ b/tests/ui/closures/region-obligations-closure-and-projection.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/46069 + //@ run-pass use std::iter::{Fuse, Cloned}; use std::slice::Iter; diff --git a/tests/ui/closures/unique-closure-type-mismatch.rs b/tests/ui/closures/unique-closure-type-mismatch.rs index 7df036c8e3a45..ee05c9ed77e40 100644 --- a/tests/ui/closures/unique-closure-type-mismatch.rs +++ b/tests/ui/closures/unique-closure-type-mismatch.rs @@ -1,3 +1,5 @@ +//! Regression test for https://github.com/rust-lang/rust/issues/24036 + fn closure_to_loc() { let mut x = |c| c + 1; x = |c| c + 1; diff --git a/tests/ui/closures/unique-closure-type-mismatch.stderr b/tests/ui/closures/unique-closure-type-mismatch.stderr index 184383b736942..3a31b6db4f62b 100644 --- a/tests/ui/closures/unique-closure-type-mismatch.stderr +++ b/tests/ui/closures/unique-closure-type-mismatch.stderr @@ -1,18 +1,18 @@ error[E0308]: mismatched types - --> $DIR/issue-24036.rs:3:9 + --> $DIR/unique-closure-type-mismatch.rs:5:9 | LL | let mut x = |c| c + 1; | --- the expected closure LL | x = |c| c + 1; | ^^^^^^^^^ expected closure, found a different closure | - = note: expected closure `{closure@$DIR/issue-24036.rs:2:17: 2:20}` - found closure `{closure@$DIR/issue-24036.rs:3:9: 3:12}` + = note: expected closure `{closure@$DIR/unique-closure-type-mismatch.rs:4:17: 4:20}` + found closure `{closure@$DIR/unique-closure-type-mismatch.rs:5:9: 5:12}` = note: no two closures, even if identical, have the same type = help: consider boxing your closure and/or using it as a trait object error[E0284]: type annotations needed - --> $DIR/issue-24036.rs:9:15 + --> $DIR/unique-closure-type-mismatch.rs:11:15 | LL | 1 => |c| c + 1, | ^ - type must be known at this point diff --git a/tests/ui/issues/issue-16560.rs b/tests/ui/issues/issue-16560.rs deleted file mode 100644 index d9a7aa9101d3f..0000000000000 --- a/tests/ui/issues/issue-16560.rs +++ /dev/null @@ -1,18 +0,0 @@ -//@ run-pass -#![allow(unused_variables)] -//@ needs-threads - -use std::thread; -use std::mem; - -fn main() { - let y = 0u8; - let closure = move |x: u8| y + x; - - // Check that both closures are capturing by value - assert_eq!(1, mem::size_of_val(&closure)); - - thread::spawn(move|| { - let ok = closure; - }).join().ok().unwrap(); -} diff --git a/tests/ui/issues/issue-16668.rs b/tests/ui/issues/issue-16668.rs deleted file mode 100644 index 2b2370b0e2b4c..0000000000000 --- a/tests/ui/issues/issue-16668.rs +++ /dev/null @@ -1,20 +0,0 @@ -//@ check-pass -#![allow(dead_code)] -struct Parser<'a, I, O> { - parse: Box Result + 'a> -} - -impl<'a, I: 'a, O: 'a> Parser<'a, I, O> { - fn compose(mut self, mut rhs: Parser<'a, O, K>) -> Parser<'a, I, K> { - Parser { - parse: Box::new(move |x: I| { - match (self.parse)(x) { - Ok(r) => (rhs.parse)(r), - Err(e) => Err(e) - } - }) - } - } -} - -fn main() {} diff --git a/tests/ui/issues/issue-16671.rs b/tests/ui/issues/issue-16671.rs deleted file mode 100644 index f7f4f4348afab..0000000000000 --- a/tests/ui/issues/issue-16671.rs +++ /dev/null @@ -1,12 +0,0 @@ -//@ run-pass - -#![deny(warnings)] - -fn foo(_f: F) { } - -fn main() { - let mut var = Vec::new(); - foo(move|| { - var.push(1); - }); -} diff --git a/tests/ui/issues/issue-16994.rs b/tests/ui/issues/issue-16994.rs deleted file mode 100644 index fa3988e099945..0000000000000 --- a/tests/ui/issues/issue-16994.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ check-pass - -fn cb<'a,T>(_x: Box, bool))) -> T>) -> T { - panic!() -} - -fn main() { - cb(Box::new(|(k, &(ref v, b))| (*k, v.clone(), b))); -} diff --git a/tests/ui/issues/issue-17816.rs b/tests/ui/issues/issue-17816.rs deleted file mode 100644 index da28a14685f6c..0000000000000 --- a/tests/ui/issues/issue-17816.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -#![allow(unused_variables)] -use std::marker::PhantomData; - -fn main() { - struct Symbol<'a, F: Fn(Vec<&'a str>) -> &'a str> { function: F, marker: PhantomData<&'a ()> } - let f = |x: Vec<&str>| -> &str { "foobar" }; - let sym = Symbol { function: f, marker: PhantomData }; - (sym.function)(vec![]); -} diff --git a/tests/ui/issues/issue-17897.rs b/tests/ui/issues/issue-17897.rs deleted file mode 100644 index dbb560a199bf7..0000000000000 --- a/tests/ui/issues/issue-17897.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ run-pass -fn action(mut cb: Box usize>) -> usize { - cb(1) -} - -pub fn main() { - println!("num: {}", action(Box::new(move |u| u))); -} diff --git a/tests/ui/issues/issue-18188.rs b/tests/ui/issues/issue-18188.rs deleted file mode 100644 index b3b008229a53a..0000000000000 --- a/tests/ui/issues/issue-18188.rs +++ /dev/null @@ -1,21 +0,0 @@ -//@ check-pass - -pub trait Promisable: Send + Sync {} -impl Promisable for T {} - -pub fn propagate<'a, T, E, F, G>(mut action: F) - -> Box) -> Result + 'a> - where - T: Promisable + Clone + 'a, - E: Promisable + Clone + 'a, - F: FnMut(&T) -> Result + Send + 'a, - G: FnMut(Result) -> Result + 'a { - Box::new(move |result: Result| { - match result { - Ok(ref t) => action(t), - Err(ref e) => Err(e.clone()), - } - }) -} - -fn main() {} diff --git a/tests/ui/issues/issue-19127.rs b/tests/ui/issues/issue-19127.rs deleted file mode 100644 index 2172c631b841d..0000000000000 --- a/tests/ui/issues/issue-19127.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass -#![allow(unused_variables)] - -fn foo T>(f: F) {} -fn id<'a>(input: &'a u8) -> &'a u8 { input } - -fn main() { - foo(id); -} diff --git a/tests/ui/issues/issue-20174.rs b/tests/ui/issues/issue-20174.rs deleted file mode 100644 index 7b49fe8c7b472..0000000000000 --- a/tests/ui/issues/issue-20174.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ run-pass -struct GradFn usize>(F); - -fn main() { - let GradFn(x_squared) : GradFn<_> = GradFn(|| -> usize { 2 }); - let _ = x_squared(); -} diff --git a/tests/ui/issues/issue-20575.rs b/tests/ui/issues/issue-20575.rs deleted file mode 100644 index b213b79d37cab..0000000000000 --- a/tests/ui/issues/issue-20575.rs +++ /dev/null @@ -1,9 +0,0 @@ -//@ run-pass -// Test that overloaded calls work with zero arity closures - - -fn main() { - let functions: [Box Option<()>>; 1] = [Box::new(|| None)]; - - let _: Option> = functions.iter().map(|f| (*f)()).collect(); -} diff --git a/tests/ui/issues/issue-22346.rs b/tests/ui/issues/issue-22346.rs deleted file mode 100644 index 710dc0acda7e9..0000000000000 --- a/tests/ui/issues/issue-22346.rs +++ /dev/null @@ -1,10 +0,0 @@ -//@ run-pass -#![allow(dead_code)] - -// This used to cause an ICE because the retslot for the "return" had the wrong type -fn testcase<'a>() -> Box + 'a> { - return Box::new((0..3).map(|i| { return i; })); -} - -fn main() { -} diff --git a/tests/ui/issues/issue-23046.rs b/tests/ui/issues/issue-23046.rs deleted file mode 100644 index a68369616d8b6..0000000000000 --- a/tests/ui/issues/issue-23046.rs +++ /dev/null @@ -1,20 +0,0 @@ -pub enum Expr<'var, VAR> { - Let(Box>, - Box Fn(Expr<'v, VAR>) -> Expr<'v, VAR> + 'var>) -} - -pub fn add<'var, VAR> - (a: Expr<'var, VAR>, b: Expr<'var, VAR>) -> Expr<'var, VAR> { - loop {} -} - -pub fn let_<'var, VAR, F: for<'v> Fn(Expr<'v, VAR>) -> Expr<'v, VAR>> - (a: Expr<'var, VAR>, b: F) -> Expr<'var, VAR> { - loop {} -} - -fn main() { - let ex = |x| { //~ ERROR type annotations needed - let_(add(x,x), |y| { - let_(add(x, x), |x|x)})}; -} diff --git a/tests/ui/issues/issue-23046.stderr b/tests/ui/issues/issue-23046.stderr deleted file mode 100644 index f70ac0c9f388a..0000000000000 --- a/tests/ui/issues/issue-23046.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error[E0282]: type annotations needed for `Expr<'_, _>` - --> $DIR/issue-23046.rs:17:15 - | -LL | let ex = |x| { - | ^ - | -help: consider giving this closure parameter an explicit type, where the type for type parameter `VAR` is specified - | -LL | let ex = |x: Expr<'_, VAR>| { - | +++++++++++++++ - -error: aborting due to 1 previous error - -For more information about this error, try `rustc --explain E0282`. From 6a54c92233f46debdb3b40d1e983dd54eac56e6f Mon Sep 17 00:00:00 2001 From: Noa Levi <275430404+lphuc2250gma@users.noreply.github.com> Date: Thu, 14 May 2026 04:48:44 +0000 Subject: [PATCH 031/174] compiler: fix duplicated "the" in two doc-comments --- compiler/rustc_borrowck/src/diagnostics/var_name.rs | 2 +- compiler/rustc_type_ir/src/solve/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_borrowck/src/diagnostics/var_name.rs b/compiler/rustc_borrowck/src/diagnostics/var_name.rs index b51604fb2903a..7106bb81da97b 100644 --- a/compiler/rustc_borrowck/src/diagnostics/var_name.rs +++ b/compiler/rustc_borrowck/src/diagnostics/var_name.rs @@ -8,7 +8,7 @@ use tracing::debug; use crate::region_infer::RegionInferenceContext; impl<'tcx> RegionInferenceContext<'tcx> { - /// Find the the name and span of the variable corresponding to the given region. + /// Find the name and span of the variable corresponding to the given region. /// The returned var will also be ensured to actually be used in `body`. pub(crate) fn get_var_name_and_span_for_region( &self, diff --git a/compiler/rustc_type_ir/src/solve/mod.rs b/compiler/rustc_type_ir/src/solve/mod.rs index f2ea2e5a729e8..fb61d3a467316 100644 --- a/compiler/rustc_type_ir/src/solve/mod.rs +++ b/compiler/rustc_type_ir/src/solve/mod.rs @@ -145,7 +145,7 @@ impl AsRef<[T]> for SmallCopyList { /// | never | no | no | /// | always | yes | yes | /// | [defid in storage] | no | only if any of the defids in the list is in the opaque type storage OR if TypingMode::PostAnalysis | -/// | opaque with hidden type | no | only if any of the the opaques in the opaque type storage has a hidden type in this list AND if TypingMode::Analysis | +/// | opaque with hidden type | no | only if any of the opaques in the opaque type storage has a hidden type in this list AND if TypingMode::Analysis | /// /// - "bail" is implemented with [`should_bail`](Self::should_bail). /// If true, we're abandoning our attempt to canonicalize in [`TypingMode::ErasedNotCoherence`], From 812e230fb9e3528538854654fe2ed434e387a21d Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Thu, 14 May 2026 11:11:55 +0100 Subject: [PATCH 032/174] PR Feedback - grammatical changes, clear reason for removal and move `register_removed(...)` call --- compiler/rustc_codegen_ssa/src/codegen_attrs.rs | 2 +- compiler/rustc_feature/src/removed.rs | 2 +- compiler/rustc_lint/src/lib.rs | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs index 2895b4718d7f3..b4626724fd803 100644 --- a/compiler/rustc_codegen_ssa/src/codegen_attrs.rs +++ b/compiler/rustc_codegen_ssa/src/codegen_attrs.rs @@ -425,7 +425,7 @@ fn check_result( .dcx() .struct_span_err(span, "cannot use `#[inline(always)]` with `#[target_feature]`"); diag.note( - "See issue here for full discussion; \ + "See this issue for full discussion: \ https://github.com/rust-lang/rust/issues/145574", ); diag.emit(); diff --git a/compiler/rustc_feature/src/removed.rs b/compiler/rustc_feature/src/removed.rs index dcfd8b71dde43..53d5dda6d7f3f 100644 --- a/compiler/rustc_feature/src/removed.rs +++ b/compiler/rustc_feature/src/removed.rs @@ -284,7 +284,7 @@ declare_features! ( (removed, struct_inherit, "1.0.0", None, None), /// Allows the use of target_feature when a function is marked inline(always). (removed, target_feature_inline_always, "CURRENT_RUSTC_VERSION", Some(145574), - Some("removed because `#[inline(always)]` with `#[target_feature]` is forbidden")), + Some("removed because of unfixable soundness issues")), (removed, test_removed_feature, "1.0.0", None, None), /// Allows using items which are missing stability attributes (removed, unmarked_api, "1.0.0", None, None), diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index b9332845edab4..9683c726d9818 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -398,10 +398,6 @@ fn register_builtins(store: &mut LintStore) { store.register_removed("rustdoc", "use `rustdoc::all` instead"); store.register_removed("unknown_features", "replaced by an error"); - store.register_removed( - "inline_always_mismatching_target_features", - "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", - ); store.register_removed("unsigned_negation", "replaced by negate_unsigned feature gate"); store.register_removed("negate_unsigned", "cast a signed value instead"); store.register_removed("raw_pointer_derive", "using derive with raw pointers is ok"); @@ -648,6 +644,10 @@ fn register_builtins(store: &mut LintStore) { ); store.register_removed("wasm_c_abi", "the wasm C ABI has been fixed"); store.register_removed("soft_unstable", "the general soft-unstable mechanism has been removed"); + store.register_removed( + "inline_always_mismatching_target_features", + "replaced by a hard error for `#[inline(always)]` with `#[target_feature]`", + ); } fn register_internals(store: &mut LintStore) { From 61e801d7f39cb02f6217347cf9b254948f6780e9 Mon Sep 17 00:00:00 2001 From: Eval Exec Date: Thu, 14 May 2026 12:29:49 -0400 Subject: [PATCH 033/174] Add FileCheck for build_correct_coerce --- tests/mir-opt/build_correct_coerce.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/mir-opt/build_correct_coerce.rs b/tests/mir-opt/build_correct_coerce.rs index 004e1309b0b26..0721a1fddf067 100644 --- a/tests/mir-opt/build_correct_coerce.rs +++ b/tests/mir-opt/build_correct_coerce.rs @@ -1,5 +1,3 @@ -//@ skip-filecheck - // Validate that we record the target for the `as` coercion as `for<'a> fn(&'a (), &'a ())`, // and not `for<'a, 'b>(&'a (), &'b ())`. We previously did the latter due to a bug in // the code that records adjustments in HIR typeck. @@ -8,5 +6,8 @@ fn foo<'a, 'b>(_: &'a (), _: &'b ()) {} // EMIT_MIR build_correct_coerce.main.built.after.mir fn main() { + // CHECK-LABEL: fn main( + // CHECK: let _1: for<'a> fn(&'a (), &'a ()); + // CHECK: _1 = foo as for<'a> fn(&'a (), &'a ()) let x = foo as for<'a> fn(&'a (), &'a ()); } From 73931c4422b8fef480278afd9b4209fff5b1c0ff Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 14 May 2026 15:36:33 -0700 Subject: [PATCH 034/174] Revert "Rollup merge of #154994 - dianne:no-dbg-temp, r=Mark-Simulacrum" This reverts commit 59b36a52837f112f4adfb496e493324ddfc5294c, reversing changes made to 197eb8c6c0a495f52d71c8e7bda98818010dee55. --- library/std/src/macros.rs | 7 ++----- library/std/src/macros/tests.rs | 24 ------------------------ 2 files changed, 2 insertions(+), 29 deletions(-) diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index a23aa0d877018..108cd3cd98eb5 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -369,8 +369,7 @@ macro_rules! dbg { /// E.g. `dbg_internal!(() () (1, 2))` expands into /// ```rust, ignore /// match (1, 2) { -/// args => { -/// let (tmp_1, tmp_2) = args; +/// (tmp_1, tmp_2) => { /// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); /// (tmp_1, tmp_2) /// } @@ -386,9 +385,7 @@ pub macro dbg_internal { // of temporaries - https://stackoverflow.com/a/48732525/1063961 // Always put the arguments in a tuple to avoid an unused parens lint on the pattern. match ($($processed,)+) { - // Move the entire tuple so it doesn't stick around as a temporary (#154988). - args => { - let ($($bound,)+) = args; + ($($bound,)+) => { $crate::eprint!( $crate::concat!($($piece),+), $( diff --git a/library/std/src/macros/tests.rs b/library/std/src/macros/tests.rs index 230bfdf3c9836..db2be925ff30a 100644 --- a/library/std/src/macros/tests.rs +++ b/library/std/src/macros/tests.rs @@ -1,7 +1,5 @@ // ignore-tidy-dbg -use core::fmt::Debug; - /// Test for : /// `dbg!` shouldn't drop arguments' temporaries. #[test] @@ -13,25 +11,3 @@ fn no_dropping_temps() { *dbg!(0, &temp()).1; *dbg!(0, &temp(), 2).1; } - -/// Test for : -/// `dbg!` shouldn't create a temporary that lives past its invocation. -#[test] -fn no_leaking_internal_temps_from_dbg() { - #[derive(Debug)] - struct Foo; - - #[derive(Debug)] - struct Bar<'a>(#[allow(unused)] &'a Foo); - impl Drop for Bar<'_> { - fn drop(&mut self) {} - } - - let foo = Foo; - let bar = Bar(&foo); - // If `dbg!` creates a `(Bar<'_>,)` temporary that lasts past its expansion, this will fail - // to compile, because it will be dropped after `foo`, which it borrows from. The tuple - // mimics the drop order of block tail expressions before Rust 2024: first the result of `dbg!` - // is dropped, then `foo`, then any temporaries left over from `dbg!` are dropped, if present. - (drop(dbg!(bar)), drop(foo)); -} From c9d9448c98d8e61c445536b188ed6da50bef8c4d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 14 May 2026 15:37:07 -0700 Subject: [PATCH 035/174] Revert "Rollup merge of #154074 - dianne:dbg-temp-scopes, r=Mark-Simulacrum" This reverts commit 2a18b885ce495ee421631910c639d878254910ca, reversing changes made to cd14b73b4a41542d921f59e362a5b5005fa4f2ef. --- .../src/diagnostics/conflict_errors.rs | 40 +++----- compiler/rustc_span/src/symbol.rs | 1 - library/std/src/macros.rs | 82 ++++++++--------- library/std/src/macros/tests.rs | 13 --- .../clippy/clippy_lints/src/dbg_macro.rs | 47 +++++++++- src/tools/clippy/clippy_utils/src/sym.rs | 1 + .../dangling_primitive.stderr | 2 +- .../return_pointer_on_unwind.stderr | 2 +- tests/ui/borrowck/dbg-issue-120327.rs | 53 ++++++++--- tests/ui/borrowck/dbg-issue-120327.stderr | 91 +++++++------------ tests/ui/liveness/liveness-upvars.rs | 2 +- tests/ui/liveness/liveness-upvars.stderr | 10 +- .../dbg-macro-move-semantics.stderr | 14 +-- 13 files changed, 177 insertions(+), 181 deletions(-) delete mode 100644 library/std/src/macros/tests.rs diff --git a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs index fcf4bf4a7b7a4..9a64a063fcf5c 100644 --- a/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/conflict_errors.rs @@ -580,6 +580,8 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { } // for dbg!(x) which may take ownership, suggest dbg!(&x) instead + // but here we actually do not check whether the macro name is `dbg!` + // so that we may extend the scope a bit larger to cover more cases fn suggest_ref_for_dbg_args( &self, body: &hir::Expr<'_>, @@ -593,41 +595,29 @@ impl<'infcx, 'tcx> MirBorrowckCtxt<'_, 'infcx, 'tcx> { }); let Some(var_info) = var_info else { return }; let arg_name = var_info.name; - struct MatchArgFinder<'tcx> { - tcx: TyCtxt<'tcx>, - move_span: Span, + struct MatchArgFinder { + expr_span: Span, + match_arg_span: Option, arg_name: Symbol, - match_arg_span: Option = None, } - impl Visitor<'_> for MatchArgFinder<'_> { + impl Visitor<'_> for MatchArgFinder { fn visit_expr(&mut self, e: &hir::Expr<'_>) { // dbg! is expanded into a match pattern, we need to find the right argument span - if let hir::ExprKind::Match(scrutinee, ..) = &e.kind - && let hir::ExprKind::Tup(args) = scrutinee.kind - && e.span.macro_backtrace().any(|expn| { - expn.macro_def_id.is_some_and(|macro_def_id| { - self.tcx.is_diagnostic_item(sym::dbg_macro, macro_def_id) - }) - }) + if let hir::ExprKind::Match(expr, ..) = &e.kind + && let hir::ExprKind::Path(hir::QPath::Resolved( + _, + path @ Path { segments: [seg], .. }, + )) = &expr.kind + && seg.ident.name == self.arg_name + && self.expr_span.source_callsite().contains(expr.span) { - for arg in args { - if let hir::ExprKind::Path(hir::QPath::Resolved( - _, - path @ Path { segments: [seg], .. }, - )) = &arg.kind - && seg.ident.name == self.arg_name - && self.move_span.source_equal(arg.span) - { - self.match_arg_span = Some(path.span); - return; - } - } + self.match_arg_span = Some(path.span); } hir::intravisit::walk_expr(self, e); } } - let mut finder = MatchArgFinder { tcx: self.infcx.tcx, move_span, arg_name, .. }; + let mut finder = MatchArgFinder { expr_span: move_span, match_arg_span: None, arg_name }; finder.visit_expr(body); if let Some(macro_arg_span) = finder.match_arg_span { err.span_suggestion_verbose( diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 69ed7314855f9..9cc1c927427e3 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -752,7 +752,6 @@ symbols! { custom_mir, custom_test_frameworks, d32, - dbg_macro, dead_code, dead_code_pub_in_binary, dealloc, diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index 108cd3cd98eb5..cc000274ea8d6 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -5,9 +5,6 @@ //! library. // ignore-tidy-dbg -#[cfg(test)] -mod tests; - #[doc = include_str!("../../core/src/macros/panic.md")] #[macro_export] #[rustc_builtin_macro(std_panic)] @@ -362,16 +359,19 @@ macro_rules! dbg { }; } -/// Internal macro that processes a list of expressions, binds their results -/// with `match`, calls `eprint!` with the collected information, and returns -/// all the evaluated expressions in a tuple. +/// Internal macro that processes a list of expressions and produces a chain of +/// nested `match`es, one for each expression, before finally calling `eprint!` +/// with the collected information and returning all the evaluated expressions +/// in a tuple. /// /// E.g. `dbg_internal!(() () (1, 2))` expands into /// ```rust, ignore -/// match (1, 2) { -/// (tmp_1, tmp_2) => { -/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); -/// (tmp_1, tmp_2) +/// match 1 { +/// tmp_1 => match 2 { +/// tmp_2 => { +/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); +/// (tmp_1, tmp_2) +/// } /// } /// } /// ``` @@ -380,43 +380,39 @@ macro_rules! dbg { #[doc(hidden)] #[rustc_macro_transparency = "semiopaque"] pub macro dbg_internal { - (($($piece:literal),+) ($($processed:expr => $bound:ident),+) ()) => { + (($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{ + $crate::eprint!( + $crate::concat!($($piece),+), + $( + $crate::stringify!($processed), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&$bound as &dyn $crate::fmt::Debug + ),+, + // The location returned here is that of the macro invocation, so + // it will be the same for all expressions. Thus, label these + // arguments so that they can be reused in every piece of the + // formatting template. + file=$crate::file!(), + line=$crate::line!(), + column=$crate::column!() + ); + // Comma separate the variables only when necessary so that this will + // not yield a tuple for a single expression, but rather just parenthesize + // the expression. + ($($bound),+) + }}, + (($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 - // Always put the arguments in a tuple to avoid an unused parens lint on the pattern. - match ($($processed,)+) { - ($($bound,)+) => { - $crate::eprint!( - $crate::concat!($($piece),+), - $( - $crate::stringify!($processed), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&$bound as &dyn $crate::fmt::Debug - ),+, - // The location returned here is that of the macro invocation, so - // it will be the same for all expressions. Thus, label these - // arguments so that they can be reused in every piece of the - // formatting template. - file=$crate::file!(), - line=$crate::line!(), - column=$crate::column!() - ); - // Comma separate the variables only when necessary so that this will - // not yield a tuple for a single expression, but rather just parenthesize - // the expression. - ($($bound),+) - - } + match $val { + tmp => $crate::macros::dbg_internal!( + ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") + ($($processed => $bound,)* $val => tmp) + ($($rest),*) + ), } }, - (($($piece:literal),*) ($($processed:expr => $bound:ident),*) ($val:expr $(,$rest:expr)*)) => { - $crate::macros::dbg_internal!( - ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") - ($($processed => $bound,)* $val => tmp) - ($($rest),*) - ) - }, } #[doc(hidden)] diff --git a/library/std/src/macros/tests.rs b/library/std/src/macros/tests.rs deleted file mode 100644 index db2be925ff30a..0000000000000 --- a/library/std/src/macros/tests.rs +++ /dev/null @@ -1,13 +0,0 @@ -// ignore-tidy-dbg - -/// Test for : -/// `dbg!` shouldn't drop arguments' temporaries. -#[test] -fn no_dropping_temps() { - fn temp() {} - - *dbg!(&temp()); - *dbg!(&temp(), 1).0; - *dbg!(0, &temp()).1; - *dbg!(0, &temp(), 2).1; -} diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index 63968a8b5e04e..eb14ef18c03da 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_test, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; +use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext}; @@ -92,15 +92,16 @@ impl LateLintPass<'_> for DbgMacro { (macro_call.span, String::from("()")) } }, - ExprKind::Match(args, _, _) => { - let suggestion = match args.kind { + ExprKind::Match(first, arms, _) => { + let vals = collect_vals(first, arms); + let suggestion = match *vals.as_slice() { // dbg!(1) => 1 - ExprKind::Tup([val]) => { + [val] => { snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) .to_string() }, // dbg!(2, 3) => (2, 3) - ExprKind::Tup([first, .., last]) => { + [first, .., last] => { let snippet = snippet_with_applicability( cx, first.span.source_callsite().to(last.span.source_callsite()), @@ -164,3 +165,39 @@ fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } + +/// Extracts all value expressions from the `match`-tree generated by `dbg!`. +/// +/// E.g. from +/// ```rust, ignore +/// match 1 { +/// tmp_1 => match 2 { +/// tmp_2 => { +/// /* printing */ +/// (tmp_1, tmp_2) +/// } +/// } +/// } +/// ``` +/// this extracts `1` and `2`. +fn collect_vals<'hir>(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { + let mut vals = vec![first]; + loop { + let [arm] = arms else { + unreachable!("dbg! macro expansion only has single-arm matches") + }; + + match is_async_move_desugar(arm.body) + .unwrap_or(arm.body) + .peel_drop_temps() + .kind + { + ExprKind::Block(..) => return vals, + ExprKind::Match(val, a, _) => { + vals.push(val); + arms = a; + }, + _ => unreachable!("dbg! macro expansion only results in block or match expressions"), + } + } +} diff --git a/src/tools/clippy/clippy_utils/src/sym.rs b/src/tools/clippy/clippy_utils/src/sym.rs index 6a7637fb18585..492eb89708aeb 100644 --- a/src/tools/clippy/clippy_utils/src/sym.rs +++ b/src/tools/clippy/clippy_utils/src/sym.rs @@ -202,6 +202,7 @@ generate! { cx, cycle, cyclomatic_complexity, + dbg_macro, de, debug_struct, deprecated_in_future, diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr index 4f06a1afa50fe..afc2cb214842e 100644 --- a/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr +++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_primitive.stderr @@ -2,7 +2,7 @@ error: Undefined Behavior: memory access failed: ALLOC has been freed, so this p --> tests/fail/dangling_pointers/dangling_primitive.rs:LL:CC | LL | dbg!(*ptr); - | ^^^^ Undefined Behavior occurred here + | ^^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr index 88d5694c4736d..364a4b4a74418 100644 --- a/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr +++ b/src/tools/miri/tests/fail/function_calls/return_pointer_on_unwind.stderr @@ -7,7 +7,7 @@ error: Undefined Behavior: reading memory at ALLOC[0x0..0x4], but memory is unin --> tests/fail/function_calls/return_pointer_on_unwind.rs:LL:CC | LL | dbg!(x.0); - | ^^^ Undefined Behavior occurred here + | ^^^^^^^^^ Undefined Behavior occurred here | = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information diff --git a/tests/ui/borrowck/dbg-issue-120327.rs b/tests/ui/borrowck/dbg-issue-120327.rs index 45605acc34334..2de43f634877a 100644 --- a/tests/ui/borrowck/dbg-issue-120327.rs +++ b/tests/ui/borrowck/dbg-issue-120327.rs @@ -1,44 +1,67 @@ -//! Diagnostic test for : suggest borrowing -//! variables passed to `dbg!` that are later used. -//@ dont-require-annotations: HELP - fn s() -> String { let a = String::new(); - dbg!(a); //~ HELP consider borrowing instead of transferring ownership + dbg!(a); return a; //~ ERROR use of moved value: } fn m() -> String { let a = String::new(); - dbg!(1, 2, a, 1, 2); //~ HELP consider borrowing instead of transferring ownership + dbg!(1, 2, a, 1, 2); return a; //~ ERROR use of moved value: } fn t(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership + dbg!(a, b); return b; //~ ERROR use of moved value: } fn x(a: String) -> String { let b: String = "".to_string(); - dbg!(a, b); //~ HELP consider borrowing instead of transferring ownership + dbg!(a, b); return a; //~ ERROR use of moved value: } -fn two_of_them(a: String) -> String { - dbg!(a, a); //~ ERROR use of moved value - //~| HELP consider borrowing instead of transferring ownership - //~| HELP consider borrowing instead of transferring ownership - return a; //~ ERROR use of moved value +macro_rules! my_dbg { + () => { + eprintln!("[{}:{}:{}]", file!(), line!(), column!()) + }; + ($val:expr $(,)?) => { + match $val { + tmp => { + eprintln!("[{}:{}:{}] {} = {:#?}", + file!(), line!(), column!(), stringify!($val), &tmp); + tmp + } + } + }; + ($($val:expr),+ $(,)?) => { + ($(my_dbg!($val)),+,) + }; +} + +fn test_my_dbg() -> String { + let b: String = "".to_string(); + my_dbg!(b, 1); + return b; //~ ERROR use of moved value: +} + +fn test_not_macro() -> String { + let a = String::new(); + let _b = match a { + tmp => { + eprintln!("dbg: {}", tmp); + tmp + } + }; + return a; //~ ERROR use of moved value: } fn get_expr(_s: String) {} -// The suggestion is purely syntactic; applying it here will result in a type error. fn test() { let a: String = "".to_string(); - let _res = get_expr(dbg!(a)); //~ HELP consider borrowing instead of transferring ownership + let _res = get_expr(dbg!(a)); let _l = a.len(); //~ ERROR borrow of moved value } diff --git a/tests/ui/borrowck/dbg-issue-120327.stderr b/tests/ui/borrowck/dbg-issue-120327.stderr index e7a7151e541dd..efacc0c3f1341 100644 --- a/tests/ui/borrowck/dbg-issue-120327.stderr +++ b/tests/ui/borrowck/dbg-issue-120327.stderr @@ -1,133 +1,112 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:8:12 + --> $DIR/dbg-issue-120327.rs:4:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(a); - | - value moved here + | ------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a.clone()); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(&a); | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:14:12 + --> $DIR/dbg-issue-120327.rs:10:12 | LL | let a = String::new(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | dbg!(1, 2, a, 1, 2); - | - value moved here + | ------------------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(1, 2, a.clone(), 1, 2); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(1, 2, &a, 1, 2); | + error[E0382]: use of moved value: `b` - --> $DIR/dbg-issue-120327.rs:20:12 + --> $DIR/dbg-issue-120327.rs:16:12 | LL | let b: String = "".to_string(); | - move occurs because `b` has type `String`, which does not implement the `Copy` trait LL | dbg!(a, b); - | - value moved here + | ---------- value moved here LL | return b; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a, b.clone()); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(a, &b); | + error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:26:12 + --> $DIR/dbg-issue-120327.rs:22:12 | LL | fn x(a: String) -> String { | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let b: String = "".to_string(); LL | dbg!(a, b); - | - value moved here + | ---------- value moved here LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a.clone(), b); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | dbg!(&a, b); | + -error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:30:13 - | -LL | fn two_of_them(a: String) -> String { - | - move occurs because `a` has type `String`, which does not implement the `Copy` trait -LL | dbg!(a, a); - | - ^ value used here after move - | | - | value moved here +error[E0382]: use of moved value: `b` + --> $DIR/dbg-issue-120327.rs:46:12 | -help: consider cloning the value if the performance cost is acceptable +LL | tmp => { + | --- value moved here +... +LL | let b: String = "".to_string(); + | - move occurs because `b` has type `String`, which does not implement the `Copy` trait +LL | my_dbg!(b, 1); +LL | return b; + | ^ value used here after move | -LL | dbg!(a.clone(), a); - | ++++++++ help: consider borrowing instead of transferring ownership | -LL | dbg!(&a, a); - | + +LL | my_dbg!(&b, 1); + | + +help: borrow this binding in the pattern to avoid moving the value + | +LL | ref tmp => { + | +++ error[E0382]: use of moved value: `a` - --> $DIR/dbg-issue-120327.rs:33:12 + --> $DIR/dbg-issue-120327.rs:57:12 | -LL | fn two_of_them(a: String) -> String { - | - move occurs because `a` has type `String`, which does not implement the `Copy` trait -LL | dbg!(a, a); - | - value moved here +LL | let a = String::new(); + | - move occurs because `a` has type `String`, which does not implement the `Copy` trait +LL | let _b = match a { +LL | tmp => { + | --- value moved here ... LL | return a; | ^ value used here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | dbg!(a, a.clone()); - | ++++++++ -help: consider borrowing instead of transferring ownership +help: borrow this binding in the pattern to avoid moving the value | -LL | dbg!(a, &a); - | + +LL | ref tmp => { + | +++ error[E0382]: borrow of moved value: `a` - --> $DIR/dbg-issue-120327.rs:42:14 + --> $DIR/dbg-issue-120327.rs:65:14 | LL | let a: String = "".to_string(); | - move occurs because `a` has type `String`, which does not implement the `Copy` trait LL | let _res = get_expr(dbg!(a)); - | - value moved here + | ------- value moved here LL | let _l = a.len(); | ^ value borrowed here after move | -help: consider cloning the value if the performance cost is acceptable - | -LL | let _res = get_expr(dbg!(a.clone())); - | ++++++++ help: consider borrowing instead of transferring ownership | LL | let _res = get_expr(dbg!(&a)); diff --git a/tests/ui/liveness/liveness-upvars.rs b/tests/ui/liveness/liveness-upvars.rs index 0e198f1dea10b..be58b48a40576 100644 --- a/tests/ui/liveness/liveness-upvars.rs +++ b/tests/ui/liveness/liveness-upvars.rs @@ -98,7 +98,7 @@ pub fn g(mut v: T) { } pub fn h() { - let mut z = T::default(); //~ WARN unused variable: `z` + let mut z = T::default(); let _ = move |b| { loop { if b { diff --git a/tests/ui/liveness/liveness-upvars.stderr b/tests/ui/liveness/liveness-upvars.stderr index 9f5a3de7365a3..96a922772c919 100644 --- a/tests/ui/liveness/liveness-upvars.stderr +++ b/tests/ui/liveness/liveness-upvars.stderr @@ -157,14 +157,6 @@ LL | z = T::default(); | = help: maybe it is overwritten before being read? -warning: unused variable: `z` - --> $DIR/liveness-upvars.rs:101:9 - | -LL | let mut z = T::default(); - | ^^^^^ help: if this is intentional, prefix it with an underscore: `_z` - | - = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` - warning: value captured by `state` is never read --> $DIR/liveness-upvars.rs:131:9 | @@ -206,5 +198,5 @@ LL | s = yield (); LL | s = 3; | ----- `s` is overwritten here before the previous value is read -warning: 25 warnings emitted +warning: 24 warnings emitted diff --git a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr index 7ce5ebf81e310..f8ef315b9cc78 100644 --- a/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr +++ b/tests/ui/rfcs/rfc-2361-dbg-macro/dbg-macro-move-semantics.stderr @@ -1,21 +1,13 @@ error[E0382]: use of moved value: `a` - --> $DIR/dbg-macro-move-semantics.rs:9:18 + --> $DIR/dbg-macro-move-semantics.rs:9:13 | LL | let a = NoCopy(0); | - move occurs because `a` has type `NoCopy`, which does not implement the `Copy` trait LL | let _ = dbg!(a); - | - value moved here + | ------- value moved here LL | let _ = dbg!(a); - | ^ value used here after move + | ^^^^^^^ value used here after move | -note: if `NoCopy` implemented `Clone`, you could clone the value - --> $DIR/dbg-macro-move-semantics.rs:4:1 - | -LL | struct NoCopy(usize); - | ^^^^^^^^^^^^^ consider implementing `Clone` for this type -... -LL | let _ = dbg!(a); - | - you could clone this value help: consider borrowing instead of transferring ownership | LL | let _ = dbg!(&a); From 9246e19299259ff880d2ff9da03d62088e8827bd Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 14 May 2026 16:08:56 -0700 Subject: [PATCH 036/174] Revert "Rollup merge of #149869 - joboet:torn-dbg, r=Mark-Simulacrum" This reverts commit cc666ba8f446ff47c7e1b56019afd1dbee85b625, reversing changes made to 2da59596005edb3979aca9ad59012a8aab2339c3. --- library/std/src/lib.rs | 4 +- library/std/src/macros.rs | 77 +++++------------ .../clippy/clippy_lints/src/dbg_macro.rs | 82 ++++++------------- 3 files changed, 48 insertions(+), 115 deletions(-) diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 7a548dfb33da7..f75619c70325a 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -488,9 +488,7 @@ extern crate std as realstd; // The standard macros that are not built-in to the compiler. #[macro_use] -#[doc(hidden)] -#[unstable(feature = "std_internals", issue = "none")] -pub mod macros; +mod macros; // The runtime entry point and a few unstable public functions used by the // compiler diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index cc000274ea8d6..da5c04a618841 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -347,72 +347,37 @@ macro_rules! eprintln { /// [`debug!`]: https://docs.rs/log/*/log/macro.debug.html /// [`log`]: https://crates.io/crates/log #[macro_export] -#[allow_internal_unstable(std_internals)] #[cfg_attr(not(test), rustc_diagnostic_item = "dbg_macro")] #[stable(feature = "dbg_macro", since = "1.32.0")] macro_rules! dbg { + // NOTE: We cannot use `concat!` to make a static string as a format argument + // of `eprintln!` because `file!` could contain a `{` or + // `$val` expression could be a block (`{ .. }`), in which case the `eprintln!` + // will be malformed. () => { $crate::eprintln!("[{}:{}:{}]", $crate::file!(), $crate::line!(), $crate::column!()) }; - ($($val:expr),+ $(,)?) => { - $crate::macros::dbg_internal!(() () ($($val),+)) - }; -} - -/// Internal macro that processes a list of expressions and produces a chain of -/// nested `match`es, one for each expression, before finally calling `eprint!` -/// with the collected information and returning all the evaluated expressions -/// in a tuple. -/// -/// E.g. `dbg_internal!(() () (1, 2))` expands into -/// ```rust, ignore -/// match 1 { -/// tmp_1 => match 2 { -/// tmp_2 => { -/// eprint!("...", &tmp_1, &tmp_2, /* some other arguments */); -/// (tmp_1, tmp_2) -/// } -/// } -/// } -/// ``` -/// -/// This is necessary so that `dbg!` outputs don't get torn, see #136703. -#[doc(hidden)] -#[rustc_macro_transparency = "semiopaque"] -pub macro dbg_internal { - (($($piece:literal),+) ($($processed:expr => $bound:expr),+) ()) => {{ - $crate::eprint!( - $crate::concat!($($piece),+), - $( - $crate::stringify!($processed), - // The `&T: Debug` check happens here (not in the format literal desugaring) - // to avoid format literal related messages and suggestions. - &&$bound as &dyn $crate::fmt::Debug - ),+, - // The location returned here is that of the macro invocation, so - // it will be the same for all expressions. Thus, label these - // arguments so that they can be reused in every piece of the - // formatting template. - file=$crate::file!(), - line=$crate::line!(), - column=$crate::column!() - ); - // Comma separate the variables only when necessary so that this will - // not yield a tuple for a single expression, but rather just parenthesize - // the expression. - ($($bound),+) - }}, - (($($piece:literal),*) ($($processed:expr => $bound:expr),*) ($val:expr $(,$rest:expr)*)) => { + ($val:expr $(,)?) => { // Use of `match` here is intentional because it affects the lifetimes // of temporaries - https://stackoverflow.com/a/48732525/1063961 match $val { - tmp => $crate::macros::dbg_internal!( - ($($piece,)* "[{file}:{line}:{column}] {} = {:#?}\n") - ($($processed => $bound,)* $val => tmp) - ($($rest),*) - ), + tmp => { + $crate::eprintln!("[{}:{}:{}] {} = {:#?}", + $crate::file!(), + $crate::line!(), + $crate::column!(), + $crate::stringify!($val), + // The `&T: Debug` check happens here (not in the format literal desugaring) + // to avoid format literal related messages and suggestions. + &&tmp as &dyn $crate::fmt::Debug, + ); + tmp + } } - }, + }; + ($($val:expr),+ $(,)?) => { + ($($crate::dbg!($val)),+,) + }; } #[doc(hidden)] diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs index eb14ef18c03da..29d0b47002679 100644 --- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs +++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs @@ -5,7 +5,7 @@ use clippy_utils::source::snippet_with_applicability; use clippy_utils::{is_in_test, sym}; use rustc_data_structures::fx::FxHashSet; use rustc_errors::Applicability; -use rustc_hir::{Arm, Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; +use rustc_hir::{Closure, ClosureKind, CoroutineKind, Expr, ExprKind, LetStmt, LocalSource, Node, Stmt, StmtKind}; use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::{Span, SyntaxContext}; @@ -92,27 +92,33 @@ impl LateLintPass<'_> for DbgMacro { (macro_call.span, String::from("()")) } }, - ExprKind::Match(first, arms, _) => { - let vals = collect_vals(first, arms); - let suggestion = match *vals.as_slice() { - // dbg!(1) => 1 - [val] => { - snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) - .to_string() + // dbg!(1) + ExprKind::Match(val, ..) => ( + macro_call.span, + snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability) + .to_string(), + ), + // dbg!(2, 3) + ExprKind::Tup( + [ + Expr { + kind: ExprKind::Match(first, ..), + .. }, - // dbg!(2, 3) => (2, 3) - [first, .., last] => { - let snippet = snippet_with_applicability( - cx, - first.span.source_callsite().to(last.span.source_callsite()), - "..", - &mut applicability, - ); - format!("({snippet})") + .., + Expr { + kind: ExprKind::Match(last, ..), + .. }, - _ => unreachable!(), - }; - (macro_call.span, suggestion) + ], + ) => { + let snippet = snippet_with_applicability( + cx, + first.span.source_callsite().to(last.span.source_callsite()), + "..", + &mut applicability, + ); + (macro_call.span, format!("({snippet})")) }, _ => unreachable!(), }; @@ -165,39 +171,3 @@ fn is_async_move_desugar<'tcx>(expr: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx fn first_dbg_macro_in_expansion(cx: &LateContext<'_>, span: Span) -> Option { macro_backtrace(span).find(|mc| cx.tcx.is_diagnostic_item(sym::dbg_macro, mc.def_id)) } - -/// Extracts all value expressions from the `match`-tree generated by `dbg!`. -/// -/// E.g. from -/// ```rust, ignore -/// match 1 { -/// tmp_1 => match 2 { -/// tmp_2 => { -/// /* printing */ -/// (tmp_1, tmp_2) -/// } -/// } -/// } -/// ``` -/// this extracts `1` and `2`. -fn collect_vals<'hir>(first: &'hir Expr<'hir>, mut arms: &'hir [Arm<'hir>]) -> Vec<&'hir Expr<'hir>> { - let mut vals = vec![first]; - loop { - let [arm] = arms else { - unreachable!("dbg! macro expansion only has single-arm matches") - }; - - match is_async_move_desugar(arm.body) - .unwrap_or(arm.body) - .peel_drop_temps() - .kind - { - ExprKind::Block(..) => return vals, - ExprKind::Match(val, a, _) => { - vals.push(val); - arms = a; - }, - _ => unreachable!("dbg! macro expansion only results in block or match expressions"), - } - } -} From 6f35b83469e9a12d077dfc121aae7efb642c9be4 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Thu, 14 May 2026 16:39:43 -0700 Subject: [PATCH 037/174] Restore `dbg!` regression tests These are all tests from trying to fix the tear-free `dbg!`: * rust-lang/rust#154074 * rust-lang/rust#154994 * rust-lang/rust#155915 These should also work as we revert to the old `dbg!`, so we should keep the tests to make sure they don't regress again. Co-authored-by: dianne --- library/std/src/lib.rs | 5 ++- library/std/src/macros.rs | 3 ++ library/std/src/macros/tests.rs | 67 +++++++++++++++++++++++++++++++++ 3 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 library/std/src/macros/tests.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index f75619c70325a..4c282af401b92 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -255,7 +255,10 @@ #![allow(unused_features)] // // Features: -#![cfg_attr(test, feature(internal_output_capture, print_internals, update_panic_count, rt))] +#![cfg_attr( + test, + feature(internal_output_capture, print_internals, super_let, update_panic_count, rt) +)] #![cfg_attr( all(target_vendor = "fortanix", target_env = "sgx"), feature(slice_index_methods, coerce_unsized, sgx_platform) diff --git a/library/std/src/macros.rs b/library/std/src/macros.rs index da5c04a618841..8bcf870e9aeb4 100644 --- a/library/std/src/macros.rs +++ b/library/std/src/macros.rs @@ -5,6 +5,9 @@ //! library. // ignore-tidy-dbg +#[cfg(test)] +mod tests; + #[doc = include_str!("../../core/src/macros/panic.md")] #[macro_export] #[rustc_builtin_macro(std_panic)] diff --git a/library/std/src/macros/tests.rs b/library/std/src/macros/tests.rs new file mode 100644 index 0000000000000..1ad4cf8c4c039 --- /dev/null +++ b/library/std/src/macros/tests.rs @@ -0,0 +1,67 @@ +// ignore-tidy-dbg + +use core::fmt::Debug; + +/// Test for : +/// `dbg!` shouldn't drop arguments' temporaries. +#[test] +fn no_dropping_temps() { + fn temp() {} + + *dbg!(&temp()); + *dbg!(&temp(), 1).0; + *dbg!(0, &temp()).1; + *dbg!(0, &temp(), 2).1; +} + +/// Test for : +/// `dbg!` shouldn't create a temporary that lives past its invocation. +#[test] +fn no_leaking_internal_temps_from_dbg() { + #[derive(Debug)] + struct Foo; + + #[derive(Debug)] + struct Bar<'a>(#[allow(unused)] &'a Foo); + impl Drop for Bar<'_> { + fn drop(&mut self) {} + } + + let foo = Foo; + let bar = Bar(&foo); + // If `dbg!` creates a `(Bar<'_>,)` temporary that lasts past its expansion, this will fail + // to compile, because it will be dropped after `foo`, which it borrows from. The tuple + // mimics the drop order of block tail expressions before Rust 2024: first the result of `dbg!` + // is dropped, then `foo`, then any temporaries left over from `dbg!` are dropped, if present. + (drop(dbg!(bar)), drop(foo)); +} + +/// Test for : +/// `dbg!` shouldn't create a temporary that borrowck thinks can live past its invocation on a false +/// unwind path. +#[test] +fn no_leaking_internal_temps_from_dbg_even_on_false_unwind() { + #[derive(Debug)] + struct Foo; + impl Drop for Foo { + fn drop(&mut self) {} + } + + #[derive(Debug)] + struct Bar<'a>(#[allow(unused)] &'a Foo); + impl Drop for Bar<'_> { + fn drop(&mut self) {} + } + + { + let foo = Foo; + let bar = Bar(&foo); + // The temporaries of this `super let`'s scrutinee will outlive `bar` and `foo`, emulating + // the drop order of block tail expressions before Rust 2024. If borrowck thinks that a + // panic from moving `bar` is possible and that a `Bar<'_>`-containing temporary lives past + // the end of the block because of that, this will fail to compile. Because `Foo` implements + // `Drop`, it's an error for `foo` to be dropped before such a temporary when unwinding; + // otherwise, `foo` would just live to the end of the stack frame when unwinding. + super let _ = drop(dbg!(bar)); + } +} From afc399f1d06eaba77d5ab816086e5d649d7b8380 Mon Sep 17 00:00:00 2001 From: LimpSquid Date: Fri, 15 May 2026 10:15:58 +0200 Subject: [PATCH 038/174] Stabilize bool_to_result --- library/core/src/bool.rs | 12 ++---------- library/coretests/tests/lib.rs | 1 - 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/library/core/src/bool.rs b/library/core/src/bool.rs index 9b9d2f02550bd..751b8d0f1b762 100644 --- a/library/core/src/bool.rs +++ b/library/core/src/bool.rs @@ -78,15 +78,11 @@ impl bool { /// # Examples /// /// ``` - /// #![feature(bool_to_result)] - /// /// assert_eq!(false.ok_or(0), Err(0)); /// assert_eq!(true.ok_or(0), Ok(())); /// ``` /// /// ``` - /// #![feature(bool_to_result)] - /// /// let mut a = 0; /// let mut function_with_side_effects = || { a += 1; }; /// @@ -97,7 +93,7 @@ impl bool { /// // evaluated eagerly. /// assert_eq!(a, 2); /// ``` - #[unstable(feature = "bool_to_result", issue = "142748")] + #[stable(feature = "bool_to_result", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] pub const fn ok_or(self, err: E) -> Result<(), E> { @@ -110,15 +106,11 @@ impl bool { /// # Examples /// /// ``` - /// #![feature(bool_to_result)] - /// /// assert_eq!(false.ok_or_else(|| 0), Err(0)); /// assert_eq!(true.ok_or_else(|| 0), Ok(())); /// ``` /// /// ``` - /// #![feature(bool_to_result)] - /// /// let mut a = 0; /// /// assert!(true.ok_or_else(|| { a += 1; }).is_ok()); @@ -128,7 +120,7 @@ impl bool { /// // `ok_or_else`. /// assert_eq!(a, 1); /// ``` - #[unstable(feature = "bool_to_result", issue = "142748")] + #[stable(feature = "bool_to_result", since = "CURRENT_RUSTC_VERSION")] #[rustc_const_unstable(feature = "const_bool", issue = "151531")] #[inline] pub const fn ok_or_else E + [const] Destruct>( diff --git a/library/coretests/tests/lib.rs b/library/coretests/tests/lib.rs index 12b81fea9d27c..6fed117521f85 100644 --- a/library/coretests/tests/lib.rs +++ b/library/coretests/tests/lib.rs @@ -8,7 +8,6 @@ #![feature(ascii_char_variants)] #![feature(async_iter_from_iter)] #![feature(async_iterator)] -#![feature(bool_to_result)] #![feature(borrowed_buf_init)] #![feature(bstr)] #![feature(cfg_target_has_reliable_f16_f128)] From 0453288ecc3c494781bdb896bf9af5af4e90cf13 Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 15 May 2026 09:02:44 +0100 Subject: [PATCH 039/174] Fix for new error message --- tests/ui/target-feature/invalid-attribute.rs | 2 +- tests/ui/target-feature/invalid-attribute.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/target-feature/invalid-attribute.rs b/tests/ui/target-feature/invalid-attribute.rs index ee1e54ac239c0..968fbdc1dc7f1 100644 --- a/tests/ui/target-feature/invalid-attribute.rs +++ b/tests/ui/target-feature/invalid-attribute.rs @@ -67,7 +67,7 @@ trait Baz {} #[inline(always)] //~^ ERROR: cannot use `#[inline(always)]` -//~| NOTE: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 +//~| NOTE: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 #[target_feature(enable = "sse2")] unsafe fn test() {} diff --git a/tests/ui/target-feature/invalid-attribute.stderr b/tests/ui/target-feature/invalid-attribute.stderr index a4248f5ced9e0..cb19bdc60ceb4 100644 --- a/tests/ui/target-feature/invalid-attribute.stderr +++ b/tests/ui/target-feature/invalid-attribute.stderr @@ -166,7 +166,7 @@ error: cannot use `#[inline(always)]` with `#[target_feature]` LL | #[inline(always)] | ^^^^^^^^^^^^^^^^^ | - = note: See issue here for full discussion; https://github.com/rust-lang/rust/issues/145574 + = note: See this issue for full discussion: https://github.com/rust-lang/rust/issues/145574 error: the feature named `foo` is not valid for this target --> $DIR/invalid-attribute.rs:26:18 From 405214dd924835aa43367565dbacaf69609c2e4b Mon Sep 17 00:00:00 2001 From: James Barford-Evans Date: Fri, 15 May 2026 10:30:01 +0100 Subject: [PATCH 040/174] PR feedback - revert `fn inline_attr(...)` to how it was prior to inlinealways + target feature changes, factoring in subsequent codebase changes --- compiler/rustc_codegen_llvm/src/attributes.rs | 31 +++++++++---------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index 0ea4bd60d69a9..ae14a72191d0e 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -42,20 +42,12 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { /// Get LLVM attribute for the provided inline heuristic. #[inline] -fn inline_attr<'ll, 'tcx>( +fn inline_attr<'ll>( cx: &SimpleCx<'ll>, - tcx: TyCtxt<'tcx>, - instance: ty::Instance<'tcx>, + sess: &Session, + inline: InlineAttr, ) -> Option<&'ll Attribute> { - // `optnone` requires `noinline` - let codegen_fn_attrs = tcx.codegen_fn_attrs(instance.def_id()); - let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { - (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, - (inline, _) => inline, - }; - - if !tcx.sess.opts.unstable_opts.inline_llvm { + if !sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); } @@ -65,7 +57,7 @@ fn inline_attr<'ll, 'tcx>( Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) } InlineAttr::Never => { - if tcx.sess.target.arch != Arch::AmdGpu { + if sess.target.arch != Arch::AmdGpu { Some(AttributeKind::NoInline.create_attr(cx.llcx)) } else { None @@ -419,10 +411,15 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( OptimizeAttr::Speed => {} } - if let Some(instance) = instance - && let Some(inline_attr) = inline_attr(cx, tcx, instance) - { - to_add.push(inline_attr); + if let Some(instance) = instance { + // `optnone` requires `noinline` + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; + + to_add.extend(inline_attr(cx, sess, inline)); } if sess.must_emit_unwind_tables() { From 4becc8179d9ab338bce489ba127a44fa5b73123b Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Fri, 15 May 2026 22:49:19 +0200 Subject: [PATCH 041/174] refactor thread block timeout handling --- src/tools/miri/src/clock.rs | 81 ++++++++++++- src/tools/miri/src/concurrency/blocking_io.rs | 4 +- src/tools/miri/src/concurrency/sync.rs | 9 +- src/tools/miri/src/concurrency/thread.rs | 110 +++++------------- src/tools/miri/src/lib.rs | 4 +- src/tools/miri/src/shims/time.rs | 23 ++-- src/tools/miri/src/shims/unix/freebsd/sync.rs | 22 ++-- .../miri/src/shims/unix/linux_like/epoll.rs | 8 +- .../miri/src/shims/unix/linux_like/sync.rs | 12 +- src/tools/miri/src/shims/unix/macos/sync.rs | 12 +- src/tools/miri/src/shims/unix/socket.rs | 10 +- src/tools/miri/src/shims/unix/sync.rs | 8 +- src/tools/miri/src/shims/windows/sync.rs | 6 +- 13 files changed, 172 insertions(+), 137 deletions(-) diff --git a/src/tools/miri/src/clock.rs b/src/tools/miri/src/clock.rs index 47608f873a481..6556975095528 100644 --- a/src/tools/miri/src/clock.rs +++ b/src/tools/miri/src/clock.rs @@ -1,5 +1,7 @@ use std::cell::Cell; -use std::time::{Duration, Instant as StdInstant}; +use std::time::{Duration, Instant as StdInstant, SystemTime}; + +use crate::MiriMachine; /// When using a virtual clock, this defines how many nanoseconds we pretend are passing for each /// basic block. @@ -8,6 +10,7 @@ use std::time::{Duration, Instant as StdInstant}; /// (See `tests/pass/shims/time-with-isolation*.rs`.) const NANOSECONDS_PER_BASIC_BLOCK: u128 = 5000; +/// An instant (a fixed moment in time) in Miri's monotone clock. #[derive(Debug)] pub struct Instant { kind: InstantKind, @@ -129,3 +132,79 @@ impl MonotonicClock { } } } + +/// A deadline for some event to occur. +#[derive(Debug)] +pub enum Deadline { + Monotonic(Instant), + RealTime(SystemTime), +} + +impl From for Deadline { + fn from(value: Instant) -> Self { + Deadline::Monotonic(value) + } +} + +impl Deadline { + /// Will try to add `duration`, but if that overflows it may add less. + fn add_lossy(&self, duration: Duration) -> Self { + match self { + Deadline::Monotonic(i) => Deadline::Monotonic(i.add_lossy(duration)), + Deadline::RealTime(s) => { + // If this overflows, try adding just 1h and assume that will not overflow. + Deadline::RealTime( + s.checked_add(duration) + .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()), + ) + } + } + } +} + +/// The clock to use for the timeout you are asking for. +#[derive(Debug, Copy, Clone, PartialEq)] +pub enum TimeoutClock { + /// The timeout is measured using the monotone clock. + Monotonic, + /// The timeout is measured using the host's system clock. + RealTime, +} + +/// Whether the timeout is relative or absolute. +#[derive(Debug, Copy, Clone)] +pub enum TimeoutStyle { + /// The given duration is interpreted relative to "now" for the selected clock. + Relative, + /// The given duration is interpreted as an "absolute" time, i.e., relative to the epoch of the + /// selected clock. + Absolute, +} + +impl MiriMachine<'_> { + /// Computes the deadline for a given timeout configuration and duration. + pub fn timeout( + &self, + clock: TimeoutClock, + style: TimeoutStyle, + duration: Duration, + ) -> Deadline { + // First let's figure out what "zero" means for the given clock and style. + let zero = match clock { + TimeoutClock::RealTime => { + assert!(self.communicate(), "cannot have `RealTime` timeout with isolation"); + Deadline::RealTime(match style { + TimeoutStyle::Absolute => SystemTime::UNIX_EPOCH, + TimeoutStyle::Relative => SystemTime::now(), + }) + } + TimeoutClock::Monotonic => + Deadline::Monotonic(match style { + TimeoutStyle::Absolute => self.monotonic_clock.epoch(), + TimeoutStyle::Relative => self.monotonic_clock.now(), + }), + }; + // Then add the given duration relative to that "zero". + zero.add_lossy(duration) + } +} diff --git a/src/tools/miri/src/concurrency/blocking_io.rs b/src/tools/miri/src/concurrency/blocking_io.rs index 9dc9bbfd1f1d2..c16aecedb3340 100644 --- a/src/tools/miri/src/concurrency/blocking_io.rs +++ b/src/tools/miri/src/concurrency/blocking_io.rs @@ -355,7 +355,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { &mut self, source_fd: FileDescriptionRef, interest: BlockingIoInterest, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) -> InterpResult<'tcx> { let this = self.eval_context_mut(); @@ -370,7 +370,7 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> { } else { // The I/O readiness is currently not fulfilled. We block the thread // until the readiness is fulfilled and execute the callback then. - this.block_thread(BlockReason::IO, timeout, callback); + this.block_thread(BlockReason::IO, deadline, callback); interp_ok(()) } } diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index c529ed5145edd..de2e0d8e23924 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -5,7 +5,6 @@ use std::collections::hash_map::Entry; use std::default::Default; use std::ops::Not; use std::rc::Rc; -use std::time::Duration; use std::{fmt, iter}; use rustc_abi::Size; @@ -684,7 +683,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, condvar_ref: CondvarRef, mutex_ref: MutexRef, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, retval_succ: Scalar, retval_timeout: Scalar, dest: MPlaceTy<'tcx>, @@ -706,7 +705,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { condvar_ref.0.borrow_mut().waiters.push_back(thread); this.block_thread( BlockReason::Condvar, - timeout, + deadline, callback!( @capture<'tcx> { condvar_ref: CondvarRef, @@ -763,7 +762,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { &mut self, futex_ref: FutexRef, bitset: u32, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); @@ -776,7 +775,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.block_thread( BlockReason::Futex, - timeout, + deadline, callback!( @capture<'tcx> { futex_ref: FutexRef, diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index 3d4210276ceeb..2b293a59ddb70 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -125,7 +125,7 @@ enum ThreadState<'tcx> { /// The thread is enabled and can be executed. Enabled, /// The thread is blocked on something. - Blocked { reason: BlockReason, timeout: Option, callback: DynUnblockCallback<'tcx> }, + Blocked { reason: BlockReason, deadline: Option, callback: DynUnblockCallback<'tcx> }, /// The thread has terminated its execution. We do not delete terminated /// threads (FIXME: why?). Terminated, @@ -135,8 +135,11 @@ impl<'tcx> std::fmt::Debug for ThreadState<'tcx> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Self::Enabled => write!(f, "Enabled"), - Self::Blocked { reason, timeout, .. } => - f.debug_struct("Blocked").field("reason", reason).field("timeout", timeout).finish(), + Self::Blocked { reason, deadline, .. } => + f.debug_struct("Blocked") + .field("reason", reason) + .field("deadline", deadline) + .finish(), Self::Terminated => write!(f, "Terminated"), } } @@ -375,52 +378,6 @@ impl VisitProvenance for Frame<'_, Provenance, FrameExtra<'_>> { } } -/// The moment in time when a blocked thread should be woken up. -#[derive(Debug)] -enum Timeout { - Monotonic(Instant), - RealTime(SystemTime), -} - -impl Timeout { - /// How long do we have to wait from now until the specified time? - fn get_wait_time(&self, clock: &MonotonicClock) -> Duration { - match self { - Timeout::Monotonic(instant) => instant.duration_since(clock.now()), - Timeout::RealTime(time) => - time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO), - } - } - - /// Will try to add `duration`, but if that overflows it may add less. - fn add_lossy(&self, duration: Duration) -> Self { - match self { - Timeout::Monotonic(i) => Timeout::Monotonic(i.add_lossy(duration)), - Timeout::RealTime(s) => { - // If this overflows, try adding just 1h and assume that will not overflow. - Timeout::RealTime( - s.checked_add(duration) - .unwrap_or_else(|| s.checked_add(Duration::from_secs(3600)).unwrap()), - ) - } - } - } -} - -/// The clock to use for the timeout you are asking for. -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum TimeoutClock { - Monotonic, - RealTime, -} - -/// Whether the timeout is relative or absolute. -#[derive(Debug, Copy, Clone)] -pub enum TimeoutAnchor { - Relative, - Absolute, -} - /// An error signaling that the requested thread doesn't exist or has terminated. #[derive(Debug, Copy, Clone)] pub enum ThreadLookupError { @@ -655,12 +612,12 @@ impl<'tcx> ThreadManager<'tcx> { fn block_thread( &mut self, reason: BlockReason, - timeout: Option, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let state = &mut self.threads[self.active_thread].state; assert!(state.is_enabled()); - *state = ThreadState::Blocked { reason, timeout, callback } + *state = ThreadState::Blocked { reason, deadline, callback } } /// Change the active thread to some enabled thread. @@ -755,7 +712,7 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { // `pthread_cond_timedwait`, "an error is returned if [...] the absolute time specified by // abstime has already been passed at the time of the call". // - let potential_sleep_time = this.unblock_expired_timeouts()?; + let potential_sleep_time = this.unblock_expired_deadlines()?; let thread_manager = &mut this.machine.threads; let rng = this.machine.rng.get_mut(); @@ -856,19 +813,27 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> { /// Find all threads with expired timeouts, unblock them and execute their timeout callbacks. /// - /// This method returns the minimum duration until the next thread timeout expires. - /// If all ready threads have no timeout set, [`None`] is returned. - fn unblock_expired_timeouts(&mut self) -> InterpResult<'tcx, Option> { + /// This method returns the minimum duration until the next thread deadline. + /// If all ready threads have no deadline set, [`None`] is returned. + fn unblock_expired_deadlines(&mut self) -> InterpResult<'tcx, Option> { let this = self.eval_context_mut(); - let clock = &this.machine.monotonic_clock; + let communicate = this.machine.communicate(); let mut min_wait_time = Option::::None; let mut callbacks = Vec::new(); for (id, thread) in this.machine.threads.threads.iter_enumerated_mut() { match &thread.state { - ThreadState::Blocked { timeout: Some(timeout), .. } => { - let wait_time = timeout.get_wait_time(clock); + ThreadState::Blocked { deadline: Some(deadline), .. } => { + let wait_time = match deadline { + Deadline::Monotonic(instant) => + instant.duration_since(this.machine.monotonic_clock.now()), + Deadline::RealTime(time) => { + assert!(communicate, "cannot have `RealTime` timeout with isolation"); + time.duration_since(SystemTime::now()).unwrap_or(Duration::ZERO) + } + }; + if wait_time.is_zero() { // The timeout expired for this thread. let old_state = mem::replace(&mut thread.state, ThreadState::Enabled); @@ -1098,34 +1063,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { fn block_thread( &mut self, reason: BlockReason, - timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>, + deadline: Option, callback: DynUnblockCallback<'tcx>, ) { let this = self.eval_context_mut(); - if timeout.is_some() && this.machine.data_race.as_genmc_ref().is_some() { + if deadline.is_some() && this.machine.data_race.as_genmc_ref().is_some() { panic!("Unimplemented: Timeouts not yet supported in GenMC mode."); } - let timeout = timeout.map(|(clock, anchor, duration)| { - let anchor = match clock { - TimeoutClock::RealTime => { - assert!( - this.machine.communicate(), - "cannot have `RealTime` timeout with isolation enabled!" - ); - Timeout::RealTime(match anchor { - TimeoutAnchor::Absolute => SystemTime::UNIX_EPOCH, - TimeoutAnchor::Relative => SystemTime::now(), - }) - } - TimeoutClock::Monotonic => - Timeout::Monotonic(match anchor { - TimeoutAnchor::Absolute => this.machine.monotonic_clock.epoch(), - TimeoutAnchor::Relative => this.machine.monotonic_clock.now(), - }), - }; - anchor.add_lossy(duration) - }); - this.machine.threads.block_thread(reason, timeout, callback); + if matches!(deadline, Some(Deadline::RealTime(_))) && !this.machine.communicate() { + panic!("cannot have `RealTime` timeout with isolation"); + } + this.machine.threads.block_thread(reason, deadline, callback); } /// Put the blocked thread into the enabled state. diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs index 944ff933ac9ba..160f31d50c4bf 100644 --- a/src/tools/miri/src/lib.rs +++ b/src/tools/miri/src/lib.rs @@ -133,7 +133,7 @@ pub use crate::borrow_tracker::tree_borrows::{EvalContextExt as _, Tree}; pub use crate::borrow_tracker::{ BorTag, BorrowTrackerMethod, EvalContextExt as _, TreeBorrowsParams, }; -pub use crate::clock::{Instant, MonotonicClock}; +pub use crate::clock::{Deadline, Instant, MonotonicClock, TimeoutClock, TimeoutStyle}; pub use crate::concurrency::blocking_io::{ BlockingIoInterest, BlockingIoManager, BlockingIoSourceReadiness, EvalContextExt as _, SourceFileDescription, @@ -146,7 +146,7 @@ pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceRef}; pub use crate::concurrency::sync::{CondvarRef, EvalContextExt as _, MutexRef, RwLockRef}; pub use crate::concurrency::thread::{ BlockReason, DynUnblockCallback, EvalContextExt as _, StackEmptyCallback, ThreadId, - ThreadManager, TimeoutAnchor, TimeoutClock, UnblockKind, + ThreadManager, UnblockKind, }; pub use crate::concurrency::{GenmcConfig, GenmcCtx, run_genmc_mode}; pub use crate::data_structures::dedup_range_map::DedupRangeMap; diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index 66bf50e5f18d2..87dded0766ad8 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -334,11 +334,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let deadline = this.read_scalar(deadline_op)?.to_u64()?; // Our mach_absolute_time "ticks" are plain nanoseconds. - let duration = Duration::from_nanos(deadline); + let deadline = Duration::from_nanos(deadline); + // This is *absolute* time. + let deadline = this.machine.monotonic_clock.epoch().add_lossy(deadline); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -362,10 +364,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let Some(duration) = this.read_timespec(&duration)? else { return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; + let deadline = this.machine.monotonic_clock.now().add_lossy(duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -401,14 +404,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return_i32(LibcError("EINVAL")); }; - let timeout_anchor = if flags == 0 { + let timeout_style = if flags == 0 { // No flags set, the timespec should be interpreted as a duration - // to sleep for - TimeoutAnchor::Relative + // to sleep for, i.e., a relative time. + TimeoutStyle::Relative } else if flags == this.eval_libc_i32("TIMER_ABSTIME") { // Only flag TIMER_ABSTIME set, the timespec should be interpreted as // an absolute time. - TimeoutAnchor::Absolute + TimeoutStyle::Absolute } else { // The standard lib (through `sleep_until`) only needs TIMER_ABSTIME throw_unsup_format!( @@ -416,10 +419,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { TIMER_ABSTIME is supported" ); }; + let deadline = this.machine.timeout(TimeoutClock::Monotonic, timeout_style, duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, timeout_anchor, duration)), + Some(deadline), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { @@ -440,10 +444,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let timeout_ms = this.read_scalar(timeout)?.to_u32()?; let duration = Duration::from_millis(timeout_ms.into()); + let deadline = this.machine.monotonic_clock.now().add_lossy(duration); this.block_thread( BlockReason::Sleep, - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)), + Some(deadline.into()), callback!( @capture<'tcx> {} |_this, unblock: UnblockKind| { diff --git a/src/tools/miri/src/shims/unix/freebsd/sync.rs b/src/tools/miri/src/shims/unix/freebsd/sync.rs index 7c46dd549bc0a..32338391f2dd8 100644 --- a/src/tools/miri/src/shims/unix/freebsd/sync.rs +++ b/src/tools/miri/src/shims/unix/freebsd/sync.rs @@ -93,7 +93,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // extended variant, `struct _umtx_time`, as the `uaddr2` argument of _umtx_op(). // They are distinguished by the `uaddr` value, which must be equal // to the size of the structure pointed to by `uaddr2`, casted to uintptr_t. - let timeout = if this.ptr_is_null(uaddr2)? { + let deadline = if this.ptr_is_null(uaddr2)? { // no timeout parameter None } else { @@ -105,13 +105,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EINVAL"), dest); }; - let anchor = if umtx_time.abs_time { - TimeoutAnchor::Absolute + let style = if umtx_time.abs_time { + TimeoutStyle::Absolute } else { - TimeoutAnchor::Relative + TimeoutStyle::Relative }; - Some((umtx_time.timeout_clock, anchor, umtx_time.timeout)) + Some(this.machine.timeout( + umtx_time.timeout_clock, + style, + umtx_time.timeout, + )) } else if uaddr == timespec_layout.size.bytes() { // RealTime clock can't be used in isolation mode. this.check_no_isolation("`_umtx_op` with `timespec` timeout")?; @@ -127,7 +131,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // code (umtx_copyin_umtx_time() in kern_umtx.c), it seems to default to CLOCK_REALTIME, // so that's what we also do. // Discussion in golang: https://github.com/golang/go/issues/17168#issuecomment-250235271 - Some((TimeoutClock::RealTime, TimeoutAnchor::Relative, duration)) + Some(this.machine.timeout( + TimeoutClock::RealTime, + TimeoutStyle::Relative, + duration, + )) } else { return this.set_last_error_and_return(LibcError("EINVAL"), dest); } @@ -137,7 +145,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref, u32::MAX, // we set the bitset to include all bits - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/linux_like/epoll.rs b/src/tools/miri/src/shims/unix/linux_like/epoll.rs index 1758c04c8981e..d2d978bffee7d 100644 --- a/src/tools/miri/src/shims/unix/linux_like/epoll.rs +++ b/src/tools/miri/src/shims/unix/linux_like/epoll.rs @@ -473,11 +473,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If the timeout is 0 or there is a ready event, we can return immediately. return_ready_list(&epfd, dest, &event, this)?; } else { - // Blocking - let timeout = match timeout { + // Blocking, with a relative timeout. + let deadline = match timeout { 0.. => { let duration = Duration::from_millis(timeout.try_into().unwrap()); - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)) + Some(this.machine.monotonic_clock.now().add_lossy(duration).into()) } -1 => None, ..-1 => { @@ -495,7 +495,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // a thread is permanently blocked so this is fine. this.block_thread( BlockReason::Epoll { epfd: epfd.clone() }, - timeout, + deadline, callback!( @capture<'tcx> { epfd: FileDescriptionRef, diff --git a/src/tools/miri/src/shims/unix/linux_like/sync.rs b/src/tools/miri/src/shims/unix/linux_like/sync.rs index 00df4ee2b2ae3..5a1a9ed535941 100644 --- a/src/tools/miri/src/shims/unix/linux_like/sync.rs +++ b/src/tools/miri/src/shims/unix/linux_like/sync.rs @@ -68,7 +68,7 @@ pub fn futex<'tcx>( } let timeout = ecx.deref_pointer_as(timeout, ecx.libc_ty_layout("timespec"))?; - let timeout = if ecx.ptr_is_null(timeout.ptr())? { + let deadline = if ecx.ptr_is_null(timeout.ptr())? { None } else { let Some(duration) = ecx.read_timespec(&timeout)? else { @@ -82,14 +82,14 @@ pub fn futex<'tcx>( } else { TimeoutClock::Monotonic }; - let timeout_anchor = if wait_bitset { + let timeout_style = if wait_bitset { // FUTEX_WAIT_BITSET uses an absolute timestamp. - TimeoutAnchor::Absolute + TimeoutStyle::Absolute } else { // FUTEX_WAIT uses a relative timestamp. - TimeoutAnchor::Relative + TimeoutStyle::Relative }; - Some((timeout_clock, timeout_anchor, duration)) + Some(ecx.machine.timeout(timeout_clock, timeout_style, duration)) }; // There may be a concurrent thread changing the value of addr // and then invoking the FUTEX_WAKE syscall. It is critical that the @@ -154,7 +154,7 @@ pub fn futex<'tcx>( ecx.futex_wait( futex_ref, bitset, - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs index 6ee9ffaf37762..ea0fd5c5b0a3c 100644 --- a/src/tools/miri/src/shims/unix/macos/sync.rs +++ b/src/tools/miri/src/shims/unix/macos/sync.rs @@ -145,12 +145,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { MacOsFutexTimeout::Relative { clock_op, timeout_op } => { let clock = this.read_scalar(clock_op)?.to_u32()?; let timeout = this.read_scalar(timeout_op)?.to_u64()?; - Some((clock, TimeoutAnchor::Relative, timeout)) + Some((clock, TimeoutStyle::Relative, timeout)) } MacOsFutexTimeout::Absolute { clock_op, timeout_op } => { let clock = this.read_scalar(clock_op)?.to_u32()?; let timeout = this.read_scalar(timeout_op)?.to_u64()?; - Some((clock, TimeoutAnchor::Absolute, timeout)) + Some((clock, TimeoutStyle::Absolute, timeout)) } }; @@ -168,12 +168,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } let is_shared = flags == shared; - let timeout = clock_timeout.map(|(_, anchor, timeout)| { - // The only clock that is currently supported is the monotonic clock. + let deadline = clock_timeout.map(|(_, style, timeout)| { + // The only clock that is currently supported is the monotonic clock (checked above). // While the deadline argument of `os_sync_wait_on_address_with_deadline` // is actually not in nanoseconds but in the units of `mach_current_time`, // the two are equivalent in miri. - (TimeoutClock::Monotonic, anchor, Duration::from_nanos(timeout)) + this.machine.timeout(TimeoutClock::Monotonic, style, Duration::from_nanos(timeout)) }); // See the Linux futex implementation for why this fence exists. @@ -213,7 +213,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref.clone(), u32::MAX, // bitset - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx>, diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index dbf2ebb37dd4e..a700c263854b6 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -3,7 +3,6 @@ use std::io; use std::io::Read; use std::net::{Ipv4Addr, Shutdown, SocketAddr, SocketAddrV4}; use std::sync::atomic::AtomicBool; -use std::time::Duration; use mio::event::Source; use mio::net::{TcpListener, TcpStream}; @@ -1780,16 +1779,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // If we should wait until the connection is established, the timeout is `None`. // Otherwise, we use a zero duration timeout, i.e. we return immediately // (but we still go through the scheduler once -- which is fine). - let timeout = if should_wait { - None - } else { - Some((TimeoutClock::Monotonic, TimeoutAnchor::Absolute, Duration::ZERO)) - }; + let deadline = + if should_wait { None } else { Some(this.machine.monotonic_clock.now().into()) }; this.block_thread_for_io( socket.clone(), BlockingIoInterest::Write, - timeout, + deadline, callback!( @capture<'tcx> { socket: FileDescriptionRef, diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs index 1b65dea982464..4e351c1571218 100644 --- a/src/tools/miri/src/shims/unix/sync.rs +++ b/src/tools/miri/src/shims/unix/sync.rs @@ -920,22 +920,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return interp_ok(()); }; - let (clock, anchor) = if macos_relative_np { + let (clock, style) = if macos_relative_np { // `pthread_cond_timedwait_relative_np` always measures time against the // monotonic clock, regardless of the condvar clock. - (TimeoutClock::Monotonic, TimeoutAnchor::Relative) + (TimeoutClock::Monotonic, TimeoutStyle::Relative) } else { if data.clock == TimeoutClock::RealTime { this.check_no_isolation("`pthread_cond_timedwait` with `CLOCK_REALTIME`")?; } - (data.clock, TimeoutAnchor::Absolute) + (data.clock, TimeoutStyle::Absolute) }; this.condvar_wait( data.condvar_ref, mutex_ref, - Some((clock, anchor, duration)), + Some(this.machine.timeout(clock, style, duration)), Scalar::from_i32(0), this.eval_libc("ETIMEDOUT"), // retval_timeout dest.clone(), diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 14562450e6e07..1b75d6647c831 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -209,11 +209,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; let size = Size::from_bytes(size); - let timeout = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { + let deadline = if timeout_ms == this.eval_windows_u32("c", "INFINITE") { None } else { let duration = Duration::from_millis(timeout_ms.into()); - Some((TimeoutClock::Monotonic, TimeoutAnchor::Relative, duration)) + Some(this.machine.monotonic_clock.now().add_lossy(duration).into()) }; // See the Linux futex implementation for why this fence exists. @@ -238,7 +238,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.futex_wait( futex_ref, u32::MAX, // bitset - timeout, + deadline, callback!( @capture<'tcx> { dest: MPlaceTy<'tcx> From 170e6eb7be8940c93e3027b893dda47d25940e31 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 12:20:01 +0200 Subject: [PATCH 042/174] add --native flag to './miri run' to run tests on the host --- src/tools/miri/miri-script/src/commands.rs | 40 ++++++----- src/tools/miri/miri-script/src/main.rs | 4 ++ src/tools/miri/tests/ui.rs | 77 +++++++++++++++++----- src/tools/miri/tests/utils/miri_extern.rs | 35 ++++++++++ 4 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs index d469502c243f4..24fe5b66bb0b4 100644 --- a/src/tools/miri/miri-script/src/commands.rs +++ b/src/tools/miri/miri-script/src/commands.rs @@ -114,8 +114,8 @@ impl Command { Command::Check { features, flags } => Self::check(features, flags), Command::Test { bless, target, coverage, features, flags } => Self::test(bless, target, coverage, features, flags), - Command::Run { dep, quiet, target, edition, features, flags } => - Self::run(dep, quiet, target, edition, features, flags), + Command::Run { dep, native, quiet, target, edition, features, flags } => + Self::run(dep, native, quiet, target, edition, features, flags), Command::Doc { features, flags } => Self::doc(features, flags), Command::Fmt { flags } => Self::fmt(flags), Command::Clippy { features, flags } => Self::clippy(features, flags), @@ -465,6 +465,7 @@ impl Command { fn run( dep: bool, + native: bool, quiet: bool, target: Option, edition: Option, @@ -472,8 +473,10 @@ impl Command { flags: Vec, ) -> Result<()> { let mut e = MiriEnv::new()?; + let run_via_ui_test = dep || native; // Preparation: get a sysroot, and get the miri binary. + // We do this even for native run as it also builds Miri itself. let miri_sysroot = e.build_miri_sysroot(/* quiet */ quiet, target.as_deref(), &features)?; let miri_bin = e @@ -484,8 +487,8 @@ impl Command { // (because `flags` may contain `--`). let mut early_flags = Vec::::new(); - // In `dep` mode, the target is already passed via `MIRI_TEST_TARGET` - if !dep { + // In ui_test mode, the target is already passed via `MIRI_TEST_TARGET` + if !run_via_ui_test { if let Some(target) = &target { early_flags.push("--target".into()); early_flags.push(target.into()); @@ -493,35 +496,36 @@ impl Command { } early_flags.push("--edition".into()); early_flags.push(edition.as_deref().unwrap_or("2021").into()); - early_flags.push("--sysroot".into()); - early_flags.push(miri_sysroot.into()); + if !native { + early_flags.push("--sysroot".into()); + early_flags.push(miri_sysroot.into()); + } // Compute flags. let miri_flags = e.sh.var("MIRIFLAGS").unwrap_or_default(); let miri_flags = flagsplit(&miri_flags); - let quiet_flag = if quiet { Some("--quiet") } else { None }; // Run Miri. // The basic command that executes the Miri driver. - let mut cmd = if dep { + let mut cmd = if run_via_ui_test { // We invoke the test suite as that has all the logic for running with dependencies. - e.cargo_cmd(".", "test", &features) + let mut cmd = e + .cargo_cmd(".", "test", &features) .args(&["--test", "ui"]) - .args(quiet_flag) + // This does not show anything useful so we always hide it. + .arg("--quiet") .arg("--") - .args(&["--miri-run-dep-mode"]) + .env("MIRI_RUN_MODE", if native { "native" } else { "1" }); + if let Some(target) = &target { + cmd = cmd.env("MIRI_TEST_TARGET", target); + } + cmd } else { cmd!(e.sh, "{miri_bin}") }; cmd.set_quiet(quiet); // Add Miri flags - let mut cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); - // For `--dep` we also need to set the target in the env var. - if dep { - if let Some(target) = &target { - cmd = cmd.env("MIRI_TEST_TARGET", target); - } - } + let cmd = cmd.args(&miri_flags).args(&early_flags).args(&flags); // Finally, run the thing. Ok(cmd.run()?) } diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs index 419c128e5b72f..b575842fc6c78 100644 --- a/src/tools/miri/miri-script/src/main.rs +++ b/src/tools/miri/miri-script/src/main.rs @@ -78,6 +78,10 @@ pub enum Command { /// Build the program with the dependencies declared in `tests/deps/Cargo.toml`. #[arg(long)] dep: bool, + /// Compile and run the program natively instead of via Miri. Implies `--dep`. + /// All flags are passed to rustc; there is currently no way to pass flags to the program. + #[arg(long)] + native: bool, /// Hide build progress. #[arg(long, short)] quiet: bool, diff --git a/src/tools/miri/tests/ui.rs b/src/tools/miri/tests/ui.rs index 96c0ccb7d6974..1449013e0478e 100644 --- a/src/tools/miri/tests/ui.rs +++ b/src/tools/miri/tests/ui.rs @@ -1,5 +1,4 @@ use std::env; -use std::ffi::OsString; use std::num::NonZero; use std::path::{Path, PathBuf}; use std::process::Command; @@ -330,13 +329,9 @@ fn main() -> Result<()> { let target = get_target(&host); let tmpdir = tempfile::Builder::new().prefix("miri-uitest-").tempdir()?; - let mut args = std::env::args_os(); - - // Skip the program name and check whether this is a `./miri run-dep` invocation - if let Some(first) = args.nth(1) - && first == "--miri-run-dep-mode" - { - return run_dep_mode(target, args); + // Check whether this is a `./miri run` invocation + if let Ok(mode) = env::var("MIRI_RUN_MODE") { + return run_mode(target, mode); } ui(Mode::Pass, "tests/pass", &target, WithoutDependencies, tmpdir.path())?; @@ -366,21 +361,67 @@ fn main() -> Result<()> { Ok(()) } -fn run_dep_mode(target: String, args: impl Iterator) -> Result<()> { +fn run_mode(target: String, mode: String) -> Result<()> { + let native = mode == "native"; + let mut config = miri_config(&target, "", Mode::RunDep, Some(WithDependencies { bless: false })); config.comment_defaults.base().custom.remove("edition"); // `./miri` adds an `--edition` in `args`, so don't set it twice + if native { + // Patch things up so that we actually compile the program. + config.program.envs.push(("MIRI_BE_RUSTC".into(), Some("host".into()))); + config.comment_defaults.base().set_custom( + "dependencies", + DependencyBuilder { + crate_manifest_path: Path::new("tests/deps").join("Cargo.toml"), + ..Default::default() + }, + ); + } config.fill_host_and_target()?; - let dep_builder = BuildManager::one_off(config.clone()); - // Only set these for the actual run, not the dep builder, so invalid flags do not fail - // the dependency build. - config.program.args = args.collect(); - let test_config = TestConfig::one_off_runner(config, PathBuf::new()); + // Reset `args` (otherwise we'll get JSON output). + config.program.args = vec![]; + // Compute the actual Miri invocation command. + let test_config = TestConfig::one_off_runner(config.clone(), PathBuf::new()); let mut cmd = test_config.config.program.build(&test_config.config.out_dir); - cmd.arg("--target").arg(test_config.config.target.as_ref().unwrap()); - // Build dependencies - test_config.apply_custom(&mut cmd, &dep_builder).expect("failed to build dependencies"); + // For some reason we need to set the target ourselves. + cmd.arg("--target").arg(&target); + // Also forward arguments to the program (skipping the binary name). + // We don't put this in the `config` since we don't want it to affect the dependency build. + cmd.args({ + let mut args = env::args_os(); + args.next().unwrap(); + args + }); + + // Build dependencies (which will mutate that command) + test_config + .apply_custom(&mut cmd, &BuildManager::one_off(config.clone())) + .expect("failed to build dependencies"); + // Finally, actually run Miri. + let exit_status = cmd.spawn()?.wait()?; + if !exit_status.success() { + std::process::exit(1) + } - if cmd.spawn()?.wait()?.success() { Ok(()) } else { std::process::exit(1) } + if native { + // We just built the program, we still have to run it. We can't use the ui_test `Run` flag + // as that needs an actual BuildManager, not just the one-off stub we have here. So we + // implement the core logic ourselves. + + // First, figure out the output binary by re-running the compiler with `--print`. + cmd.arg("--print").arg("file-names"); + let output = cmd.output()?; + let exe = std::str::from_utf8(&output.stdout).unwrap().trim(); + let exe = config.out_dir.join(exe); + // Then run that binary. + let mut cmd = Command::new(exe); + let exit_status = cmd.spawn()?.wait()?; + if !exit_status.success() { + std::process::exit(1) + } + } + + Ok(()) } diff --git a/src/tools/miri/tests/utils/miri_extern.rs b/src/tools/miri/tests/utils/miri_extern.rs index e9cde20412f49..3cc4610c6b0a3 100644 --- a/src/tools/miri/tests/utils/miri_extern.rs +++ b/src/tools/miri/tests/utils/miri_extern.rs @@ -172,3 +172,38 @@ extern "Rust" { /// As far as Miri is concerned, this is equivalent to `yield_now`. pub fn miri_spin_loop(); } + +// Stubs so we can run things without Miri. +#[cfg(not(miri))] +mod stubs { + use std::ffi::CStr; + use std::io::Write; + + pub unsafe fn miri_write_to_stdout(bytes: &[u8]) { + std::io::stdout().write_all(bytes).unwrap() + } + + pub unsafe fn miri_write_to_stderr(bytes: &[u8]) { + std::io::stderr().write_all(bytes).unwrap() + } + + pub unsafe fn miri_host_to_target_path( + path: *const core::ffi::c_char, + out: *mut core::ffi::c_char, + out_size: usize, + ) -> usize { + let path = CStr::from_ptr(path); + let len = path.count_bytes() + 1; + if out_size >= len { + out.copy_from(path.as_ptr(), len); + 0 + } else { + len + } + } + + pub unsafe fn miri_run_provenance_gc() {} +} + +#[cfg(not(miri))] +pub use stubs::*; From a2400e2ecc438852d68f152d4a6d9df6c1092c0f Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 12:53:58 +0200 Subject: [PATCH 043/174] fix eventfd writes with too big buffers --- .../miri/src/shims/unix/linux_like/eventfd.rs | 5 +- .../miri/tests/pass-dep/libc/libc-eventfd.rs | 96 +++++++++---------- 2 files changed, 51 insertions(+), 50 deletions(-) diff --git a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs index 76374ca24d0da..ee318a265fd30 100644 --- a/src/tools/miri/src/shims/unix/linux_like/eventfd.rs +++ b/src/tools/miri/src/shims/unix/linux_like/eventfd.rs @@ -97,8 +97,9 @@ impl FileDescription for EventFd { ) -> InterpResult<'tcx> { // We're treating the buffer as a `u64`. let ty = ecx.machine.layouts.u64; - // Check the size of slice, and return error only if the size of the slice < 8. - if len < ty.layout.size.bytes_usize() { + // Check the size of slice, and return error if the size is wrong. The docs say we only + // error when the size is too small, but Linux seems to also error when the size is too big. + if len != ty.layout.size.bytes_usize() { return finish.call(ecx, Err(ErrorKind::InvalidInput.into())); } diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs index 56d215d0ed633..e86c70b590b36 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs @@ -5,7 +5,10 @@ // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint #![allow(static_mut_refs)] -use std::thread; +use std::{io, thread}; + +#[path = "../../utils/libc.rs"] +mod libc_utils; fn main() { test_read_write(); @@ -19,15 +22,16 @@ fn main() { test_two_threads_blocked_on_eventfd(); } -fn read_bytes(fd: i32, buf: &mut [u8; N]) -> i32 { - let res: i32 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N).try_into().unwrap() }; - return res; +// We want to do individual read/write calls here so we avoid read_exact/write_all. + +fn read_bytes(fd: i32, buf: &mut [u8; N]) -> io::Result { + libc_utils::errno_result(unsafe { libc::read(fd, buf.as_mut_ptr().cast(), N) }) + .map(|len| len.try_into().unwrap()) } -fn write_bytes(fd: i32, data: [u8; N]) -> i32 { - let res: i32 = - unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N).try_into().unwrap() }; - return res; +fn write_bytes(fd: i32, data: [u8; N]) -> io::Result { + libc_utils::errno_result(unsafe { libc::write(fd, data.as_ptr() as *const libc::c_void, N) }) + .map(|len| len.try_into().unwrap()) } fn test_read_write() { @@ -35,64 +39,60 @@ fn test_read_write() { let fd = unsafe { libc::eventfd(0, flags) }; let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes(); // Write 1 to the counter. - let res = write_bytes(fd, sized_8_data); + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); + // Write 0 to the counter, + let res = write_bytes(fd, [0u8; 8]).unwrap(); assert_eq!(res, 8); // Read 1 from the counter. let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // Read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); // Check the value of counter read. - let counter = u64::from_ne_bytes(buf); - assert_eq!(counter, 1); + assert_eq!(u64::from_ne_bytes(buf), 1); - // After read, the counter is currently 0, read counter 0 should fail with return - // value -1. + // After read, the counter is currently 0, read counter 0 should fail with EAGAIN. let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EAGAIN)); - assert_eq!(res, -1); - - // Write with supplied buffer bigger than 8 bytes should be allowed. - let sized_9_data: [u8; 9]; - if cfg!(target_endian = "big") { - // Adjust the data based on the endianness of host system. - sized_9_data = [0, 0, 0, 0, 0, 0, 0, 1, 0]; - } else { - sized_9_data = [1, 0, 0, 0, 0, 0, 0, 0, 0]; - } - let res = write_bytes(fd, sized_9_data); - assert_eq!(res, 8); + let err = read_bytes(fd, &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EAGAIN)); + + // Write with supplied buffer bigger than 8 bytes should be allowed according to the docs, + // but tests on real systems indicate that it fails. + let sized_9_data = [0u8; 9]; + let err = write_bytes(fd, sized_9_data).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); // Read with supplied buffer smaller than 8 bytes should fail with return // value -1. let mut buf: [u8; 7] = [1; 7]; - let res = read_bytes(fd, &mut buf); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = read_bytes(fd, &mut buf).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); // Write with supplied buffer smaller than 8 bytes should fail with return // value -1. let size_7_data: [u8; 7] = [1; 7]; - let res = write_bytes(fd, size_7_data); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = write_bytes(fd, size_7_data).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); + + // Do two valid writes so we have something to read for the next test. + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); + let res = write_bytes(fd, sized_8_data).unwrap(); + assert_eq!(res, 8); // Read with supplied buffer bigger than 8 bytes should be allowed. let mut buf: [u8; 9] = [1; 9]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); assert_eq!(res, 8); + let buf: &[u8; 8] = (&buf[..8]).try_into().unwrap(); + assert_eq!(u64::from_ne_bytes(*buf), 2); // Write u64::MAX should fail. let u64_max_bytes: [u8; 8] = [255; 8]; - let res = write_bytes(fd, u64_max_bytes); - let e = std::io::Error::last_os_error(); - assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); - assert_eq!(res, -1); + let err = write_bytes(fd, u64_max_bytes).unwrap_err(); + assert_eq!(err.raw_os_error(), Some(libc::EINVAL)); } fn test_race() { @@ -101,7 +101,7 @@ fn test_race() { let fd = unsafe { libc::eventfd(0, flags) }; let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -110,7 +110,7 @@ fn test_race() { }); unsafe { VAL = 1 }; let data: [u8; 8] = 1_u64.to_ne_bytes(); - let res = write_bytes(fd, data); + let res = write_bytes(fd, data).unwrap(); // write returns number of bytes written, which is always 8. assert_eq!(res, 8); thread::yield_now(); @@ -135,7 +135,7 @@ fn test_blocking_read() { let thread1 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will block. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -145,7 +145,7 @@ fn test_blocking_read() { // Pass control to thread1 so it can block on eventfd `read`. thread::yield_now(); // Write 1 to the counter to unblock thread1. - let res = write_bytes(fd, sized_8_data); + let res = write_bytes(fd, sized_8_data).unwrap(); assert_eq!(res, 8); thread1.join().unwrap(); } @@ -176,7 +176,7 @@ fn test_blocking_write() { // Pass control to thread1 so it can block on eventfd `write`. thread::yield_now(); // This will unblock previously blocked eventfd read. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); @@ -222,7 +222,7 @@ fn test_two_threads_blocked_on_eventfd() { let thread3 = thread::spawn(move || { let mut buf: [u8; 8] = [0; 8]; // This will unblock previously blocked eventfd read. - let res = read_bytes(fd, &mut buf); + let res = read_bytes(fd, &mut buf).unwrap(); // read returns number of bytes has been read, which is always 8. assert_eq!(res, 8); let counter = u64::from_ne_bytes(buf); From 68f4a9033766452e801dd4a428334b828a09e1d6 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 13:01:24 +0200 Subject: [PATCH 044/174] libc-epoll-blocking data race test: fix what we are blocking for --- .../miri/tests/pass-dep/libc/libc-epoll-blocking.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs index d9a2be7fc5014..8865c605a3b65 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-epoll-blocking.rs @@ -89,7 +89,7 @@ fn test_notification_after_timeout() { check_epoll_wait::<1>(epfd, &[Ev { events: libc::EPOLLIN | libc::EPOLLOUT, data: fds[0] }], 10); } -// This test shows a data_race before epoll had vector clocks added. +// This test shows a data race before epoll had vector clocks added. fn test_epoll_race() { // Create an epoll instance. let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); @@ -99,7 +99,7 @@ fn test_epoll_race() { let fd = errno_result(unsafe { libc::eventfd(0, flags) }).unwrap(); // Register eventfd with the epoll instance. - epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET).unwrap(); + epoll_ctl_add(epfd, fd, libc::EPOLLIN | libc::EPOLLET).unwrap(); static mut VAL: u8 = 0; let thread1 = thread::spawn(move || { @@ -109,13 +109,10 @@ fn test_epoll_race() { write_all(fd, &1_u64.to_ne_bytes()).unwrap(); }); thread::yield_now(); - // epoll_wait for the event to happen. - check_epoll_wait::<8>(epfd, &[Ev { events: (libc::EPOLLIN | libc::EPOLLOUT), data: fd }], -1); + // epoll_wait for EPOLLIN. + check_epoll_wait::<8>(epfd, &[Ev { events: libc::EPOLLIN, data: fd }], -1); // Read from the static mut variable. - #[allow(static_mut_refs)] - unsafe { - assert_eq!(VAL, 1) - }; + assert_eq!(unsafe { VAL }, 1); thread1.join().unwrap(); } From 05731b4ca2f978152d03a3a8f4772cbcbf75e474 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 13:12:05 +0200 Subject: [PATCH 045/174] libc tests: remove unused features --- src/tools/miri/tests/pass-dep/libc/libc-affinity.rs | 2 -- src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs | 3 --- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 3 --- src/tools/miri/tests/pass-dep/libc/libc-misc.rs | 2 -- 4 files changed, 10 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs index 4793ee560fca4..f162c306784d0 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs @@ -1,7 +1,5 @@ //@only-target: linux freebsd # these are Linux/FreeBSD-specific APIs //@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4 -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] use std::mem::{size_of, size_of_val}; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs index f03e1ad8641de..fd67afbe83c4b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs-permissions.rs @@ -2,9 +2,6 @@ //@ignore-host: windows # needs unix PermissionExt //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(io_error_uncategorized)] - use std::ffi::{CStr, CString}; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a388943f922ff..cfb4126f6775e 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -1,9 +1,6 @@ //@ignore-target: windows # no libc //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(io_error_uncategorized)] - use std::ffi::{CStr, CString, OsString}; use std::fs::{File, canonicalize, remove_file}; use std::io::{Error, ErrorKind, Write}; diff --git a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs index 1eac3d25e56a1..10d756e05104b 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-misc.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-misc.rs @@ -1,7 +1,5 @@ //@ignore-target: windows # only very limited libc on Windows //@compile-flags: -Zmiri-disable-isolation -#![feature(io_error_more)] -#![feature(pointer_is_aligned_to)] use std::mem::transmute; From 51af80a0ae4bac782af7394538e4ea5dd0d0a5e3 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 13:16:00 +0200 Subject: [PATCH 046/174] libc-time: expand comment --- src/tools/miri/tests/pass-dep/libc/libc-time.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-time.rs b/src/tools/miri/tests/pass-dep/libc/libc-time.rs index 141e0009101aa..f315d2ab117f9 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-time.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-time.rs @@ -66,7 +66,7 @@ fn test_posix_gettimeofday() { assert!(tv.tv_sec > 0); assert!(tv.tv_usec >= 0); // Theoretically this could be 0. - // Test that non-null tz returns an error. + // Test that non-null tz returns an error (because we don't support it). let mut tz = mem::MaybeUninit::::uninit(); let tz_ptr = tz.as_mut_ptr(); let is_error = unsafe { libc::gettimeofday(tp.as_mut_ptr(), tz_ptr.cast()) }; From faa411e65e0d4ee9591b706acbd4c6c6d1711f36 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 13:25:13 +0200 Subject: [PATCH 047/174] fs: statx always returns MODE; make test pass outside Miri as well --- src/tools/miri/src/shims/unix/fs.rs | 4 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 38 +++++++++++-------- 2 files changed, 25 insertions(+), 17 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index 0caae04d93644..d05a1f456ec4c 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -750,7 +750,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // However, `statx` is allowed to return information that was not requested or to not // return information that was requested. This `mask` represents the information we can // actually provide for any target. - let mut mask = this.eval_libc_u32("STATX_TYPE") | this.eval_libc_u32("STATX_SIZE"); + let mut mask = this.eval_libc_u32("STATX_TYPE") + | this.eval_libc_u32("STATX_MODE") + | this.eval_libc_u32("STATX_SIZE"); // Check which pieces of metadata we acquired, and set the appropriate flags in the mask. if metadata.ino.is_some() { diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index cfb4126f6775e..8cd7fbde91a07 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -64,10 +64,12 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &std::fs::Metadata, ex let mask = stx.stx_mask; // Guaranteed by the shim on any Linux target. - assert!(mask & libc::STATX_TYPE != 0); assert!(mask & libc::STATX_SIZE != 0); assert_eq!(stx.stx_size, expected_size); - assert_eq!((stx.stx_mode as u32) & libc::S_IFMT, libc::S_IFREG); + assert!(mask & libc::STATX_TYPE != 0); + assert_eq!((stx.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFREG); + assert!(mask & libc::STATX_MODE != 0); + assert_ne!((stx.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); // Host-dependent enrichment: only assert when the mask says the field is real. if mask & libc::STATX_INO != 0 { @@ -86,9 +88,6 @@ fn assert_statx_matches_metadata(stx: &libc::statx, meta: &std::fs::Metadata, ex assert_eq!(stx.stx_blocks, meta.blocks()); } - // We don't support non-S_IFMT bits in stx_mode. - assert_eq!(mask & libc::STATX_MODE, 0); - // Do not assert stx_blksize and stx_dev_* : there are no mask bits for them. } @@ -174,18 +173,21 @@ fn test_statx_empty_path_on_pipe() { let statx_buf = statx_buf.assume_init(); - assert_ne!(statx_buf.stx_mask & libc::STATX_TYPE, 0); assert_ne!(statx_buf.stx_mask & libc::STATX_SIZE, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_MODE, 0); - assert_eq!((statx_buf.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFIFO); assert_eq!(statx_buf.stx_size, 0); - - // Synthetic metadata must not advertise host-only fields. - assert_eq!(statx_buf.stx_mask & libc::STATX_INO, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_NLINK, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_UID, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_GID, 0); - assert_eq!(statx_buf.stx_mask & libc::STATX_BLOCKS, 0); + assert_ne!(statx_buf.stx_mask & libc::STATX_TYPE, 0); + assert_eq!((statx_buf.stx_mode as libc::mode_t) & libc::S_IFMT, libc::S_IFIFO); + assert_ne!(statx_buf.stx_mask & libc::STATX_MODE, 0); + assert_ne!((statx_buf.stx_mode as libc::mode_t) & !libc::S_IFMT, 0); + + if cfg!(miri) { + // Synthetic metadata must not advertise host-only fields. + assert_eq!(statx_buf.stx_mask & libc::STATX_INO, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_NLINK, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_UID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_GID, 0); + assert_eq!(statx_buf.stx_mask & libc::STATX_BLOCKS, 0); + } errno_check(libc::close(fds[0])); errno_check(libc::close(fds[1])); @@ -322,13 +324,16 @@ fn test_ftruncate>( #[cfg(target_os = "linux")] fn test_o_tmpfile_flag() { + if !cfg!(miri) { + return; // checks miri-specific behavior + } + use std::fs::{OpenOptions, create_dir}; use std::os::unix::fs::OpenOptionsExt; let dir_path = utils::prepare_dir("miri_test_fs_dir"); create_dir(&dir_path).unwrap(); // test that the `O_TMPFILE` custom flag gracefully errors instead of stopping execution assert_eq!( - Some(libc::EOPNOTSUPP), OpenOptions::new() .read(true) .write(true) @@ -336,6 +341,7 @@ fn test_o_tmpfile_flag() { .open(dir_path) .unwrap_err() .raw_os_error(), + Some(libc::EOPNOTSUPP), ); } From 79697e532086007c6bbb14cd58c3b6c75389c0f4 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Sat, 16 May 2026 13:43:24 +0200 Subject: [PATCH 048/174] fix mkstemp behavior: we should not add the /tmp path --- src/tools/miri/src/shims/unix/fs.rs | 11 +++-------- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 5 ++++- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs index d05a1f456ec4c..a5b3f891d9ca7 100644 --- a/src/tools/miri/src/shims/unix/fs.rs +++ b/src/tools/miri/src/shims/unix/fs.rs @@ -4,7 +4,7 @@ use std::borrow::Cow; use std::ffi::OsString; use std::fs::{self, DirBuilder, File, FileType, OpenOptions, TryLockError}; use std::io::{self, ErrorKind, Read, Seek, SeekFrom, Write}; -use std::path::{self, Path, PathBuf}; +use std::path::{self, Path}; use std::time::SystemTime; use rustc_abi::Size; @@ -1652,13 +1652,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Write the modified template back to the passed in pointer to maintain POSIX semantics. this.write_bytes_ptr(template_ptr, template_bytes.iter().copied())?; - // To actually open the file, turn this into a host OsString. - let p = bytes_to_os_str(template_bytes)?.to_os_string(); - - let possibly_unique = std::env::temp_dir().join::(p.into()); - - let file = fopts.open(possibly_unique); - + // See if we can create and open this file. + let file = fopts.open(bytes_to_os_str(template_bytes)?); match file { Ok(f) => { let fd = this.machine.fds.insert_new(FileHandle { file: f, writable: true }); diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 8cd7fbde91a07..47670e9f1b35e 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -362,7 +362,7 @@ fn test_posix_mkstemp() { assert!(fd > 0); let osstr = OsStr::from_bytes(slice.to_bytes()); let path: &Path = osstr.as_ref(); - let name = path.file_name().unwrap().to_string_lossy(); + let name = path.to_string_lossy(); assert!(name.ne("fooXXXXXX")); assert!(name.starts_with("foo")); assert_eq!(name.len(), 9); @@ -372,6 +372,9 @@ fn test_posix_mkstemp() { ); let file = unsafe { File::from_raw_fd(fd) }; assert!(file.set_len(0).is_ok()); + // Cleanup. Also checks that the filename actually exists. + drop(file); + remove_file(path).unwrap(); let invalid_templates = vec!["foo", "barXX", "XXXXXXbaz", "whatXXXXXXever", "X"]; for t in invalid_templates { From af7f76d89303cfcfb9c463a66ccc39d18d6f45a1 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Sat, 9 May 2026 23:41:38 +0200 Subject: [PATCH 049/174] Add shims for vectored reads and vectored writes This adds shims for the `readv`, `writev`, `preadv` and `pwritev` functions. --- src/tools/miri/src/provenance_gc.rs | 6 + src/tools/miri/src/shims/unix/fd.rs | 430 +++++++++++++++--- .../miri/src/shims/unix/foreign_items.rs | 39 +- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 287 ++++++++++++ .../miri/tests/pass-dep/libc/libc-socket.rs | 75 ++- src/tools/miri/tests/pass/shims/fs.rs | 87 +++- 6 files changed, 848 insertions(+), 76 deletions(-) diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index 02353411eb944..3656a9eaa87c2 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -44,6 +44,12 @@ where } } +impl VisitProvenance for Vec { + fn visit_provenance(&self, visit: &mut VisitWith<'_>) { + self.iter().for_each(|el| el.visit_provenance(visit)); + } +} + impl VisitProvenance for std::cell::RefCell { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { self.borrow().visit_provenance(visit) diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs index 4a1dc10341646..ab17be0e04f4e 100644 --- a/src/tools/miri/src/shims/unix/fd.rs +++ b/src/tools/miri/src/shims/unix/fd.rs @@ -5,10 +5,10 @@ use std::io; use std::io::ErrorKind; use rand::RngExt; -use rustc_abi::Size; +use rustc_abi::{Align, Size}; use rustc_target::spec::Os; -use crate::shims::files::FileDescription; +use crate::shims::files::{DynFileDescriptionRef, FileDescription}; use crate::shims::sig::check_min_vararg_count; use crate::shims::unix::linux_like::epoll::EpollReadiness; use crate::shims::unix::*; @@ -316,7 +316,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .min(u64::try_from(this.target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let count = usize::try_from(count).unwrap(); // now it fits in a `usize` - let communicate = this.machine.communicate(); // Get the FD. let Some(fd) = this.machine.fds.get(fd_num) else { @@ -324,33 +323,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; - // Handle the zero-sized case. The man page says: - // > If count is zero, read() may detect the errors described below. In the absence of any - // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero - // > and has no other effects. - if count == 0 { - this.write_null(dest)?; - return interp_ok(()); - } - // Non-deterministically decide to further reduce the count, simulating a partial read (but - // never to 0, that would indicate EOF). - let count = if this.machine.short_fd_operations - && fd.short_fd_operations() - && count >= 2 - && this.machine.rng.get_mut().random() - { - count / 2 // since `count` is at least 2, the result is still at least 1 - } else { - count - }; - trace!("read: FD mapped to {fd:?}"); // We want to read at most `count` bytes. We are sure that `count` is not negative // because it was a target's `usize`. Also we are sure that it's smaller than // `usize::MAX` because it is bounded by the host's `isize`. - let finish = { - let dest = dest.clone(); + let dest = dest.clone(); + this.read_from_fd( + fd, + buf, + count, + offset, callback!( @capture<'tcx> { count: usize, @@ -363,22 +346,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // This must fit since `count` fits. this.write_int(u64::try_from(read_size).unwrap(), &dest) } - Err(e) => { - this.set_last_error_and_return(e, &dest) - } + Err(e) => this.set_last_error_and_return(e, &dest) }} - ) - }; - match offset { - None => fd.read(communicate, buf, count, this, finish)?, - Some(offset) => { - let Ok(offset) = u64::try_from(offset) else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); - }; - fd.as_unix(this).pread(communicate, offset, buf, count, this, finish)? - } - }; - interp_ok(()) + ), + ) } fn write( @@ -402,64 +373,379 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { .min(u64::try_from(this.target_isize_max()).unwrap()) .min(u64::try_from(isize::MAX).unwrap()); let count = usize::try_from(count).unwrap(); // now it fits in a `usize` - let communicate = this.machine.communicate(); // We temporarily dup the FD to be able to retain mutable access to `this`. let Some(fd) = this.machine.fds.get(fd_num) else { return this.set_last_error_and_return(LibcError("EBADF"), dest); }; + let dest = dest.clone(); + this.write_to_fd( + fd, + buf, + count, + offset, + callback!( + @capture<'tcx> { + count: usize, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + match result { + Ok(write_size) => { + assert!(write_size <= count); + // This must fit since `count` fits. + this.write_int(u64::try_from(write_size).unwrap(), &dest) + } + Err(e) => this.set_last_error_and_return(e, &dest) + + }} + ), + ) + } + + /// Vectored reads are implemented by first reading bytes from `fd` + /// into a temporary buffer which has the combined size of all buffers in + /// `iov`. After that we split the bytes of the combined buffer into the + /// buffers of `iov`. This ensures that the vectored read occurs atomically. + fn readv( + &mut self, + fd: &OpTy<'tcx>, + iov: &OpTy<'tcx>, + iovcnt: &OpTy<'tcx>, + offset: Option<&OpTy<'tcx>>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let fd = this.read_scalar(fd)?.to_i32()?; + let iov_ptr = this.read_pointer(iov)?; + let iovcnt: u64 = this.read_scalar(iovcnt)?.to_i32()?.try_into().unwrap(); + // `readv` is the same as `preadv` without an offset. + let offset = if let Some(offset) = offset { + if matches!(this.tcx.sess.target.os, Os::Solaris) { + throw_unsup_format!( + "preadv: vectored reads with offsets aren't supported on Solaris" + ) + } + Some(this.read_scalar(offset)?.to_int(offset.layout.size)?) + } else { + None + }; + + // Check that the FD exists. + let Some(fd) = this.machine.fds.get(fd) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let iovec_layout = this.libc_array_ty_layout("iovec", iovcnt); + let iov_ptr_mplace = this.ptr_to_mplace(iov_ptr, iovec_layout); + + // Read list of buffers from `iov`. + let mut buffers = Vec::new(); + + let mut array = this.project_array_fields(&iov_ptr_mplace)?; + while let Some((_idx, iovec)) = array.next(this)? { + let iov_len_field = this.project_field_named(&iovec, "iov_len")?; + let iov_len: u64 = this + .read_scalar(&iov_len_field)? + .to_int(iov_len_field.layout.size)? + .try_into() + .unwrap(); + + let iov_base_field = this.project_field_named(&iovec, "iov_base")?; + let iov_base_ptr = this.read_pointer(&iov_base_field)?; + + buffers.push((iov_base_ptr, iov_len)); + } + + let total_bytes = buffers.iter().map(|(_, len)| len).sum::(); + + // Allocate a temporary buffer which has the combined size of all buffers provided in `iov`. + let tmp_ptr: Pointer = this + .allocate_ptr( + Size::from_bytes(total_bytes), + Align::ONE, + MemoryKind::Stack, + AllocInit::Uninit, + )? + .into(); + + let dest = dest.clone(); + this.read_from_fd( + fd, + tmp_ptr, + usize::try_from(total_bytes).unwrap(), + offset, + callback!( + @capture<'tcx> { + tmp_ptr: Pointer, + buffers: Vec<(Pointer, u64)>, + dest: MPlaceTy<'tcx> + } |this, result: Result| { + let bytes_read = match result { + Ok(size) => { + this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest)?; + u64::try_from(size).unwrap() + }, + Err(e) => return this.set_last_error_and_return(e, &dest) + }; + let mut remaining_bytes = bytes_read; + + // Split the bytes from the temporary buffer into the buffers provided in `iov`. + // We start at the first buffer and fill them in order, until we reach the end of the + // initialized bytes in the temporary buffer. + for (buffer_ptr, buffer_len) in buffers { + // Offset temporary buffer by the amount of bytes we already copied into previous buffers. + let tmp_ptr_with_offset = + this.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_read.strict_sub(remaining_bytes)).unwrap())?; + + // Copy at most as many bytes as the buffer fits but without reading + // any uninitialized bytes from the temporary buffer. + let copy_amount = buffer_len.min(remaining_bytes); + this.mem_copy( + tmp_ptr_with_offset, + buffer_ptr, + Size::from_bytes(copy_amount), + // The buffers are guaranteed to not overlap because we just newly allocated + // the `tmp_ptr`, and `tmp_ptr_with_offset` is guaranteed to be + // within those boundaries. + true, + )?; + + remaining_bytes = remaining_bytes.strict_sub(copy_amount); + if remaining_bytes == 0 { + // We don't have anything left to copy; exit the loop. + break; + } + } + + this.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack) + }), + ) + } + + /// Vectored writes are implemented by first writing the bytes from all + /// buffers of `iov` into a combined temporary buffer and then writing this + /// combined buffer into `fd`. This ensures that the vectored write occurs atomically. + fn writev( + &mut self, + fd: &OpTy<'tcx>, + iov: &OpTy<'tcx>, + iovcnt: &OpTy<'tcx>, + offset: Option<&OpTy<'tcx>>, + dest: &MPlaceTy<'tcx>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + let fd = this.read_scalar(fd)?.to_i32()?; + let iov_ptr = this.read_pointer(iov)?; + let iovcnt: u64 = this.read_scalar(iovcnt)?.to_i32()?.try_into().unwrap(); + // `writev` is the same as `pwritev` without an offset. + let offset = if let Some(offset) = offset { + if matches!(this.tcx.sess.target.os, Os::Solaris) { + throw_unsup_format!( + "pwritev: vectored writes with offsets aren't supported on Solaris" + ) + } + Some(this.read_scalar(offset)?.to_int(offset.layout.size)?) + } else { + None + }; + + // Check that the FD exists. + let Some(fd) = this.machine.fds.get(fd) else { + return this.set_last_error_and_return(LibcError("EBADF"), dest); + }; + + let iovec_layout = this.libc_array_ty_layout("iovec", iovcnt); + let iov_ptr_mplace = this.ptr_to_mplace(iov_ptr, iovec_layout); + + // Read list of buffers from `iov`. + let mut buffers = Vec::new(); + + let mut array = this.project_array_fields(&iov_ptr_mplace)?; + while let Some((_idx, iovec)) = array.next(this)? { + let iov_len_field = this.project_field_named(&iovec, "iov_len")?; + let iov_len: u64 = this + .read_scalar(&iov_len_field)? + .to_int(iov_len_field.layout.size)? + .try_into() + .unwrap(); + + let iov_base_field = this.project_field_named(&iovec, "iov_base")?; + let iov_base_ptr = this.read_pointer(&iov_base_field)?; + + buffers.push((iov_base_ptr, iov_len)); + } + + let total_bytes = buffers.iter().map(|(_, len)| len).sum::(); + + // Allocate a temporary buffer which has the combined size of all buffers provided in `iov`. + let tmp_ptr: Pointer = this + .allocate_ptr( + Size::from_bytes(total_bytes), + Align::ONE, + MemoryKind::Stack, + AllocInit::Uninit, + )? + .into(); + + // Copy the bytes from all buffers provided in `iov` into the temporary buffer. + // We start at the first buffer and then continue buffer by buffer. + let mut bytes_copied: u64 = 0; + for (buffer_ptr, buffer_len) in buffers { + // Offset temporary buffer by the amount of bytes we already copied from previous buffers. + let tmp_ptr_with_offset = + this.ptr_offset_inbounds(tmp_ptr, i64::try_from(bytes_copied).unwrap())?; + + this.mem_copy( + buffer_ptr, + tmp_ptr_with_offset, + Size::from_bytes(buffer_len), + // The buffers are guaranteed to not overlap because we just newly allocated + // the `tmp_ptr`, and `tmp_ptr_with_offset` is guaranteed to be + // within those boundaries. + true, + )?; + + bytes_copied = bytes_copied.strict_add(buffer_len); + } + + let dest = dest.clone(); + // Write bytes from the temporary buffer. This ensures the write is atomic. + this.write_to_fd( + fd, + tmp_ptr, + usize::try_from(total_bytes).unwrap(), + offset, + callback!( + @capture<'tcx> { + tmp_ptr: Pointer, + dest: MPlaceTy<'tcx>, + } + |this, result: Result| { + this.deallocate_ptr(tmp_ptr, None, MemoryKind::Stack)?; + match result { + Ok(size) => this.write_scalar(Scalar::from_target_isize(size.try_into().unwrap(), this), &dest), + Err(e) => this.set_last_error_and_return(e, &dest) + } + }), + ) + } +} + +impl<'tcx> EvalContextPrivExt<'tcx> for crate::MiriInterpCx<'tcx> {} +trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { + /// Read `len` bytes from the `fd` file description at `offset` into the buffer + /// pointed to by `ptr`. + /// If `offset` is [`Some`], the read occurs at the given absolute position rather + /// than the current file position (`read_at` semantics rather than `read`). + /// `finish` will be invoked when the read is done (which might be way after + /// this function returns as the read may block). + fn read_from_fd( + &mut self, + fd: DynFileDescriptionRef, + ptr: Pointer, + len: usize, + offset: Option, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + + // Handle the zero-sized case. The man page says: + // > If count is zero, read() may detect the errors described below. In the absence of any + // > errors, or if read() does not check for errors, a read() with a count of 0 returns zero + // > and has no other effects. + if len == 0 { + return finish.call(this, Ok(0)); + } + + // Non-deterministically decide to further reduce the length, simulating a partial read (but + // never to 0, that would indicate EOF). + let len = if this.machine.short_fd_operations + && fd.short_fd_operations() + && len >= 2 + && this.machine.rng.get_mut().random() + { + len / 2 // since `len` is at least 2, the result is still at least 1 + } else { + len + }; + + match offset { + None => fd.read(this.machine.communicate(), ptr, len, this, finish)?, + Some(offset) => { + let Ok(offset) = u64::try_from(offset) else { + return finish.call(this, Err(LibcError("EINVAL"))); + }; + fd.as_unix(this).pread( + this.machine.communicate(), + offset, + ptr, + len, + this, + finish, + )? + } + }; + interp_ok(()) + } + + /// Write `len` bytes at `offset` from the buffer pointed to by `ptr` into the `fd` + /// file description. + /// If `offset` is [`Some`], the write occurs at the given absolute position rather + /// than the current file position (`write_at` semantics rather than `write`). + /// `finish` will be invoked when the write is done (which might be way after + /// this function returns as the write may block). + fn write_to_fd( + &mut self, + fd: DynFileDescriptionRef, + ptr: Pointer, + len: usize, + offset: Option, + finish: DynMachineCallback<'tcx, Result>, + ) -> InterpResult<'tcx> { + let this = self.eval_context_mut(); + // Handle the zero-sized case. The man page says: // > If count is zero and fd refers to a regular file, then write() may return a failure // > status if one of the errors below is detected. If no errors are detected, or error // > detection is not performed, 0 is returned without causing any other effect. If count // > is zero and fd refers to a file other than a regular file, the results are not // > specified. - if count == 0 { + if len == 0 { // For now let's not open the can of worms of what exactly "not specified" could mean... - this.write_null(dest)?; - return interp_ok(()); + return finish.call(this, Ok(0)); } - // Non-deterministically decide to further reduce the count, simulating a partial write. + + // Non-deterministically decide to further reduce the length, simulating a partial write. // We avoid reducing the write size to 0: the docs seem to be entirely fine with that, // but the standard library is not (https://github.com/rust-lang/rust/issues/145959). - let count = if this.machine.short_fd_operations + let len = if this.machine.short_fd_operations && fd.short_fd_operations() - && count >= 2 + && len >= 2 && this.machine.rng.get_mut().random() { - count / 2 + len / 2 } else { - count + len }; - let finish = { - let dest = dest.clone(); - callback!( - @capture<'tcx> { - count: usize, - dest: MPlaceTy<'tcx>, - } - |this, result: Result| { - match result { - Ok(write_size) => { - assert!(write_size <= count); - // This must fit since `count` fits. - this.write_int(u64::try_from(write_size).unwrap(), &dest) - } - Err(e) => { - this.set_last_error_and_return(e, &dest) - } - }} - ) - }; match offset { - None => fd.write(communicate, buf, count, this, finish)?, + None => fd.write(this.machine.communicate(), ptr, len, this, finish)?, Some(offset) => { let Ok(offset) = u64::try_from(offset) else { - return this.set_last_error_and_return(LibcError("EINVAL"), dest); + return finish.call(this, Err(LibcError("EINVAL"))); }; - fd.as_unix(this).pwrite(communicate, buf, count, offset, this, finish)? + fd.as_unix(this).pwrite( + this.machine.communicate(), + ptr, + len, + offset, + this, + finish, + )? } }; interp_ok(()) diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs index 2ed708975c7d5..67602a4fe88e2 100644 --- a/src/tools/miri/src/shims/unix/foreign_items.rs +++ b/src/tools/miri/src/shims/unix/foreign_items.rs @@ -226,8 +226,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called write({:?}, {:?}, {:?})", fd, buf, count); this.write(fd, buf, count, None, dest)?; } + "readv" => { + let [fd, iov, iovcnt] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + link_name, + abi, + args, + )?; + this.readv(fd, iov, iovcnt, None, dest)?; + } + "writev" => { + let [fd, iov, iovcnt] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32) -> isize), + link_name, + abi, + args, + )?; + this.writev(fd, iov, iovcnt, None, dest)?; + } "pread" => { - // FIXME: This does not have a direct test (#3179). let [fd, buf, count, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *mut _, usize, libc::off_t) -> isize), link_name, @@ -241,7 +258,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { this.read(fd, buf, count, Some(offset), dest)?; } "pwrite" => { - // FIXME: This does not have a direct test (#3179). let [fd, buf, n, offset] = this.check_shim_sig( shim_sig!(extern "C" fn(i32, *const _, usize, libc::off_t) -> isize), link_name, @@ -255,6 +271,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { trace!("Called pwrite({:?}, {:?}, {:?}, {:?})", fd, buf, count, offset); this.write(fd, buf, count, Some(offset), dest)?; } + "preadv" => { + let [fd, iov, iovcnt, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + link_name, + abi, + args, + )?; + this.readv(fd, iov, iovcnt, Some(offset), dest)?; + } + "pwritev" => { + let [fd, iov, iovcnt, offset] = this.check_shim_sig( + shim_sig!(extern "C" fn(i32, *const _, i32, libc::off_t) -> isize), + link_name, + abi, + args, + )?; + this.writev(fd, iov, iovcnt, Some(offset), dest)?; + } + "close" => { let [fd] = this.check_shim_sig( shim_sig!(extern "C" fn(i32) -> i32), diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index a388943f922ff..56a4bbd27e714 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -10,6 +10,7 @@ use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; use std::path::PathBuf; +use std::ptr; #[path = "../../utils/mod.rs"] mod utils; @@ -17,6 +18,8 @@ mod utils; #[path = "../../utils/libc.rs"] mod libc_utils; +use libc_utils::errno_result; + fn main() { test_dup(); test_dup_stdout_stderr(); @@ -58,6 +61,16 @@ fn main() { test_statx_on_file_descriptor(); #[cfg(target_os = "linux")] test_statx_empty_path_on_pipe(); + test_readv(); + test_readv_empty_bufs(); + #[cfg(not(target_os = "solaris"))] + test_preadv(); + test_pread(); + test_writev(); + test_writev_empty_bufs(); + #[cfg(not(target_os = "solaris"))] + test_pwritev(); + test_pwrite(); } #[cfg(target_os = "linux")] @@ -846,3 +859,277 @@ pub fn check_stat_fields(stat: &libc::stat) { let _st_mtime_nsec = stat.st_mtime_nsec; let _st_ctime_nsec = stat.st_ctime_nsec; } + +/// Test vectored reads with multiple buffers. +fn test_readv() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-readv.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 4]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let bytes_read = unsafe { + errno_result(libc::readv(fd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() as usize + }; + + // The vectored read should read at least one byte. + assert!(bytes_read > 0); + assert_eq!(&buffer[0..bytes_read], &file_contents[0..bytes_read]); +} + +/// Test that vectored reads without any buffers return zero. +fn test_readv_empty_bufs() { + let path = utils::prepare_with_content("pass-libc-readv-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + unsafe { assert_eq!(errno_result(libc::readv(fd, ptr::null::(), 0)).unwrap(), 0) }; +} + +/// Test vectored reads with multiple buffers and a byte offset. +/// +/// **Note**: We skip this test on Solaris targets because Solaris +/// doesn't have `preadv`. +#[cfg(not(target_os = "solaris"))] +fn test_preadv() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-preadv.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 4]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + // Read with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_read = unsafe { + errno_result(libc::preadv( + fd, + iov.as_ptr(), + iov.len() as libc::c_int, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + + // The vectored read should read at least one byte. + assert!(bytes_read > 0); + // The vectored read should start at the provided byte offset. + assert_eq!(&buffer[0..bytes_read], &file_contents[OFFSET..(bytes_read + OFFSET)]); +} + +/// Test reading with an offset. +fn test_pread() { + let file_contents = [1u8, 2, 3, 4, 5, 6]; + let path = utils::prepare_with_content("pass-libc-pread.txt", &file_contents); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut buffer = [0u8; 2]; + + // Read with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_read = unsafe { + errno_result(libc::pread( + fd, + buffer.as_mut_ptr().cast(), + buffer.len() as libc::size_t, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + + // We should read at least one byte. + assert!(bytes_read > 0); + // The read should start at the provided byte offset. + assert_eq!(&buffer[0..bytes_read], &file_contents[OFFSET..(bytes_read + OFFSET)]); +} + +/// Test vectored writes with multiple buffers. +fn test_writev() { + let path = utils::prepare_with_content("pass-libc-writev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let mut write_buffer = [1u8, 2, 3, 4, 5, 6]; + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let bytes_written = unsafe { + errno_result(libc::writev(fd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() as usize + }; + // The vectored write should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + bytes_written as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[0..bytes_written]); +} + +/// Test that vectored writes without any buffers return zero. +fn test_writev_empty_bufs() { + let path = utils::prepare_with_content("pass-libc-writev-empty-bufs.txt", &[1u8, 2, 3]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + unsafe { + assert_eq!(errno_result(libc::writev(fd, ptr::null::(), 0)).unwrap(), 0) + }; +} + +/// Test vectored writes with multiple buffers and a byte offset. +/// +/// **Note**: We skip this test on Solaris targets because Solaris +/// doesn't have `pwritev`. +#[cfg(not(target_os = "solaris"))] +fn test_pwritev() { + let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let mut write_buffer = [1u8, 2, 3, 4, 5, 6]; + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + // Write with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_written = unsafe { + errno_result(libc::pwritev( + fd, + iov.as_ptr(), + iov.len() as libc::c_int, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + // The vectored write should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + // Read offset + bytes written. + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + (bytes_written + OFFSET) as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + // The vectored write should start at the provided byte offset. + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); +} + +/// Test writing with an offset. +fn test_pwrite() { + let path = utils::prepare_with_content("pass-libc-pwritev.txt", &[]); + let cpath = CString::new(path.into_os_string().into_encoded_bytes()).unwrap(); + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_WRONLY) }; + assert_ne!(fd, -1); + + let write_buffer = [1u8, 2, 3, 4, 5, 6]; + + // Write with a 2 byte offset. + const OFFSET: usize = 2; + let bytes_written = unsafe { + errno_result(libc::pwrite( + fd, + write_buffer.as_ptr().cast(), + write_buffer.len() as libc::size_t, + OFFSET as libc::off_t, + )) + .unwrap() as usize + }; + // We should write at least one byte. + assert!(bytes_written > 0); + + // Open the FD again in readonly mode and with an unadvanced pointer. + let fd = unsafe { libc::open(cpath.as_ptr(), libc::O_RDONLY) }; + assert_ne!(fd, -1); + + let mut read_buffer = [0u8; 16]; + // Read offset + bytes written. + unsafe { + libc_utils::read_exact_generic( + read_buffer.as_mut_ptr().cast(), + (bytes_written + OFFSET) as libc::size_t, + libc_utils::Retry::NoRetry, + |buf, count| libc::read(fd, buf, count), + ) + .unwrap() + }; + + // The write should start at the provided byte offset. + assert_eq!(&write_buffer[0..bytes_written], &read_buffer[OFFSET..(bytes_written + OFFSET)]); +} diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index e067f006325d9..16ec68198db1f 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -7,8 +7,8 @@ mod libc_utils; mod utils; use std::io::ErrorKind; -use std::thread; use std::time::Duration; +use std::{ptr, thread}; use libc_utils::*; @@ -37,6 +37,8 @@ fn main() { test_accept_connect(); test_send_peek_recv(); test_write_read(); + test_readv(); + test_writev(); test_getsockname_ipv4(); test_getsockname_ipv4_random_port(); @@ -342,6 +344,77 @@ fn test_write_read() { server_thread.join().unwrap(); } +/// Test vectored reads with multiple buffers on a connected socket. +fn test_readv() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + let mut buffer = [0u8; TEST_BYTES.len()]; + let (buffer1, buffer2) = buffer.split_at_mut(2); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let num = unsafe { + errno_result(libc::readv(client_sockfd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() + }; + assert_eq!(num as usize, TEST_BYTES.len()); + // The vectored read should read the entire buffer because we don't have + // short reads on sockets. + assert_eq!(&buffer, TEST_BYTES); +} + +/// Test vectored writes with multiple buffers on a connected socket. +fn test_writev() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + let mut write_buffer = TEST_BYTES.to_owned(); + let (buffer1, buffer2) = write_buffer.split_at_mut(3); + + let iov = [ + libc::iovec { iov_base: ptr::null_mut::(), iov_len: 0 as libc::size_t }, + libc::iovec { + iov_base: buffer1.as_mut_ptr().cast::(), + iov_len: buffer1.len() as libc::size_t, + }, + libc::iovec { + iov_base: buffer2.as_mut_ptr().cast::(), + iov_len: buffer2.len() as libc::size_t, + }, + ]; + + let num = unsafe { + errno_result(libc::writev(client_sockfd, iov.as_ptr(), iov.len() as libc::c_int)).unwrap() + }; + assert_eq!(num as usize, TEST_BYTES.len()); + + let mut buffer = [0u8; TEST_BYTES.len()]; + libc_utils::read_exact(peerfd, &mut buffer).unwrap(); + // The vectored write should write the entire buffer because we don't have + // short writes on sockets. + assert_eq!(&buffer, TEST_BYTES); +} + /// Test the `getsockname` syscall on an IPv4 socket which is bound. /// The `getsockname` syscall should return the same address as to /// which the socket was bound to. diff --git a/src/tools/miri/tests/pass/shims/fs.rs b/src/tools/miri/tests/pass/shims/fs.rs index cd3ae9f7c3287..b249c065318c2 100644 --- a/src/tools/miri/tests/pass/shims/fs.rs +++ b/src/tools/miri/tests/pass/shims/fs.rs @@ -2,13 +2,16 @@ #![feature(io_error_more)] #![feature(io_error_uncategorized)] +#![cfg_attr(unix, feature(unix_file_vectored_at))] use std::collections::BTreeMap; use std::ffi::OsString; use std::fs::{ self, File, OpenOptions, create_dir, read_dir, remove_dir, remove_dir_all, remove_file, rename, }; -use std::io::{Error, ErrorKind, IsTerminal, Read, Result, Seek, SeekFrom, Write}; +use std::io::{ + Error, ErrorKind, IoSlice, IoSliceMut, IsTerminal, Read, Result, Seek, SeekFrom, Write, +}; use std::path::Path; #[path = "../../utils/mod.rs"] @@ -39,6 +42,9 @@ fn main() { test_pread_pwrite(); #[cfg(not(any(target_os = "solaris", target_os = "android")))] test_flock(); + test_readv_writev(); + #[cfg(all(unix, not(any(target_os = "solaris", target_os = "android"))))] + test_preadv_pwritev(); } } @@ -427,3 +433,82 @@ fn test_flock() { // Unlock exclusive lock file1.unlock().unwrap(); } + +/// Test vectored reads and vectored writes. +fn test_readv_writev() { + let bytes = b"hello world!"; + let path = utils::prepare_with_content("miri_test_fs_readv_writev.txt", bytes); + let mut f = OpenOptions::new().read(true).write(true).open(path).unwrap(); + + let mut read_buffer = [0u8; 10]; + let (buffer1, buffer2) = read_buffer.split_at_mut(5); + + let bytes_read = + f.read_vectored(&mut [IoSliceMut::new(buffer1), IoSliceMut::new(buffer2)]).unwrap(); + + // Vectored read should read at least a byte. + assert!(bytes_read > 0); + assert_eq!(read_buffer[0..bytes_read], bytes[0..bytes_read]); + + let write_buffer = b"some additional bytes"; + let (buffer1, buffer2) = write_buffer.split_at(write_buffer.len() / 2); + + let bytes_written = f.write_vectored(&[IoSlice::new(buffer1), IoSlice::new(buffer2)]).unwrap(); + + // Vectored write should write at least a byte. + assert!(bytes_written > 0); + + // Reset file cursor to read the written bytes. + f.seek(SeekFrom::Start(bytes_read as u64)).unwrap(); + let mut written_bytes = vec![0u8; bytes_written]; + f.read_exact(&mut written_bytes).unwrap(); + assert_eq!(written_bytes.as_slice(), &write_buffer[0..bytes_written]); +} + +/// Test vectored reads and vectored writes with byte offsets. +/// +/// **Note**: We skip this test on Solaris and Android targets. This is +/// because Solaris doesn't have `preadv`/`pwritev`, and on Android the +/// standard library uses `syscall(...)` for vectored reads/writes with +/// offsets because older Android versions also didn't have `preadv`/`pwritev`. +#[cfg(all(unix, not(any(target_os = "solaris", target_os = "android"))))] +fn test_preadv_pwritev() { + use std::os::unix::fs::FileExt; + + let bytes = b"hello world!"; + let path = utils::prepare_with_content("miri_test_fs_preadv_pwritev.txt", bytes); + let mut f = OpenOptions::new().read(true).write(true).open(path).unwrap(); + + const OFFSET: usize = 2; + + let mut read_buffer = [0u8; 10]; + let (buffer1, buffer2) = read_buffer.split_at_mut(5); + + let bytes_read = f + .read_vectored_at(&mut [IoSliceMut::new(buffer1), IoSliceMut::new(buffer2)], OFFSET as u64) + .unwrap(); + + // Vectored read should read at least a byte at the provided offset. + assert!(bytes_read > 0); + assert_eq!(read_buffer[0..bytes_read], bytes[OFFSET..(bytes_read + OFFSET)]); + + let write_buffer = b"some additional bytes"; + let (buffer1, buffer2) = write_buffer.split_at(write_buffer.len() / 2); + + let bytes_written = f + .write_vectored_at( + &[IoSlice::new(buffer1), IoSlice::new(buffer2)], + (bytes.len() + OFFSET) as u64, + ) + .unwrap(); + + // Vectored write should write at least a byte at the provided offset. + assert!(bytes_written > 0); + + // Reset file cursor to read the written bytes. We move the cursor + // to include the offset. + f.seek(SeekFrom::Start((bytes.len() + OFFSET) as u64)).unwrap(); + let mut written_bytes = vec![0u8; bytes_written]; + f.read_exact(&mut written_bytes).unwrap(); + assert_eq!(written_bytes.as_slice(), &write_buffer[0..bytes_written]); +} From 425ec49922ed460e07b6503b5627d6f0d58905e9 Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Fri, 15 May 2026 20:58:30 +0200 Subject: [PATCH 050/174] fix: don't remove readable readiness after short `peek` --- src/tools/miri/src/shims/unix/socket.rs | 12 ++--- .../libc/libc-socket-no-blocking-epoll.rs | 50 +++++++++++++++++++ 2 files changed, 56 insertions(+), 6 deletions(-) diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index dbf2ebb37dd4e..14226f3068244 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -1697,12 +1697,12 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // would be returned on UNIX-like systems. We thus remap this error to an EWOULDBLOCK. interp_ok(Err(IoError::HostError(io::ErrorKind::WouldBlock.into()))) } - Ok(bytes_read) if bytes_read < length && bytes_read > 0 => { - // We had a short read. (Note that reading 0 bytes is guaranteed to indicate EOF, - // and can never happen spuriously, so we have to exclude that case.) On Unix hosts - // using the `epoll` and `kqueue` backends, a short read means that the read buffer - // is empty. We update the readiness accordingly, which means that next time we see - // "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on + Ok(bytes_read) if !should_peek && bytes_read < length && bytes_read > 0 => { + // We had a short read (and were not peeking). (Note that reading 0 bytes is guaranteed + // to indicate EOF, and can never happen spuriously, so we have to exclude that case.) + // On Unix hosts using the `epoll` and `kqueue` backends, a short read means that the + // read buffer is empty. We update the readiness accordingly, which means that next time + // we see "readable" we will report an epoll edge. Some applications (e.g. tokio) rely on // this behavior; see // if cfg!(any( diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs index 08f1ee91b1b1f..9c8c5be647d4c 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket-no-blocking-epoll.rs @@ -25,6 +25,7 @@ fn main() { test_shutdown_read(); test_shutdown_write(); test_readiness_after_short_read(); + test_readiness_after_short_peek(); test_readiness_after_short_write(); } @@ -505,6 +506,55 @@ fn test_readiness_after_short_read() { }; } +/// Test that Miri doesn't remove the readable readiness after a short peek. +fn test_readiness_after_short_peek() { + let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + net::connect_ipv4(client_sockfd, addr).unwrap(); + let (peerfd, _) = net::accept_ipv4(server_sockfd).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // Write some bytes into the peer socket. + libc_utils::write_all(peerfd, TEST_BYTES).unwrap(); + + // FIXME: Changes in host I/O readiness are only processed when entering the scheduler. + // Ensure that we process the effects if the `write_all` by yielding the current (only) thread. + // + thread::yield_now(); + + // `buffer` is intentionally bigger than `TEST_BYTES.len()` to trigger a short peek. + let mut buffer = [0; 128]; + let bytes_read = unsafe { + errno_result(libc::recv( + client_sockfd, + buffer.as_mut_ptr().cast(), + buffer.len(), + libc::MSG_PEEK, + )) + .unwrap() + } as usize; + assert_eq!(bytes_read, TEST_BYTES.len()); + + // FIXME(#5047): same as above. + thread::yield_now(); + + // Ensure that the readable readiness is still set. + assert_eq!(current_epoll_readiness::<8>(client_sockfd, EPOLLIN | EPOLLET), EPOLLIN); + + // We should be able to read the buffer without blocking indefinitely. + let bytes_read = unsafe { + errno_result(libc::recv(client_sockfd, buffer.as_mut_ptr().cast(), buffer.len(), 0)) + .unwrap() + } as usize; + assert_eq!(bytes_read, TEST_BYTES.len()); +} + /// Test that Miri correctly removes the writable readiness or emits a new edge after a short write. fn test_readiness_after_short_write() { let (server_sockfd, addr) = net::make_listener_ipv4().unwrap(); From 5f8a3f792a1eb413f7a36cea47fe96c2ce0cc2e1 Mon Sep 17 00:00:00 2001 From: The Miri Cronjob Bot Date: Sun, 17 May 2026 06:02:43 +0000 Subject: [PATCH 051/174] Prepare for merging from rust-lang/rust This updates the rust-version file to 281c97c3240a9abd984ca0c6a2cd7389115e80d5. --- src/tools/miri/rust-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version index 6e25d8deaf25d..5e581cb38e5e6 100644 --- a/src/tools/miri/rust-version +++ b/src/tools/miri/rust-version @@ -1 +1 @@ -c8c4c83d57d3b4cf72a7306cd77bb8b5c0f03434 +281c97c3240a9abd984ca0c6a2cd7389115e80d5 From e2b209e6ed828184d4198da9b6e4e29db39bdf8f Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 16:18:11 +1000 Subject: [PATCH 052/174] Temporarily move the simple `needs-*` list to a different file This intermediate commit makes subsequent diffs/blame a bit nicer, by encouraging git's blame view to preserve line history for the remaining parts of `handle_needs`, which would otherwise be obscured. A subsequent commit will move the list back into `needs.rs` in an altered form. --- src/tools/compiletest/src/directives/needs.rs | 197 +---------------- .../src/directives/needs/simple.rs | 200 ++++++++++++++++++ 2 files changed, 203 insertions(+), 194 deletions(-) create mode 100644 src/tools/compiletest/src/directives/needs/simple.rs diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 88ca347edd0a5..e420a00519da7 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -3,205 +3,14 @@ use crate::common::{ }; use crate::directives::{DirectiveLine, IgnoreDecision}; +mod simple; + pub(super) fn handle_needs( cache: &CachedNeedsConditions, config: &Config, ln: &DirectiveLine<'_>, ) -> IgnoreDecision { - // Note that we intentionally still put the needs- prefix here to make the file show up when - // grepping for a directive name, even though we could technically strip that. - let needs = &[ - Need { - name: "needs-asm-support", - condition: config.has_asm_support(), - ignore_reason: "ignored on targets without inline assembly support", - }, - Need { - name: "needs-sanitizer-support", - condition: cache.sanitizer_support, - ignore_reason: "ignored on targets without sanitizers support", - }, - Need { - name: "needs-sanitizer-address", - condition: cache.sanitizer_address, - ignore_reason: "ignored on targets without address sanitizer", - }, - Need { - name: "needs-sanitizer-cfi", - condition: cache.sanitizer_cfi, - ignore_reason: "ignored on targets without CFI sanitizer", - }, - Need { - name: "needs-sanitizer-dataflow", - condition: cache.sanitizer_dataflow, - ignore_reason: "ignored on targets without dataflow sanitizer", - }, - Need { - name: "needs-sanitizer-kcfi", - condition: cache.sanitizer_kcfi, - ignore_reason: "ignored on targets without kernel CFI sanitizer", - }, - Need { - name: "needs-sanitizer-kasan", - condition: cache.sanitizer_kasan, - ignore_reason: "ignored on targets without kernel address sanitizer", - }, - Need { - name: "needs-sanitizer-khwasan", - condition: cache.sanitizer_khwasan, - ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-leak", - condition: cache.sanitizer_leak, - ignore_reason: "ignored on targets without leak sanitizer", - }, - Need { - name: "needs-sanitizer-memory", - condition: cache.sanitizer_memory, - ignore_reason: "ignored on targets without memory sanitizer", - }, - Need { - name: "needs-sanitizer-thread", - condition: cache.sanitizer_thread, - ignore_reason: "ignored on targets without thread sanitizer", - }, - Need { - name: "needs-sanitizer-hwaddress", - condition: cache.sanitizer_hwaddress, - ignore_reason: "ignored on targets without hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-memtag", - condition: cache.sanitizer_memtag, - ignore_reason: "ignored on targets without memory tagging sanitizer", - }, - Need { - name: "needs-sanitizer-realtime", - condition: cache.sanitizer_realtime, - ignore_reason: "ignored on targets without realtime sanitizer", - }, - Need { - name: "needs-sanitizer-shadow-call-stack", - condition: cache.sanitizer_shadow_call_stack, - ignore_reason: "ignored on targets without shadow call stacks", - }, - Need { - name: "needs-sanitizer-safestack", - condition: cache.sanitizer_safestack, - ignore_reason: "ignored on targets without SafeStack support", - }, - Need { - name: "needs-enzyme", - condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-offload", - condition: config.has_offload && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-run-enabled", - condition: config.run_enabled(), - ignore_reason: "ignored when running the resulting test binaries is disabled", - }, - Need { - name: "needs-threads", - condition: config.has_threads(), - ignore_reason: "ignored on targets without threading support", - }, - Need { - name: "needs-subprocess", - condition: config.has_subprocess_support(), - ignore_reason: "ignored on targets without subprocess support", - }, - Need { - name: "needs-unwind", - condition: config.can_unwind(), - ignore_reason: "ignored on targets without unwinding support", - }, - Need { - name: "needs-profiler-runtime", - condition: config.profiler_runtime, - ignore_reason: "ignored when the profiler runtime is not available", - }, - Need { - name: "needs-force-clang-based-tests", - condition: config.run_clang_based_tests_with.is_some(), - ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", - }, - Need { - name: "needs-xray", - condition: cache.xray, - ignore_reason: "ignored on targets without xray tracing", - }, - Need { - name: "needs-rust-lld", - condition: cache.rust_lld, - ignore_reason: "ignored on targets without Rust's LLD", - }, - Need { - name: "needs-dlltool", - condition: cache.dlltool, - ignore_reason: "ignored when dlltool for the current architecture is not present", - }, - Need { - name: "needs-git-hash", - condition: config.git_hash, - ignore_reason: "ignored when git hashes have been omitted for building", - }, - Need { - name: "needs-dynamic-linking", - condition: config.target_cfg().dynamic_linking, - ignore_reason: "ignored on targets without dynamic linking", - }, - Need { - name: "needs-relocation-model-pic", - condition: config.target_cfg().relocation_model == "pic", - ignore_reason: "ignored on targets without PIC relocation model", - }, - Need { - name: "needs-deterministic-layouts", - condition: !config.rust_randomized_layout, - ignore_reason: "ignored when randomizing layouts", - }, - Need { - name: "needs-wasmtime", - condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), - ignore_reason: "ignored when wasmtime runner is not available", - }, - Need { - name: "needs-symlink", - condition: cache.symlinks, - ignore_reason: "ignored if symlinks are unavailable", - }, - Need { - name: "needs-llvm-zstd", - condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", - }, - Need { - name: "needs-rustc-debug-assertions", - condition: config.with_rustc_debug_assertions, - ignore_reason: "ignored if rustc wasn't built with debug assertions", - }, - Need { - name: "needs-std-debug-assertions", - condition: config.with_std_debug_assertions, - ignore_reason: "ignored if std wasn't built with debug assertions", - }, - Need { - name: "needs-std-remap-debuginfo", - condition: config.with_std_remap_debuginfo, - ignore_reason: "ignored if std wasn't built with remapping of debuginfo", - }, - Need { - name: "needs-target-std", - condition: build_helper::targets::target_supports_std(&config.target), - ignore_reason: "ignored if target does not support std", - }, - ]; + let needs = simple::simple_needs(cache, config); let &DirectiveLine { name, .. } = ln; diff --git a/src/tools/compiletest/src/directives/needs/simple.rs b/src/tools/compiletest/src/directives/needs/simple.rs new file mode 100644 index 0000000000000..9ab32f9f8cf10 --- /dev/null +++ b/src/tools/compiletest/src/directives/needs/simple.rs @@ -0,0 +1,200 @@ +use crate::common::Config; +use crate::directives::CachedNeedsConditions; +use crate::directives::needs::Need; + +pub(crate) fn simple_needs(cache: &CachedNeedsConditions, config: &Config) -> Vec { + // Note that we intentionally still put the needs- prefix here to make the file show up when + // grepping for a directive name, even though we could technically strip that. + vec![ + Need { + name: "needs-asm-support", + condition: config.has_asm_support(), + ignore_reason: "ignored on targets without inline assembly support", + }, + Need { + name: "needs-sanitizer-support", + condition: cache.sanitizer_support, + ignore_reason: "ignored on targets without sanitizers support", + }, + Need { + name: "needs-sanitizer-address", + condition: cache.sanitizer_address, + ignore_reason: "ignored on targets without address sanitizer", + }, + Need { + name: "needs-sanitizer-cfi", + condition: cache.sanitizer_cfi, + ignore_reason: "ignored on targets without CFI sanitizer", + }, + Need { + name: "needs-sanitizer-dataflow", + condition: cache.sanitizer_dataflow, + ignore_reason: "ignored on targets without dataflow sanitizer", + }, + Need { + name: "needs-sanitizer-kcfi", + condition: cache.sanitizer_kcfi, + ignore_reason: "ignored on targets without kernel CFI sanitizer", + }, + Need { + name: "needs-sanitizer-kasan", + condition: cache.sanitizer_kasan, + ignore_reason: "ignored on targets without kernel address sanitizer", + }, + Need { + name: "needs-sanitizer-khwasan", + condition: cache.sanitizer_khwasan, + ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-leak", + condition: cache.sanitizer_leak, + ignore_reason: "ignored on targets without leak sanitizer", + }, + Need { + name: "needs-sanitizer-memory", + condition: cache.sanitizer_memory, + ignore_reason: "ignored on targets without memory sanitizer", + }, + Need { + name: "needs-sanitizer-thread", + condition: cache.sanitizer_thread, + ignore_reason: "ignored on targets without thread sanitizer", + }, + Need { + name: "needs-sanitizer-hwaddress", + condition: cache.sanitizer_hwaddress, + ignore_reason: "ignored on targets without hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-memtag", + condition: cache.sanitizer_memtag, + ignore_reason: "ignored on targets without memory tagging sanitizer", + }, + Need { + name: "needs-sanitizer-realtime", + condition: cache.sanitizer_realtime, + ignore_reason: "ignored on targets without realtime sanitizer", + }, + Need { + name: "needs-sanitizer-shadow-call-stack", + condition: cache.sanitizer_shadow_call_stack, + ignore_reason: "ignored on targets without shadow call stacks", + }, + Need { + name: "needs-sanitizer-safestack", + condition: cache.sanitizer_safestack, + ignore_reason: "ignored on targets without SafeStack support", + }, + Need { + name: "needs-enzyme", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-offload", + condition: config.has_offload && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-run-enabled", + condition: config.run_enabled(), + ignore_reason: "ignored when running the resulting test binaries is disabled", + }, + Need { + name: "needs-threads", + condition: config.has_threads(), + ignore_reason: "ignored on targets without threading support", + }, + Need { + name: "needs-subprocess", + condition: config.has_subprocess_support(), + ignore_reason: "ignored on targets without subprocess support", + }, + Need { + name: "needs-unwind", + condition: config.can_unwind(), + ignore_reason: "ignored on targets without unwinding support", + }, + Need { + name: "needs-profiler-runtime", + condition: config.profiler_runtime, + ignore_reason: "ignored when the profiler runtime is not available", + }, + Need { + name: "needs-force-clang-based-tests", + condition: config.run_clang_based_tests_with.is_some(), + ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", + }, + Need { + name: "needs-xray", + condition: cache.xray, + ignore_reason: "ignored on targets without xray tracing", + }, + Need { + name: "needs-rust-lld", + condition: cache.rust_lld, + ignore_reason: "ignored on targets without Rust's LLD", + }, + Need { + name: "needs-dlltool", + condition: cache.dlltool, + ignore_reason: "ignored when dlltool for the current architecture is not present", + }, + Need { + name: "needs-git-hash", + condition: config.git_hash, + ignore_reason: "ignored when git hashes have been omitted for building", + }, + Need { + name: "needs-dynamic-linking", + condition: config.target_cfg().dynamic_linking, + ignore_reason: "ignored on targets without dynamic linking", + }, + Need { + name: "needs-relocation-model-pic", + condition: config.target_cfg().relocation_model == "pic", + ignore_reason: "ignored on targets without PIC relocation model", + }, + Need { + name: "needs-deterministic-layouts", + condition: !config.rust_randomized_layout, + ignore_reason: "ignored when randomizing layouts", + }, + Need { + name: "needs-wasmtime", + condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), + ignore_reason: "ignored when wasmtime runner is not available", + }, + Need { + name: "needs-symlink", + condition: cache.symlinks, + ignore_reason: "ignored if symlinks are unavailable", + }, + Need { + name: "needs-llvm-zstd", + condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", + }, + Need { + name: "needs-rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + ignore_reason: "ignored if rustc wasn't built with debug assertions", + }, + Need { + name: "needs-std-debug-assertions", + condition: config.with_std_debug_assertions, + ignore_reason: "ignored if std wasn't built with debug assertions", + }, + Need { + name: "needs-std-remap-debuginfo", + condition: config.with_std_remap_debuginfo, + ignore_reason: "ignored if std wasn't built with remapping of debuginfo", + }, + Need { + name: "needs-target-std", + condition: build_helper::targets::target_supports_std(&config.target), + ignore_reason: "ignored if target does not support std", + }, + ] +} From ce38783ee4f39e90df13b273086887774e80b8fa Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 14:37:49 +1000 Subject: [PATCH 053/174] Prepare all simple `//@ needs-*` conditions in advance --- src/tools/compiletest/src/directives.rs | 6 +- src/tools/compiletest/src/directives/needs.rs | 317 +++++++++++++----- .../src/directives/needs/simple.rs | 200 ----------- 3 files changed, 238 insertions(+), 285 deletions(-) delete mode 100644 src/tools/compiletest/src/directives/needs/simple.rs diff --git a/src/tools/compiletest/src/directives.rs b/src/tools/compiletest/src/directives.rs index 7600da856a18d..49b860d9cd4cf 100644 --- a/src/tools/compiletest/src/directives.rs +++ b/src/tools/compiletest/src/directives.rs @@ -16,7 +16,7 @@ use crate::directives::directive_names::{ pub(crate) use crate::directives::file::FileDirectives; use crate::directives::handlers::DIRECTIVE_HANDLERS_MAP; use crate::directives::line::DirectiveLine; -use crate::directives::needs::CachedNeedsConditions; +use crate::directives::needs::PreparedNeedsConditions; use crate::edition::{Edition, parse_edition}; use crate::errors::ErrorKind; use crate::executor::{CollectedTestDesc, ShouldFail}; @@ -40,14 +40,14 @@ pub(crate) struct DirectivesCache { /// "Conditions" used by `ignore-*` and `only-*` directives, prepared in /// advance so that they don't have to be evaluated repeatedly. cfg_conditions: cfg::PreparedConditions, - needs: CachedNeedsConditions, + needs: PreparedNeedsConditions, } impl DirectivesCache { pub(crate) fn load(config: &Config) -> Self { Self { cfg_conditions: cfg::prepare_conditions(config), - needs: CachedNeedsConditions::load(config), + needs: needs::prepare_needs_conditions(config), } } } diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index e420a00519da7..a5858a1d3a327 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -3,17 +3,17 @@ use crate::common::{ }; use crate::directives::{DirectiveLine, IgnoreDecision}; -mod simple; - pub(super) fn handle_needs( - cache: &CachedNeedsConditions, + conditions: &PreparedNeedsConditions, config: &Config, ln: &DirectiveLine<'_>, ) -> IgnoreDecision { - let needs = simple::simple_needs(cache, config); - let &DirectiveLine { name, .. } = ln; + if !name.starts_with("needs-") { + return IgnoreDecision::Continue; + } + if name == "needs-target-has-atomic" { let Some(rest) = ln.value_after_colon() else { return IgnoreDecision::Error { @@ -116,8 +116,8 @@ pub(super) fn handle_needs( let mnemonic = rest.trim(); let has_mnemonic = match mnemonic { - "ret" => cache.has_ret_mnemonic, - "nop" => cache.has_nop_mnemonic, + "ret" => conditions.has_ret_mnemonic, + "nop" => conditions.has_nop_mnemonic, _ => has_mnemonic(config, mnemonic), }; @@ -130,17 +130,13 @@ pub(super) fn handle_needs( } } - if !name.starts_with("needs-") { - return IgnoreDecision::Continue; - } - // Handled elsewhere. if name == "needs-llvm-components" || name == "needs-backends" { return IgnoreDecision::Continue; } let mut found_valid = false; - for need in needs { + for need in &conditions.simple_needs { if need.name == name { if need.condition { found_valid = true; @@ -170,82 +166,239 @@ struct Need { ignore_reason: &'static str, } -pub(super) struct CachedNeedsConditions { - sanitizer_support: bool, - sanitizer_address: bool, - sanitizer_cfi: bool, - sanitizer_dataflow: bool, - sanitizer_kcfi: bool, - sanitizer_kasan: bool, - sanitizer_khwasan: bool, - sanitizer_leak: bool, - sanitizer_memory: bool, - sanitizer_thread: bool, - sanitizer_hwaddress: bool, - sanitizer_memtag: bool, - sanitizer_realtime: bool, - sanitizer_shadow_call_stack: bool, - sanitizer_safestack: bool, - xray: bool, - rust_lld: bool, - dlltool: bool, - symlinks: bool, - /// Whether LLVM built with zstd, for the `needs-llvm-zstd` directive. - llvm_zstd: bool, +pub(crate) struct PreparedNeedsConditions { + /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. + simple_needs: Vec, + /// Might add particular other mnemonics heavily needed by tests here. /// Otherwise call into llvm for every check has_ret_mnemonic: bool, has_nop_mnemonic: bool, } -impl CachedNeedsConditions { - pub(super) fn load(config: &Config) -> Self { - let target = &&*config.target; - let sanitizers = &config.target_cfg().sanitizers; - Self { - sanitizer_support: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), - sanitizer_address: sanitizers.contains(&Sanitizer::Address), - sanitizer_cfi: sanitizers.contains(&Sanitizer::Cfi), - sanitizer_dataflow: sanitizers.contains(&Sanitizer::Dataflow), - sanitizer_kcfi: sanitizers.contains(&Sanitizer::Kcfi), - sanitizer_kasan: sanitizers.contains(&Sanitizer::KernelAddress), - sanitizer_khwasan: sanitizers.contains(&Sanitizer::KernelHwaddress), - sanitizer_leak: sanitizers.contains(&Sanitizer::Leak), - sanitizer_memory: sanitizers.contains(&Sanitizer::Memory), - sanitizer_thread: sanitizers.contains(&Sanitizer::Thread), - sanitizer_hwaddress: sanitizers.contains(&Sanitizer::Hwaddress), - sanitizer_memtag: sanitizers.contains(&Sanitizer::Memtag), - sanitizer_realtime: sanitizers.contains(&Sanitizer::Realtime), - sanitizer_shadow_call_stack: sanitizers.contains(&Sanitizer::ShadowCallStack), - sanitizer_safestack: sanitizers.contains(&Sanitizer::Safestack), - xray: config.target_cfg().xray, - - // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), - // we need to find whether `rust-lld` is present in the compiler under test. - // - // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For - // example: - // - on linux, it can be /lib - // - on windows, it can be /bin - // - // However, `rust-lld` is only located under the lib path, so we look for it there. - rust_lld: config - .host_compile_lib_path - .parent() - .expect("couldn't traverse to the parent of the specified --compile-lib-path") - .join("lib") - .join("rustlib") - .join(target) - .join("bin") - .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) - .exists(), - - llvm_zstd: llvm_has_zstd(&config), - dlltool: find_dlltool(&config), - symlinks: has_symlinks(), - has_ret_mnemonic: has_mnemonic(config, "ret"), - has_nop_mnemonic: has_mnemonic(config, "nop"), - } +pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditions { + let target = config.target.as_str(); + let sanitizers = config.target_cfg().sanitizers.as_slice(); + + // Note that we intentionally still put the needs- prefix here to make the file show up when + // grepping for a directive name, even though we could technically strip that. + let simple_needs = vec![ + Need { + name: "needs-asm-support", + condition: config.has_asm_support(), + ignore_reason: "ignored on targets without inline assembly support", + }, + Need { + name: "needs-sanitizer-support", + condition: std::env::var_os("RUSTC_SANITIZER_SUPPORT").is_some(), + ignore_reason: "ignored on targets without sanitizers support", + }, + Need { + name: "needs-sanitizer-address", + condition: sanitizers.contains(&Sanitizer::Address), + ignore_reason: "ignored on targets without address sanitizer", + }, + Need { + name: "needs-sanitizer-cfi", + condition: sanitizers.contains(&Sanitizer::Cfi), + ignore_reason: "ignored on targets without CFI sanitizer", + }, + Need { + name: "needs-sanitizer-dataflow", + condition: sanitizers.contains(&Sanitizer::Dataflow), + ignore_reason: "ignored on targets without dataflow sanitizer", + }, + Need { + name: "needs-sanitizer-kcfi", + condition: sanitizers.contains(&Sanitizer::Kcfi), + ignore_reason: "ignored on targets without kernel CFI sanitizer", + }, + Need { + name: "needs-sanitizer-kasan", + condition: sanitizers.contains(&Sanitizer::KernelAddress), + ignore_reason: "ignored on targets without kernel address sanitizer", + }, + Need { + name: "needs-sanitizer-khwasan", + condition: sanitizers.contains(&Sanitizer::KernelHwaddress), + ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-leak", + condition: sanitizers.contains(&Sanitizer::Leak), + ignore_reason: "ignored on targets without leak sanitizer", + }, + Need { + name: "needs-sanitizer-memory", + condition: sanitizers.contains(&Sanitizer::Memory), + ignore_reason: "ignored on targets without memory sanitizer", + }, + Need { + name: "needs-sanitizer-thread", + condition: sanitizers.contains(&Sanitizer::Thread), + ignore_reason: "ignored on targets without thread sanitizer", + }, + Need { + name: "needs-sanitizer-hwaddress", + condition: sanitizers.contains(&Sanitizer::Hwaddress), + ignore_reason: "ignored on targets without hardware-assisted address sanitizer", + }, + Need { + name: "needs-sanitizer-memtag", + condition: sanitizers.contains(&Sanitizer::Memtag), + ignore_reason: "ignored on targets without memory tagging sanitizer", + }, + Need { + name: "needs-sanitizer-realtime", + condition: sanitizers.contains(&Sanitizer::Realtime), + ignore_reason: "ignored on targets without realtime sanitizer", + }, + Need { + name: "needs-sanitizer-shadow-call-stack", + condition: sanitizers.contains(&Sanitizer::ShadowCallStack), + ignore_reason: "ignored on targets without shadow call stacks", + }, + Need { + name: "needs-sanitizer-safestack", + condition: sanitizers.contains(&Sanitizer::Safestack), + ignore_reason: "ignored on targets without SafeStack support", + }, + Need { + name: "needs-enzyme", + condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-offload", + condition: config.has_offload && config.default_codegen_backend.is_llvm(), + ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", + }, + Need { + name: "needs-run-enabled", + condition: config.run_enabled(), + ignore_reason: "ignored when running the resulting test binaries is disabled", + }, + Need { + name: "needs-threads", + condition: config.has_threads(), + ignore_reason: "ignored on targets without threading support", + }, + Need { + name: "needs-subprocess", + condition: config.has_subprocess_support(), + ignore_reason: "ignored on targets without subprocess support", + }, + Need { + name: "needs-unwind", + condition: config.can_unwind(), + ignore_reason: "ignored on targets without unwinding support", + }, + Need { + name: "needs-profiler-runtime", + condition: config.profiler_runtime, + ignore_reason: "ignored when the profiler runtime is not available", + }, + Need { + name: "needs-force-clang-based-tests", + condition: config.run_clang_based_tests_with.is_some(), + ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", + }, + Need { + name: "needs-xray", + condition: config.target_cfg().xray, + ignore_reason: "ignored on targets without xray tracing", + }, + Need { + name: "needs-rust-lld", + condition: { + // For tests using the `needs-rust-lld` directive (e.g. for `-Clink-self-contained=+linker`), + // we need to find whether `rust-lld` is present in the compiler under test. + // + // The --compile-lib-path is the path to host shared libraries, but depends on the OS. For + // example: + // - on linux, it can be /lib + // - on windows, it can be /bin + // + // However, `rust-lld` is only located under the lib path, so we look for it there. + config + .host_compile_lib_path + .parent() + .expect("couldn't traverse to the parent of the specified --compile-lib-path") + .join("lib") + .join("rustlib") + .join(target) + .join("bin") + .join(if config.host.contains("windows") { "rust-lld.exe" } else { "rust-lld" }) + .exists() + }, + ignore_reason: "ignored on targets without Rust's LLD", + }, + Need { + name: "needs-dlltool", + condition: find_dlltool(config), + ignore_reason: "ignored when dlltool for the current architecture is not present", + }, + Need { + name: "needs-git-hash", + condition: config.git_hash, + ignore_reason: "ignored when git hashes have been omitted for building", + }, + Need { + name: "needs-dynamic-linking", + condition: config.target_cfg().dynamic_linking, + ignore_reason: "ignored on targets without dynamic linking", + }, + Need { + name: "needs-relocation-model-pic", + condition: config.target_cfg().relocation_model == "pic", + ignore_reason: "ignored on targets without PIC relocation model", + }, + Need { + name: "needs-deterministic-layouts", + condition: !config.rust_randomized_layout, + ignore_reason: "ignored when randomizing layouts", + }, + Need { + name: "needs-wasmtime", + condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), + ignore_reason: "ignored when wasmtime runner is not available", + }, + Need { + name: "needs-symlink", + condition: has_symlinks(), + ignore_reason: "ignored if symlinks are unavailable", + }, + Need { + name: "needs-llvm-zstd", + condition: config.default_codegen_backend.is_llvm() && llvm_has_zstd(config), + ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", + }, + Need { + name: "needs-rustc-debug-assertions", + condition: config.with_rustc_debug_assertions, + ignore_reason: "ignored if rustc wasn't built with debug assertions", + }, + Need { + name: "needs-std-debug-assertions", + condition: config.with_std_debug_assertions, + ignore_reason: "ignored if std wasn't built with debug assertions", + }, + Need { + name: "needs-std-remap-debuginfo", + condition: config.with_std_remap_debuginfo, + ignore_reason: "ignored if std wasn't built with remapping of debuginfo", + }, + Need { + name: "needs-target-std", + condition: build_helper::targets::target_supports_std(&config.target), + ignore_reason: "ignored if target does not support std", + }, + ]; + + PreparedNeedsConditions { + simple_needs, + has_ret_mnemonic: has_mnemonic(config, "ret"), + has_nop_mnemonic: has_mnemonic(config, "nop"), } } diff --git a/src/tools/compiletest/src/directives/needs/simple.rs b/src/tools/compiletest/src/directives/needs/simple.rs deleted file mode 100644 index 9ab32f9f8cf10..0000000000000 --- a/src/tools/compiletest/src/directives/needs/simple.rs +++ /dev/null @@ -1,200 +0,0 @@ -use crate::common::Config; -use crate::directives::CachedNeedsConditions; -use crate::directives::needs::Need; - -pub(crate) fn simple_needs(cache: &CachedNeedsConditions, config: &Config) -> Vec { - // Note that we intentionally still put the needs- prefix here to make the file show up when - // grepping for a directive name, even though we could technically strip that. - vec![ - Need { - name: "needs-asm-support", - condition: config.has_asm_support(), - ignore_reason: "ignored on targets without inline assembly support", - }, - Need { - name: "needs-sanitizer-support", - condition: cache.sanitizer_support, - ignore_reason: "ignored on targets without sanitizers support", - }, - Need { - name: "needs-sanitizer-address", - condition: cache.sanitizer_address, - ignore_reason: "ignored on targets without address sanitizer", - }, - Need { - name: "needs-sanitizer-cfi", - condition: cache.sanitizer_cfi, - ignore_reason: "ignored on targets without CFI sanitizer", - }, - Need { - name: "needs-sanitizer-dataflow", - condition: cache.sanitizer_dataflow, - ignore_reason: "ignored on targets without dataflow sanitizer", - }, - Need { - name: "needs-sanitizer-kcfi", - condition: cache.sanitizer_kcfi, - ignore_reason: "ignored on targets without kernel CFI sanitizer", - }, - Need { - name: "needs-sanitizer-kasan", - condition: cache.sanitizer_kasan, - ignore_reason: "ignored on targets without kernel address sanitizer", - }, - Need { - name: "needs-sanitizer-khwasan", - condition: cache.sanitizer_khwasan, - ignore_reason: "ignored on targets without kernel hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-leak", - condition: cache.sanitizer_leak, - ignore_reason: "ignored on targets without leak sanitizer", - }, - Need { - name: "needs-sanitizer-memory", - condition: cache.sanitizer_memory, - ignore_reason: "ignored on targets without memory sanitizer", - }, - Need { - name: "needs-sanitizer-thread", - condition: cache.sanitizer_thread, - ignore_reason: "ignored on targets without thread sanitizer", - }, - Need { - name: "needs-sanitizer-hwaddress", - condition: cache.sanitizer_hwaddress, - ignore_reason: "ignored on targets without hardware-assisted address sanitizer", - }, - Need { - name: "needs-sanitizer-memtag", - condition: cache.sanitizer_memtag, - ignore_reason: "ignored on targets without memory tagging sanitizer", - }, - Need { - name: "needs-sanitizer-realtime", - condition: cache.sanitizer_realtime, - ignore_reason: "ignored on targets without realtime sanitizer", - }, - Need { - name: "needs-sanitizer-shadow-call-stack", - condition: cache.sanitizer_shadow_call_stack, - ignore_reason: "ignored on targets without shadow call stacks", - }, - Need { - name: "needs-sanitizer-safestack", - condition: cache.sanitizer_safestack, - ignore_reason: "ignored on targets without SafeStack support", - }, - Need { - name: "needs-enzyme", - condition: config.has_enzyme && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-offload", - condition: config.has_offload && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored when LLVM Offload is disabled or LLVM is not the default codegen backend", - }, - Need { - name: "needs-run-enabled", - condition: config.run_enabled(), - ignore_reason: "ignored when running the resulting test binaries is disabled", - }, - Need { - name: "needs-threads", - condition: config.has_threads(), - ignore_reason: "ignored on targets without threading support", - }, - Need { - name: "needs-subprocess", - condition: config.has_subprocess_support(), - ignore_reason: "ignored on targets without subprocess support", - }, - Need { - name: "needs-unwind", - condition: config.can_unwind(), - ignore_reason: "ignored on targets without unwinding support", - }, - Need { - name: "needs-profiler-runtime", - condition: config.profiler_runtime, - ignore_reason: "ignored when the profiler runtime is not available", - }, - Need { - name: "needs-force-clang-based-tests", - condition: config.run_clang_based_tests_with.is_some(), - ignore_reason: "ignored when RUSTBUILD_FORCE_CLANG_BASED_TESTS is not set", - }, - Need { - name: "needs-xray", - condition: cache.xray, - ignore_reason: "ignored on targets without xray tracing", - }, - Need { - name: "needs-rust-lld", - condition: cache.rust_lld, - ignore_reason: "ignored on targets without Rust's LLD", - }, - Need { - name: "needs-dlltool", - condition: cache.dlltool, - ignore_reason: "ignored when dlltool for the current architecture is not present", - }, - Need { - name: "needs-git-hash", - condition: config.git_hash, - ignore_reason: "ignored when git hashes have been omitted for building", - }, - Need { - name: "needs-dynamic-linking", - condition: config.target_cfg().dynamic_linking, - ignore_reason: "ignored on targets without dynamic linking", - }, - Need { - name: "needs-relocation-model-pic", - condition: config.target_cfg().relocation_model == "pic", - ignore_reason: "ignored on targets without PIC relocation model", - }, - Need { - name: "needs-deterministic-layouts", - condition: !config.rust_randomized_layout, - ignore_reason: "ignored when randomizing layouts", - }, - Need { - name: "needs-wasmtime", - condition: config.runner.as_ref().is_some_and(|r| r.contains("wasmtime")), - ignore_reason: "ignored when wasmtime runner is not available", - }, - Need { - name: "needs-symlink", - condition: cache.symlinks, - ignore_reason: "ignored if symlinks are unavailable", - }, - Need { - name: "needs-llvm-zstd", - condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(), - ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend", - }, - Need { - name: "needs-rustc-debug-assertions", - condition: config.with_rustc_debug_assertions, - ignore_reason: "ignored if rustc wasn't built with debug assertions", - }, - Need { - name: "needs-std-debug-assertions", - condition: config.with_std_debug_assertions, - ignore_reason: "ignored if std wasn't built with debug assertions", - }, - Need { - name: "needs-std-remap-debuginfo", - condition: config.with_std_remap_debuginfo, - ignore_reason: "ignored if std wasn't built with remapping of debuginfo", - }, - Need { - name: "needs-target-std", - condition: build_helper::targets::target_supports_std(&config.target), - ignore_reason: "ignored if target does not support std", - }, - ] -} From 9a5d93e4d9efa635296c86f2b389425b6d6d4987 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 18 May 2026 14:50:56 +1000 Subject: [PATCH 054/174] Store prepared `//@ needs-*` conditions in a HashMap --- src/tools/compiletest/src/directives/needs.rs | 43 ++++++++++--------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index a5858a1d3a327..64c488aaa7935 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -1,7 +1,9 @@ +use std::collections::HashMap; + use crate::common::{ Config, KNOWN_CRATE_TYPES, KNOWN_TARGET_HAS_ATOMIC_WIDTHS, Sanitizer, query_rustc_output, }; -use crate::directives::{DirectiveLine, IgnoreDecision}; +use crate::directives::{DirectiveLine, IgnoreDecision, KNOWN_DIRECTIVE_NAMES_SET}; pub(super) fn handle_needs( conditions: &PreparedNeedsConditions, @@ -135,26 +137,18 @@ pub(super) fn handle_needs( return IgnoreDecision::Continue; } - let mut found_valid = false; - for need in &conditions.simple_needs { - if need.name == name { - if need.condition { - found_valid = true; - break; - } else { - return IgnoreDecision::Ignore { - reason: if let Some(comment) = ln.remark_after_space() { - format!("{} ({})", need.ignore_reason, comment.trim()) - } else { - need.ignore_reason.into() - }, - }; + if let Some(need) = conditions.simple_needs.get(name) { + if need.condition { + IgnoreDecision::Continue + } else { + IgnoreDecision::Ignore { + reason: if let Some(comment) = ln.remark_after_space() { + format!("{} ({})", need.ignore_reason, comment.trim()) + } else { + need.ignore_reason.into() + }, } } - } - - if found_valid { - IgnoreDecision::Continue } else { IgnoreDecision::Error { message: format!("invalid needs directive: {name}") } } @@ -168,7 +162,7 @@ struct Need { pub(crate) struct PreparedNeedsConditions { /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. - simple_needs: Vec, + simple_needs: HashMap<&'static str, Need>, /// Might add particular other mnemonics heavily needed by tests here. /// Otherwise call into llvm for every check @@ -394,6 +388,15 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio ignore_reason: "ignored if target does not support std", }, ]; + let simple_needs = simple_needs + .into_iter() + .map(|need| { + let name = need.name; + assert!(name.starts_with("needs-"), "must start with `needs-`: {name:?}"); + assert!(KNOWN_DIRECTIVE_NAMES_SET.contains(name), "unknown directive name: {name:?}"); + (name, need) + }) + .collect::>(); PreparedNeedsConditions { simple_needs, From 52b5f55db3f26fd75b84aa851b7caf4bb924e97b Mon Sep 17 00:00:00 2001 From: Kajetan Puchalski Date: Thu, 22 Jan 2026 12:13:07 +0000 Subject: [PATCH 055/174] ci: Add dist-aarch64-freebsd Add scripts to build the aarch64-unknown-freebsd target in CI. Implements MCP: https://github.com/rust-lang/compiler-team/issues/961 --- .../dist-aarch64-freebsd/Dockerfile | 44 +++++++++++++++++++ src/ci/docker/scripts/freebsd-toolchain.sh | 1 + src/ci/github-actions/jobs.yml | 3 ++ 3 files changed, 48 insertions(+) create mode 100644 src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile diff --git a/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile b/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile new file mode 100644 index 0000000000000..ca597acb72c5c --- /dev/null +++ b/src/ci/docker/host-aarch64/dist-aarch64-freebsd/Dockerfile @@ -0,0 +1,44 @@ +FROM ubuntu:26.04 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + clang \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + bzip2 \ + xz-utils \ + texinfo \ + wget \ + libssl-dev \ + pkg-config \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/freebsd-toolchain.sh /tmp/ +RUN /tmp/freebsd-toolchain.sh aarch64 + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +COPY scripts/cmake.sh /scripts/ +RUN /scripts/cmake.sh + +ENV \ + AR_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-ar \ + CC_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-clang \ + CXX_aarch64_unknown_freebsd=aarch64-unknown-freebsd14-clang++ + +ENV HOSTS=aarch64-unknown-freebsd + +ENV RUST_CONFIGURE_ARGS="--enable-full-tools \ + --enable-extended \ + --enable-profiler \ + --enable-sanitizers \ + --disable-docs" + +ENV SCRIPT="python3 ../x.py dist --host $HOSTS --target $HOSTS" diff --git a/src/ci/docker/scripts/freebsd-toolchain.sh b/src/ci/docker/scripts/freebsd-toolchain.sh index 5308288d911a0..8b4081d27d083 100755 --- a/src/ci/docker/scripts/freebsd-toolchain.sh +++ b/src/ci/docker/scripts/freebsd-toolchain.sh @@ -46,6 +46,7 @@ mkdir -p "$sysroot" case $arch in (x86_64) freebsd_arch=amd64 ;; (i686) freebsd_arch=i386 ;; + (aarch64) freebsd_arch=arm64 ;; esac files_to_extract=( diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 97ddde799a757..448a2c927b394 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -261,6 +261,9 @@ auto: - name: dist-x86_64-freebsd <<: *job-linux-4c + - name: dist-aarch64-freebsd + <<: *job-aarch64-linux + - name: dist-x86_64-illumos <<: *job-linux-4c From 83286697296bf72ed56514b8374226f33f99d920 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Mon, 18 May 2026 14:12:34 +0000 Subject: [PATCH 056/174] rustc_driver: do not inline docs from `_impl` crate The inlined docs from `_impl` crate will not have the source code shown/linked but search results for functions such as `init_rustc_env_logger` show the one from the `rustc_driver` crate (without source code). It makes sense to only keep the `_impl` crate's docs in this case. --- compiler/rustc_driver/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/compiler/rustc_driver/src/lib.rs b/compiler/rustc_driver/src/lib.rs index 0cd0b51b6ad49..54699831e3717 100644 --- a/compiler/rustc_driver/src/lib.rs +++ b/compiler/rustc_driver/src/lib.rs @@ -1,4 +1,5 @@ // This crate is intentionally empty and a re-export of `rustc_driver_impl` to allow the code in // `rustc_driver_impl` to be compiled in parallel with other crates. +#[doc(no_inline)] pub use rustc_driver_impl::*; From 835004a53faf868e7e9a712ce49234e150bb4752 Mon Sep 17 00:00:00 2001 From: Predrag Gruevski Date: Mon, 18 May 2026 17:24:46 +0200 Subject: [PATCH 057/174] Make attr template suggestions preserve attribute safety. --- .../rustc_attr_parsing/src/attributes/cfg.rs | 14 ++++++++++---- compiler/rustc_attr_parsing/src/context.rs | 17 ++++++++++++++--- compiler/rustc_attr_parsing/src/interface.rs | 2 ++ compiler/rustc_feature/src/builtin_attrs.rs | 18 ++++++++++++++---- 4 files changed, 40 insertions(+), 11 deletions(-) diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index f838f73838499..bc014ae278183 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -327,8 +327,11 @@ pub fn parse_cfg_attr( }) { Ok(r) => return Some(r), Err(e) => { - let suggestions = CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr); + let suggestions = CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + sym::cfg_attr, + ); e.with_span_suggestions( cfg_attr.span, "must be of the form", @@ -360,8 +363,11 @@ pub fn parse_cfg_attr( description: ParsedDescription::Attribute, reason, suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( - CFG_ATTR_TEMPLATE - .suggestions(AttrSuggestionStyle::Attribute(cfg_attr.style), sym::cfg_attr), + CFG_ATTR_TEMPLATE.suggestions( + AttrSuggestionStyle::Attribute(cfg_attr.style), + matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + sym::cfg_attr, + ), ), }); } diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 81cd32faea127..2d5f46b3fb8f2 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -5,7 +5,7 @@ use std::mem; use std::ops::{Deref, DerefMut}; use std::sync::LazyLock; -use rustc_ast::{AttrStyle, MetaItemLit}; +use rustc_ast::{AttrStyle, MetaItemLit, Safety}; use rustc_data_structures::sync::{DynSend, DynSync}; use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, Level, MultiSpan}; use rustc_feature::{AttrSuggestionStyle, AttributeTemplate}; @@ -357,6 +357,9 @@ pub struct AcceptContext<'f, 'sess> { /// Used in reporting errors to give a hint to users what the attribute *should* look like. pub(crate) template: &'f AttributeTemplate, + /// The safety attribute (if any) applied to the attribute. + pub(crate) attr_safety: rustc_ast::Safety, + /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, } @@ -873,7 +876,11 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions( + style, + matches!(self.attr_safety, rustc_ast::Safety::Unsafe(_)), + &self.attr_path, + ) } } @@ -1064,7 +1071,11 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions(style, &self.attr_path) + self.template.suggestions( + style, + matches!(self.attr_safety, Safety::Unsafe(_)), + &self.attr_path, + ) } /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) diff --git a/compiler/rustc_attr_parsing/src/interface.rs b/compiler/rustc_attr_parsing/src/interface.rs index 8260663850344..4a0ad63c0c053 100644 --- a/compiler/rustc_attr_parsing/src/interface.rs +++ b/compiler/rustc_attr_parsing/src/interface.rs @@ -234,6 +234,7 @@ impl<'sess> AttributeParser<'sess> { attr_style, parsed_description, template, + attr_safety: attr_safety.unwrap_or(Safety::Default), attr_path, }; parse_fn(&mut cx, args) @@ -404,6 +405,7 @@ impl<'sess> AttributeParser<'sess> { attr_style: attr.style, parsed_description: ParsedDescription::Attribute, template: &accept.template, + attr_safety: n.item.unsafety, attr_path: attr_path.clone(), }; diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 58bf12855ad6e..fa37466d4d5cc 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -118,6 +118,7 @@ impl AttributeTemplate { pub fn suggestions( &self, style: AttrSuggestionStyle, + wrap_with_unsafe: bool, name: impl std::fmt::Display, ) -> Vec { let (start, macro_call, end) = match style { @@ -129,20 +130,29 @@ impl AttributeTemplate { let mut suggestions = vec![]; + let (maybe_unsafe_start, maybe_unsafe_end) = + if wrap_with_unsafe { ("unsafe(", ")") } else { ("", "") }; + if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{start}{name}{end}")); + suggestions.push(format!("{maybe_unsafe_start}{start}{name}{end}{maybe_unsafe_end}")); } if let Some(descr) = self.list { for descr in descr { - suggestions.push(format!("{start}{name}{macro_call}({descr}){end}")); + suggestions.push(format!( + "{maybe_unsafe_start}{start}{name}{macro_call}({descr}){end}{maybe_unsafe_end}" + )); } } - suggestions.extend(self.one_of.iter().map(|&word| format!("{start}{name}({word}){end}"))); + suggestions.extend(self.one_of.iter().map(|&word| { + format!("{maybe_unsafe_start}{start}{name}({word}){end}{maybe_unsafe_end}") + })); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { - suggestions.push(format!("{start}{name} = \"{descr}\"{end}")); + suggestions.push(format!( + "{maybe_unsafe_start}{start}{name} = \"{descr}\"{end}{maybe_unsafe_end}" + )); } } suggestions.sort(); From 79cfca599ec08f588646a087f7df886498a7db7b Mon Sep 17 00:00:00 2001 From: WhySoBad <49595640+WhySoBad@users.noreply.github.com> Date: Wed, 13 May 2026 10:45:35 +0200 Subject: [PATCH 058/174] Various TCP socket `connect` fixes 1. Blocking `connect`s which fail no longer only return EINPROGRESS but instead return the correct error code. 2. Manually reading SO_ERROR outside of `ensure_connected` no longer allows the socket to be upgraded to `Connected` despite the `connect` failing. 3. Introduced new `ConnectionFailed` state to disallow actions on sockets with a failed `connect`. --- src/tools/miri/src/shims/unix/socket.rs | 156 +++++++++++++----- .../socket-connect-after-failed-connection.rs | 68 ++++++++ ...ket-connect-after-failed-connection.stderr | 16 ++ .../miri/tests/pass-dep/libc/libc-socket.rs | 22 +++ 4 files changed, 224 insertions(+), 38 deletions(-) create mode 100644 src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs create mode 100644 src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr diff --git a/src/tools/miri/src/shims/unix/socket.rs b/src/tools/miri/src/shims/unix/socket.rs index b06a151e116e4..2e9a26974eff5 100644 --- a/src/tools/miri/src/shims/unix/socket.rs +++ b/src/tools/miri/src/shims/unix/socket.rs @@ -45,6 +45,13 @@ enum SocketState { /// For a socket created using the `connect` syscall, this is /// only reachable from the [`SocketState::Connecting`] state. Connected(TcpStream), + /// The SO_ERROR socket option has been set after calling + /// the `connect` syscall, indicating that the connection + /// attempt failed. By the POSIX specification, a socket is + /// is an unspecified state after a failed connection attempt + /// and thus nothing (except destroying the socket) should be + /// supported when a socket is in this state. + ConnectionFailed(TcpStream), } #[derive(Debug)] @@ -77,7 +84,10 @@ impl FileDescription for Socket { if matches!( &*self.state.borrow(), - SocketState::Listening(_) | SocketState::Connecting(_) | SocketState::Connected(_) + SocketState::Listening(_) + | SocketState::Connecting(_) + | SocketState::Connected(_) + | SocketState::ConnectionFailed(_) ) { // There exists an associated host socket so we need to deregister it // from the blocking I/O manager. @@ -373,6 +383,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "bind")?; let mut state = socket.state.borrow_mut(); @@ -411,6 +422,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { "bind: socket is already bound and binding a socket \ multiple times is unsupported" ), + SocketState::ConnectionFailed(_) => unreachable!(), } interp_ok(Scalar::from_i32(0)) @@ -434,6 +446,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "listen")?; let mut state = socket.state.borrow_mut(); @@ -460,6 +473,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SocketState::Connecting(_) | SocketState::Connected(_) => { throw_unsup_format!("listen: listening on a connected socket is unsupported") } + SocketState::ConnectionFailed(_) => unreachable!(), } interp_ok(Scalar::from_i32(0)) @@ -495,6 +509,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "accept4")?; if !matches!(*socket.state.borrow(), SocketState::Listening(_)) { throw_unsup_format!( @@ -589,6 +604,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "connect")?; match &*socket.state.borrow() { SocketState::Initial => { /* fall-through to below */ } @@ -632,15 +648,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let dest = dest.clone(); this.ensure_connected( - socket, + socket.clone(), /* should_wait */ true, "connect", callback!( @capture<'tcx> { + socket: FileDescriptionRef, dest: MPlaceTy<'tcx> } |this, result: Result<(), ()>| { if result.is_err() { - this.set_last_error_and_return(LibcError("ENOTCONN"), &dest) + // An error occurred whilst connecting. We know + // that it has been consumed by `ensure_connected` + // and is now stored in `socket.error`. + let err = socket.error.take().unwrap(); + this.set_last_error_and_return(err, &dest) } else { this.write_scalar(Scalar::from_i32(0), &dest) } @@ -964,6 +985,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SocketState::Listening(listener) => listener.set_ttl(ttl), SocketState::Connecting(stream) | SocketState::Connected(stream) => stream.set_ttl(ttl), + SocketState::ConnectionFailed(_) => unreachable!(), }; return match result { @@ -994,6 +1016,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ), SocketState::Connecting(stream) | SocketState::Connected(stream) => stream.set_nodelay(nodelay), + SocketState::ConnectionFailed(_) => unreachable!(), }; return match result { @@ -1064,17 +1087,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { let opt_so_error = this.eval_libc_i32("SO_ERROR"); if option_name == opt_so_error { - // Because `TcpStream::take_error()` and `TcpListener::take_error()` consume the latest async - // error, we know that our stored `socket.error` is outdated when `TcpStream::take_error()`/ - // `TcpListener::take_error()` returns `Ok(Some(...))`. - // If they return `Ok(None)`, then we fall back to the stored `socket.error`. - let error = match &*socket.state.borrow() { - SocketState::Initial | SocketState::Bound(_) => socket.error.take(), - SocketState::Listening(listener) => - listener.take_error().unwrap_or(socket.error.take()), - SocketState::Connecting(stream) | SocketState::Connected(stream) => - stream.take_error().unwrap_or(socket.error.take()), + // Reading SO_ERROR should always return the latest async error. Because our stored + // `socket.error` could be outdated, we attempt to update it here. + this.update_last_error(&socket); + + let return_value = match socket.error.take() { + Some(err) => this.io_error_to_errnum(err)?.to_i32()?, + // If there is no error, we return 0 as the option value. + None => 0, }; + // Clear our own stored error -- it was either `take`n above or it is outdated. socket.error.replace(None); @@ -1083,12 +1105,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { socket.io_readiness.borrow_mut().error = false; this.update_epoll_active_events(socket, /* force_edge */ false)?; - let return_value = match error { - Some(err) => this.io_error_to_errnum(err)?.to_i32()?, - // If there is no error, we write 0 into the option value buffer. - None => 0, - }; - // Allocate new buffer on the stack with the `i32` layout. let value_buffer = this.allocate(this.machine.layouts.i32, MemoryKind::Stack)?; this.write_int(return_value, &value_buffer)?; @@ -1111,6 +1127,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { SocketState::Listening(listener) => listener.ttl(), SocketState::Connecting(stream) | SocketState::Connected(stream) => stream.ttl(), + SocketState::ConnectionFailed(_) => unreachable!(), }; let ttl = match ttl { @@ -1139,6 +1156,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ), SocketState::Connecting(stream) | SocketState::Connected(stream) => stream.nodelay(), + SocketState::ConnectionFailed(_) => unreachable!(), }; let nodelay = match nodelay { @@ -1211,6 +1229,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "getsockname")?; let state = socket.state.borrow(); @@ -1254,6 +1273,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // For non-bound sockets the POSIX manual says the returned address is unspecified. // Often this is 0.0.0.0:0 and thus we set it to this value. SocketState::Initial => SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0)), + SocketState::ConnectionFailed(_) => unreachable!(), }; this.write_socket_address(&address, address_ptr, address_len_ptr, "getsockname") @@ -1343,6 +1363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { }; assert!(this.machine.communicate(), "cannot have `Socket` with isolation enabled!"); + this.ensure_not_failed(&socket, "shutdown")?; let state = socket.state.borrow(); @@ -1744,10 +1765,13 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Execute the provided callback function when the socket is either in // [`SocketState::Connected`] or an error occurred. /// If the socket is currently neither in the [`SocketState::Connecting`] nor - /// the [`SocketState::Connecting`] state, an ENOTCONN error is returned. - /// When the callback function is called with `Ok(_)`, then we're guaranteed + /// the [`SocketState::Connecting`] state, [`Err`] is returned. + /// When the callback function is called with [`Ok`], then we're guaranteed /// that the socket is in the [`SocketState::Connected`] state. /// + /// This method internally calls `ensure_not_failed` and thus an unsupported + /// error is thrown should `socket` be in [`SocketState::ConnectionFailed`]. + /// /// This function can optionally also block until either an error occurred or /// the socket reached the [`SocketState::Connected`] state. fn ensure_connected( @@ -1768,6 +1792,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { } _ => { drop(state); + this.ensure_not_failed(&socket, foreign_name)?; return action.call(this, Err(())); } }; @@ -1805,9 +1830,9 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The thread woke up because it's ready, indicating a writeable or error event. - let mut state = socket.state.borrow_mut(); - let stream = match &*state { - SocketState::Connecting(stream) => stream, + let state = socket.state.borrow(); + match &*state { + SocketState::Connecting(_) => { /* fall-through to below */ }, SocketState::Connected(_) => { drop(state); // This can happen because we blocked the thread: @@ -1820,25 +1845,19 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // Since this thread just got rescheduled, it could be that another // thread realized that the connection failed and we're thus in // an "invalid state". + this.ensure_not_failed(&socket, foreign_name)?; return action.call(this, Err(())) } }; - // Manually check whether there were any errors since calling `connect`. - if let Ok(Some(err)) = stream.take_error() { - // There was an error during connecting and thus we - // return ENOTCONN. It's the program's responsibility - // to read SO_ERROR itself. + drop(state); - // Store the error such that we can return it when - // `getsockopt(SOL_SOCKET, SO_ERROR, ...)` is called on the socket. - socket.error.replace(Some(err)); + // Set `socket.error` if `socket` currently has an error. + this.update_last_error(&socket); - // Go back to initial state since the only way of getting into the - // `Connecting` state is from the `Initial` state and at this point - // we know that the connection won't be established anymore. - *state = SocketState::Initial; - drop(state); + if socket.error.borrow().is_some() { + // There was an error during connecting. + // It's the program's responsibility to read SO_ERROR itself. return action.call(this, Err(())) } @@ -1860,6 +1879,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { // The connection is established. // Temporarily use dummy state to take ownership of the stream. + let mut state = socket.state.borrow_mut(); let SocketState::Connecting(stream) = std::mem::replace(&mut*state, SocketState::Initial) else { // At the start of the function we ensured that we're currently connecting. unreachable!() @@ -1871,6 +1891,64 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> { ), ) } + + /// Ensure that `socket` is not in the [`SocketState::ConnectionFailed`] state. + /// If `socket` is currently in [`SocketState::ConnectionFailed`], an unsupported + /// error is thrown. + fn ensure_not_failed( + &self, + socket: &FileDescriptionRef, + foreign_name: &'static str, + ) -> InterpResult<'tcx> { + if let SocketState::ConnectionFailed(_) = &*socket.state.borrow() { + throw_unsup_format!( + "{foreign_name}: sockets are in an unspecified state after a failed `connect`; \ + any operation on such a socket is thus unsupported" + ); + } else { + interp_ok(()) + } + } + + /// Check whether the underlying host socket of `socket` contains an error. + /// If there is an error, we store it in `socket.error`. + /// + /// Should `socket` be in the [`SocketState::Connecting`] state whilst there is + /// an error on the host socket, we transition into the [`SocketState::ConnectionFailed`] + /// state because we know that `socket` can no longer successfully establish a + /// connection. + fn update_last_error(&self, socket: &FileDescriptionRef) { + let mut state = socket.state.borrow_mut(); + + let new_error = match &*state { + SocketState::Listening(listener) => + listener.take_error().expect("Reading SO_ERROR should not fail"), + SocketState::Connecting(stream) | SocketState::Connected(stream) => + stream.take_error().expect("Reading SO_ERROR should not fail"), + SocketState::Initial | SocketState::Bound(_) | SocketState::ConnectionFailed(_) => None, + }; + + let Some(new_error) = new_error else { return }; + + // Store the error such that we can return it when + // `getsockopt(SOL_SOCKET, SO_ERROR, ...)` is called on the socket. + socket.error.replace(Some(new_error)); + + if matches!(&*state, SocketState::Connecting(_)) { + // After reading an error on a connecting socket, we know that + // the connection won't be established anymore. By the POSIX + // specification, the socket is now in an unspecified state. + // We thus change the socket state to `ConnectionFailed`. + + // Temporarily use dummy state to take ownership of the stream. + let SocketState::Connecting(stream) = + std::mem::replace(&mut *state, SocketState::Initial) + else { + unreachable!() + }; + *state = SocketState::ConnectionFailed(stream); + } + } } impl VisitProvenance for FileDescriptionRef { @@ -1884,7 +1962,9 @@ impl SourceFileDescription for Socket { let mut state = self.state.borrow_mut(); match &mut *state { SocketState::Listening(listener) => f(listener), - SocketState::Connecting(stream) | SocketState::Connected(stream) => f(stream), + SocketState::Connecting(stream) + | SocketState::Connected(stream) + | SocketState::ConnectionFailed(stream) => f(stream), // We never try adding a socket which is not backed by a real socket to the poll registry. _ => unreachable!(), } diff --git a/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs new file mode 100644 index 0000000000000..babd2d8410207 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.rs @@ -0,0 +1,68 @@ +//@only-target: linux android illumos +//@compile-flags: -Zmiri-disable-isolation + +#![feature(io_error_inprogress)] + +#[path = "../../utils/libc.rs"] +mod libc_utils; + +use std::io::ErrorKind; + +use libc_utils::epoll::*; +use libc_utils::*; + +/// Test calling `connect` after the first `connect` failed. +fn main() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + let epfd = errno_result(unsafe { libc::epoll_create1(0) }).unwrap(); + + unsafe { + // Change client socket to be non-blocking. + errno_check(libc::fcntl(client_sockfd, libc::F_SETFL, libc::O_NONBLOCK)); + } + + // We cannot attempt to connect to a localhost address because + // it could be the case that a socket from another test is + // currently listening on `localhost:12321` because we bind to + // random ports everywhere. For `127.0.1.1` we know that it's a loopback + // address and thus exists but because it's not the standard loopback + // address we also assume that nothing is bound to it. + // The port `12321` is just a random non-zero port because Windows + // and Apple hosts return EADDRNOTAVAIL when attempting to connect to + // a zero port. + let addr = net::sock_addr_ipv4([127, 0, 1, 1], 12321); + + // Non-blocking connect should fail with EINPROGRESS. + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + assert_eq!(err.kind(), ErrorKind::InProgress); + + // Add interest for client socket. + epoll_ctl_add(epfd, client_sockfd, EPOLLOUT | EPOLLET | libc::EPOLLERR).unwrap(); + + // Wait until the socket has an error. + check_epoll_wait::<8>( + epfd, + &[Ev { events: libc::EPOLLERR | EPOLLOUT | EPOLLHUP, data: client_sockfd }], + -1, + ); + + let errno = + net::getsockopt::(client_sockfd, libc::SOL_SOCKET, libc::SO_ERROR).unwrap(); + // Depending on the host we receive different error kinds. Thus, we only check + // that it's a nonzero error code. + assert!(errno != 0); + + // Ensure that error readiness is cleared after reading SO_ERROR. + let readiness = current_epoll_readiness::<8>(client_sockfd, EPOLLET | EPOLLOUT | EPOLLERR); + assert!(readiness & EPOLLERR == 0); + + unsafe { + //~vERROR: sockets are in an unspecified state after a failed `connect` + libc::connect( + client_sockfd, + (&addr as *const libc::sockaddr_in).cast(), + size_of::() as libc::socklen_t, + ) + }; +} diff --git a/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr new file mode 100644 index 0000000000000..3743c3ca58f22 --- /dev/null +++ b/src/tools/miri/tests/fail-dep/libc/socket-connect-after-failed-connection.stderr @@ -0,0 +1,16 @@ +error: unsupported operation: connect: sockets are in an unspecified state after a failed `connect`; any operation on such a socket is thus unsupported + --> tests/fail-dep/libc/socket-connect-after-failed-connection.rs:LL:CC + | +LL | / libc::connect( +LL | | client_sockfd, +LL | | (&addr as *const libc::sockaddr_in).cast(), +LL | | size_of::() as libc::socklen_t, +LL | | ) + | |_________^ unsupported operation occurred here + | + = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support + +note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace + +error: aborting due to 1 previous error + diff --git a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs index 16ec68198db1f..1ecfba3ff6ff0 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-socket.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-socket.rs @@ -35,6 +35,7 @@ fn main() { test_listen(); test_accept_connect(); + test_connect_error(); test_send_peek_recv(); test_write_read(); test_readv(); @@ -258,6 +259,27 @@ fn test_accept_connect() { server_thread.join().unwrap(); } +/// Test connecting to an address where nothing is listening and ensure the error matches what +/// the standard library expects. +fn test_connect_error() { + let client_sockfd = + unsafe { errno_result(libc::socket(libc::AF_INET, libc::SOCK_STREAM, 0)).unwrap() }; + + // Connecting to a zero port fails on all host platforms. + let addr = net::sock_addr_ipv4(net::IPV4_LOCALHOST, 0); + + let err = net::connect_ipv4(client_sockfd, addr).unwrap_err(); + // Ensure that we fail for the same reasons as the standard library expects. + assert!(matches!( + err.kind(), + ErrorKind::ConnectionRefused + | ErrorKind::InvalidInput + | ErrorKind::AddrInUse + | ErrorKind::AddrNotAvailable + | ErrorKind::NetworkUnreachable + )); +} + /// Test sending bytes into a connected stream and then peeking and receiving /// them from the other end. /// We especially want to test that the peeking doesn't remove the bytes from From 6c88e90ab1ce2469594d0fd51fd34a0a192ee1c7 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 May 2026 01:47:00 +0200 Subject: [PATCH 059/174] Move `span_map` file to the right folder --- src/librustdoc/html/highlight.rs | 4 ++-- src/librustdoc/html/mod.rs | 1 + src/librustdoc/html/render/context.rs | 4 ++-- src/librustdoc/html/render/mod.rs | 2 -- src/librustdoc/html/{render => }/span_map.rs | 0 5 files changed, 5 insertions(+), 6 deletions(-) rename src/librustdoc/html/{render => }/span_map.rs (100%) diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1c162a79c4c44..d081634fafebb 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -23,8 +23,8 @@ use crate::display::Joined as _; use crate::html::escape::EscapeBodyText; use crate::html::format::HrefInfo; use crate::html::macro_expansion::ExpandedCode; -use crate::html::render::span_map::{DUMMY_SP, Span}; -use crate::html::render::{Context, LinkFromSrc}; +use crate::html::render::Context; +use crate::html::span_map::{DUMMY_SP, LinkFromSrc, Span}; /// This type is needed in case we want to render links on items to allow to go to their definition. pub(crate) struct HrefContext<'a, 'tcx> { diff --git a/src/librustdoc/html/mod.rs b/src/librustdoc/html/mod.rs index d42f4782845d8..382d97265cf69 100644 --- a/src/librustdoc/html/mod.rs +++ b/src/librustdoc/html/mod.rs @@ -8,6 +8,7 @@ pub(crate) mod macro_expansion; pub mod markdown; pub(crate) mod render; pub(crate) mod sources; +pub(crate) mod span_map; pub(crate) mod static_files; pub(crate) mod toc; mod url_parts_builder; diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index a2a43b3b2da75..9e52b03b3210b 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -20,7 +20,7 @@ use tracing::info; use super::print_item::{full_path, print_item, print_item_path, print_ty_path}; use super::sidebar::{ModuleLike, Sidebar, print_sidebar, sidebar_module_like}; -use super::{AllTypes, LinkFromSrc, StylePath, collect_spans_and_sources, scrape_examples_help}; +use super::{AllTypes, StylePath, scrape_examples_help}; use crate::clean::types::ExternalLocation; use crate::clean::utils::has_doc_flag; use crate::clean::{self, ExternalCrate}; @@ -33,8 +33,8 @@ use crate::formats::item_type::ItemType; use crate::html::escape::Escape; use crate::html::macro_expansion::ExpandedCode; use crate::html::markdown::{self, ErrorCodes, IdMap, plain_text_summary}; -use crate::html::render::span_map::Span; use crate::html::render::write_shared::write_shared; +use crate::html::span_map::{LinkFromSrc, Span, collect_spans_and_sources}; use crate::html::url_parts_builder::UrlPartsBuilder; use crate::html::{layout, sources, static_files}; use crate::scrape_examples::AllCallLocations; diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index fe7311719fd54..8108316a856ba 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -36,7 +36,6 @@ mod ordered_json; mod print_item; pub(crate) mod sidebar; mod sorted_template; -pub(crate) mod span_map; mod type_layout; mod write_shared; @@ -64,7 +63,6 @@ use rustc_span::symbol::{Symbol, sym}; use tracing::{debug, info}; pub(crate) use self::context::*; -pub(crate) use self::span_map::{LinkFromSrc, collect_spans_and_sources}; pub(crate) use self::write_shared::*; use crate::clean::{self, Defaultness, Item, ItemId, RenderedLink}; use crate::display::{Joined as _, MaybeDisplay as _}; diff --git a/src/librustdoc/html/render/span_map.rs b/src/librustdoc/html/span_map.rs similarity index 100% rename from src/librustdoc/html/render/span_map.rs rename to src/librustdoc/html/span_map.rs From d143a60611072727d83ad4621f02f374d2e729c8 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Wed, 6 May 2026 12:48:25 +0300 Subject: [PATCH 060/174] Privacy: try use queue instead of fixed-point iteration --- compiler/rustc_privacy/src/lib.rs | 225 ++++++++++++++++++++++++------ 1 file changed, 182 insertions(+), 43 deletions(-) diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 6a792e5a803b7..23a8ddaa3d6da 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -6,9 +6,9 @@ mod errors; -use std::fmt; use std::marker::PhantomData; use std::ops::ControlFlow; +use std::{debug_assert_matches, fmt}; use errors::{ FieldIsPrivate, FieldIsPrivateLabel, FromPrivateDependencyInPublicInterface, InPublicInterface, @@ -16,14 +16,14 @@ use errors::{ UnnamedItemIsPrivate, }; use rustc_ast::visit::{VisitorResult, try_visit}; -use rustc_data_structures::fx::FxHashSet; +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::indexmap::IndexSet; use rustc_data_structures::intern::Interned; use rustc_errors::{MultiSpan, listify}; -use rustc_hir as hir; -use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def::{CtorOf, DefKind, Res}; use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::intravisit::{self, InferKind, Visitor}; -use rustc_hir::{AmbigArg, ForeignItemId, ItemId, OwnerId, PatKind, find_attr}; +use rustc_hir::{self as hir, AmbigArg, ForeignItemId, ItemId, OwnerId, PatKind, find_attr}; use rustc_middle::middle::privacy::{EffectiveVisibilities, EffectiveVisibility, Level}; use rustc_middle::query::Providers; use rustc_middle::ty::print::PrintTraitRefExt as _; @@ -414,13 +414,76 @@ impl VisibilityLike for EffectiveVisibility { } } +type DefIdsToImpls = FxHashMap>; + +/// Visitor that collects correspondence map between defs and +/// enclosing impls. +struct DefIdsToImplsCollector<'tcx, 'a> { + tcx: TyCtxt<'tcx>, + def_ids_to_impls: &'a mut DefIdsToImpls, + impl_def_id: LocalDefId, +} + +impl<'tcx, 'a> DefIdsToImplsCollector<'tcx, 'a> { + fn collect(tcx: TyCtxt<'tcx>) -> DefIdsToImpls { + let mut def_ids_to_impls = Default::default(); + for item in tcx.hir_free_items() { + let impl_def_id = item.owner_id.def_id; + let DefKind::Impl { of_trait } = tcx.def_kind(impl_def_id) else { + continue; + }; + + // This behavior should mirror `EffectiveVisibility::of_impl::`. + let mut visitor = DefIdsToImplsCollector { + tcx, + impl_def_id, + def_ids_to_impls: &mut def_ids_to_impls, + }; + + visitor.visit(tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip()); + if of_trait { + visitor.visit_trait( + tcx.impl_trait_ref(impl_def_id).instantiate_identity().skip_norm_wip(), + ); + } + } + + def_ids_to_impls + } +} + +impl<'tcx, 'a> DefIdVisitor<'tcx> for DefIdsToImplsCollector<'tcx, 'a> { + const SHALLOW: bool = true; + fn skip_assoc_tys(&self) -> bool { + true + } + fn tcx(&self) -> TyCtxt<'tcx> { + self.tcx + } + fn visit_def_id(&mut self, def_id: DefId, _kind: &str, _descr: &dyn fmt::Display) { + if let Some(def_id) = def_id.as_local() { + debug_assert_matches!( + self.tcx.def_kind(def_id), + DefKind::Enum + | DefKind::Union + | DefKind::Struct + | DefKind::ForeignTy + | DefKind::Trait + ); + self.def_ids_to_impls.entry(def_id).or_default().insert(self.impl_def_id); + } + } +} + /// The embargo visitor, used to determine the exports of the AST. struct EmbargoVisitor<'tcx> { tcx: TyCtxt<'tcx>, /// Effective visibilities for reachable nodes. effective_visibilities: EffectiveVisibilities, - /// Has something changed in the level map? - changed: bool, + /// Queue with modified items. + queue: IndexSet, + /// Correspondence between def and impls containing this def. + def_ids_to_impls: DefIdsToImpls, } struct ReachEverythingInTheInterfaceVisitor<'a, 'tcx> { @@ -452,12 +515,12 @@ impl<'tcx> EmbargoVisitor<'tcx> { inherited_effective_vis: EffectiveVisibility, max_vis: Option, level: Level, - ) { + ) -> bool { // FIXME(typed_def_id): Make `Visibility::Restricted` use a `LocalModDefId` by default. let private_vis = ty::Visibility::Restricted(self.tcx.parent_module_from_def_id(def_id).into()); if max_vis != Some(private_vis) { - self.changed |= self.effective_visibilities.update( + return self.effective_visibilities.update( def_id, max_vis, private_vis, @@ -466,6 +529,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.tcx, ); } + false } fn reach( @@ -509,11 +573,12 @@ impl<'tcx> EmbargoVisitor<'tcx> { } } - fn check_def_id(&mut self, owner_id: OwnerId) { + fn check_def_id(&mut self, def_id: LocalDefId) { // Update levels of nested things and mark all items // in interfaces of reachable items as reachable. - let item_ev = self.get(owner_id.def_id); - match self.tcx.def_kind(owner_id) { + let item_ev = self.get(def_id); + let def_kind = self.tcx.def_kind(def_id); + match def_kind { // The interface is empty, and no nested items. DefKind::Use | DefKind::ExternCrate | DefKind::GlobalAsm => {} // The interface is empty, and all nested items are processed by `check_def_id`. @@ -526,14 +591,14 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::Fn | DefKind::TyAlias => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates().ty(); + self.reach(def_id, item_ev).generics().predicates().ty(); } } DefKind::Trait => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); - for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { + for assoc_item in self.tcx.associated_items(def_id).in_definition_order() { let def_id = assoc_item.def_id.expect_local(); self.update(def_id, item_ev, Level::Reachable); @@ -543,7 +608,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { } DefKind::TraitAlias => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); } } DefKind::Impl { of_trait } => { @@ -558,23 +623,23 @@ impl<'tcx> EmbargoVisitor<'tcx> { // without knowing both "shallow" version of its self type and "shallow" version of // its trait if it exists (which require reaching the `DefId`s in them). let item_ev = EffectiveVisibility::of_impl::( - owner_id.def_id, + def_id, of_trait, self.tcx, &self.effective_visibilities, ); - self.update_eff_vis(owner_id.def_id, item_ev, None, Level::Direct); + self.update_eff_vis(def_id, item_ev, None, Level::Direct); { - let mut reach = self.reach(owner_id.def_id, item_ev); + let mut reach = self.reach(def_id, item_ev); reach.generics().predicates().ty(); if of_trait { reach.trait_ref(); } } - for assoc_item in self.tcx.associated_items(owner_id).in_definition_order() { + for assoc_item in self.tcx.associated_items(def_id).in_definition_order() { let def_id = assoc_item.def_id.expect_local(); let max_vis = if of_trait { None } else { Some(self.tcx.local_visibility(def_id)) }; @@ -587,9 +652,9 @@ impl<'tcx> EmbargoVisitor<'tcx> { } DefKind::Enum => { if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); } - let def = self.tcx.adt_def(owner_id); + let def = self.tcx.adt_def(def_id); for variant in def.variants() { if let Some(item_ev) = item_ev { self.update(variant.def_id.expect_local(), item_ev, Level::Reachable); @@ -607,19 +672,19 @@ impl<'tcx> EmbargoVisitor<'tcx> { } // Corner case: if the variant is reachable, but its // enum is not, make the enum reachable as well. - self.reach(owner_id.def_id, variant_ev).ty(); + self.reach(def_id, variant_ev).ty(); } if let Some(ctor_def_id) = variant.ctor_def_id() { if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) { - self.reach(owner_id.def_id, ctor_ev).ty(); + self.reach(def_id, ctor_ev).ty(); } } } } DefKind::Struct | DefKind::Union => { - let def = self.tcx.adt_def(owner_id).non_enum_variant(); + let def = self.tcx.adt_def(def_id).non_enum_variant(); if let Some(item_ev) = item_ev { - self.reach(owner_id.def_id, item_ev).generics().predicates(); + self.reach(def_id, item_ev).generics().predicates(); for field in &def.fields { let field = field.did.expect_local(); self.update(field, item_ev, Level::Reachable); @@ -633,7 +698,7 @@ impl<'tcx> EmbargoVisitor<'tcx> { self.update(ctor_def_id.expect_local(), item_ev, Level::Reachable); } if let Some(ctor_ev) = self.get(ctor_def_id.expect_local()) { - self.reach(owner_id.def_id, ctor_ev).ty(); + self.reach(def_id, ctor_ev).ty(); } } } @@ -653,7 +718,10 @@ impl<'tcx> EmbargoVisitor<'tcx> { | DefKind::ConstParam | DefKind::LifetimeParam | DefKind::Ctor(..) => { - bug!("should be checked while checking parent") + span_bug!( + self.tcx.def_span(def_id), + "{def_kind:?} should be checked while checking parent" + ) } } } @@ -695,6 +763,76 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> { ); self } + + // If a def encountered in the interface is updated, we put those items + // that may be affected by this update into the queue. + fn enqueue_def_id(&mut self, def_id: LocalDefId) { + let def_kind = self.ev.tcx.def_kind(def_id); + match def_kind { + DefKind::Enum + | DefKind::Union + | DefKind::Struct + | DefKind::ForeignTy + | DefKind::Trait => { + self.ev.queue.insert(def_id); + // Make sure that all affected impls are traversed one more time. + if let Some(impls) = self.ev.def_ids_to_impls.get(&def_id) { + // The order in which items are traversed is irrelevant. + #[allow(rustc::potential_query_instability)] + self.ev.queue.extend(impls); + } + } + + DefKind::TraitAlias | DefKind::Fn => { + self.ev.queue.insert(def_id); + } + + DefKind::AssocConst { .. } | DefKind::AssocFn | DefKind::AssocTy => { + // FIXME: `EmbargoVisitor` can't check assoc items(see `check_def_id`). + // Let's traverse the whole impl/trait. + self.ev.queue.insert(self.ev.tcx.local_parent(def_id)); + } + + DefKind::Ctor(ctor_of, _) => { + let update_id = match ctor_of { + CtorOf::Struct => self.ev.tcx.local_parent(def_id), + CtorOf::Variant => self.ev.tcx.local_parent(self.ev.tcx.local_parent(def_id)), + }; + // Update the whole ADT. + self.ev.queue.insert(update_id); + } + + // Can be reached via RPIT (impl Fn), but can't affect + // the effective visibility of other defs. + DefKind::Closure => {} + + // Can't be reached + DefKind::Impl { .. } + | DefKind::TyAlias + | DefKind::Field + | DefKind::Variant + | DefKind::Static { .. } + | DefKind::Macro(_) + | DefKind::TyParam + | DefKind::AnonConst + | DefKind::InlineConst + | DefKind::OpaqueTy + | DefKind::SyntheticCoroutineBody + | DefKind::ConstParam + | DefKind::LifetimeParam + | DefKind::Mod + | DefKind::Use + | DefKind::ExternCrate + | DefKind::GlobalAsm + | DefKind::ForeignMod + | DefKind::Const { .. } => { + span_bug!( + self.tcx().def_span(def_id), + "{def_kind:?} unexpectedly reached by `ReachEverythingInTheInterfaceVisitor`" + ) + } + } + } } impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> { @@ -708,7 +846,9 @@ impl<'tcx> DefIdVisitor<'tcx> for ReachEverythingInTheInterfaceVisitor<'_, 'tcx> // produce type privacy errors on any use, so we don't consider it leaked. let max_vis = (self.level != Level::ReachableThroughImplTrait) .then(|| self.ev.tcx.local_visibility(def_id)); - self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level); + if self.ev.update_eff_vis(def_id, self.effective_vis, max_vis, self.level) { + self.enqueue_def_id(def_id); + } } } } @@ -1634,12 +1774,15 @@ fn check_mod_privacy(tcx: TyCtxt<'_>, module_def_id: LocalModDefId) { } fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { + let def_ids_to_impls = DefIdsToImplsCollector::collect(tcx); + // Build up a set of all exported items in the AST. This is a set of all // items which are reachable from external crates based on visibility. let mut visitor = EmbargoVisitor { tcx, effective_visibilities: tcx.resolutions(()).effective_visibilities.clone(), - changed: false, + queue: Default::default(), + def_ids_to_impls, }; visitor.effective_visibilities.check_invariants(tcx); @@ -1692,7 +1835,7 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { } } - visitor.changed = false; + visitor.queue.clear(); } // FIXME: remove this once proper support for defs reachability from macros is implemented. @@ -1716,18 +1859,14 @@ fn effective_visibilities(tcx: TyCtxt<'_>, (): ()) -> &EffectiveVisibilities { } let crate_items = tcx.hir_crate_items(()); - loop { - for id in crate_items.free_items() { - visitor.check_def_id(id.owner_id); - } - for id in crate_items.foreign_items() { - visitor.check_def_id(id.owner_id); - } - if visitor.changed { - visitor.changed = false; - } else { - break; - } + for id in crate_items.free_items() { + visitor.check_def_id(id.owner_id.def_id); + } + for id in crate_items.foreign_items() { + visitor.check_def_id(id.owner_id.def_id); + } + while let Some(def_id) = visitor.queue.pop() { + visitor.check_def_id(def_id); } visitor.effective_visibilities.check_invariants(tcx); From c454d92f618df7e27f11bea4c38eff7d52b45f33 Mon Sep 17 00:00:00 2001 From: Abdul Rafey Ahmed Date: Tue, 19 May 2026 02:13:40 +0530 Subject: [PATCH 061/174] fix: improve turbofish jump-to-def handling Handle turbofish syntax correctly in rustdoc jump-to-def links and add regression tests covering type aliases. --- src/librustdoc/html/highlight.rs | 4 ++-- tests/rustdoc-html/jump-to-def/turbofish.rs | 17 +++++++++++++++++ 2 files changed, 19 insertions(+), 2 deletions(-) create mode 100644 tests/rustdoc-html/jump-to-def/turbofish.rs diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 1c162a79c4c44..bf0f70f1a00be 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -1001,8 +1001,8 @@ impl<'src> Classifier<'src> { has_ident = true; nb_items += 1; } else if nb > 0 && has_ident { - // Following `;` will be handled on its own. - break Some(nb_items - 1); + // Drop all the colons we just peeked (e.g. `Option::` → keep `Option`). + break Some(nb_items - nb); } else if has_ident { break Some(nb_items); } else { diff --git a/tests/rustdoc-html/jump-to-def/turbofish.rs b/tests/rustdoc-html/jump-to-def/turbofish.rs new file mode 100644 index 0000000000000..1ad7a112a4343 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/turbofish.rs @@ -0,0 +1,17 @@ +// This test ensures that turbofish (`::<...>`) does not prevent jump-to-definition +// links from being generated. + +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/turbofish.rs.html' +use std::marker::PhantomData; + +pub fn foo() { + // `PhantomData::` — `PhantomData` must be linked despite the turbofish. + type TheOne = PhantomData<()>; + + //@ has - '//a[@href="#13"]' 'TheOne' + let _: TheOne:: = PhantomData; +} From 7ff768034cc2f4d2adaadc155429c3b01c742d15 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 13:43:13 +0000 Subject: [PATCH 062/174] intrinsic-test: run rustfmt on generated sources This allows for simplification of the intrinsic-test generator as it no longer needs to maintain readable formatting. --- library/stdarch/.github/workflows/main.yml | 1 + .../intrinsic-test/src/common/gen_rust.rs | 18 ++++++++++++++++++ .../crates/intrinsic-test/src/common/mod.rs | 10 +++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 5c84e856d54bb..aabe44a1c5721 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -297,6 +297,7 @@ jobs: rustup component add rust-src echo "CARGO_UNSTABLE_BUILD_STD=std" >> $GITHUB_ENV if: ${{ matrix.build_std }} + - run: rustup component add rustfmt # Configure some env vars based on matrix configuration - run: echo "PROFILE=${{ matrix.profile }}" >> $GITHUB_ENV diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index d23710451d478..78777313a1494 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -1,3 +1,5 @@ +use std::process::Command; + use itertools::Itertools; use super::indentation::Indentation; @@ -36,6 +38,22 @@ macro_rules! concatln { }; } +/// Run rustfmt on the generated source code +pub fn run_rustfmt(source_path: &str) { + let output = Command::new("rustfmt") + .args([source_path]) + .output() + .expect("failed to run rustfmt on generated sources"); + + if !output.status.success() { + panic!( + "failed to run rustfmt on generated sources:\nstdout:{stdout}\nstderr:{stderr}", + stdout = String::from_utf8_lossy(&output.stdout), + stderr = String::from_utf8_lossy(&output.stderr) + ); + } +} + pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 86849f7db34e0..74d68838a4f35 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -6,7 +6,9 @@ use cli::ProcessedCli; use crate::common::{ gen_c::write_wrapper_c, - gen_rust::{write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs}, + gen_rust::{ + run_rustfmt, write_bin_cargo_toml, write_build_rs, write_lib_cargo_toml, write_lib_rs, + }, intrinsic::Intrinsic, intrinsic_helpers::IntrinsicTypeDefinition, }; @@ -74,7 +76,7 @@ pub trait SupportedArchitectureTest { let rust_filename = format!("rust_programs/mod_{i}/src/lib.rs"); trace!("generating `{rust_filename}`"); - let mut file = File::create(rust_filename)?; + let mut file = File::create(&rust_filename)?; write_lib_rs( &mut file, @@ -84,6 +86,7 @@ pub trait SupportedArchitectureTest { i, chunk, )?; + run_rustfmt(&rust_filename); let toml_filename = format!("rust_programs/mod_{i}/Cargo.toml"); trace!("generating `{toml_filename}`"); @@ -93,9 +96,10 @@ pub trait SupportedArchitectureTest { let build_rs_filename = format!("rust_programs/mod_{i}/build.rs"); trace!("generating `{build_rs_filename}`"); - let mut file = File::create(build_rs_filename).unwrap(); + let mut file = File::create(&build_rs_filename).unwrap(); write_build_rs(&mut file, i, &arch_flags).unwrap(); + run_rustfmt(&build_rs_filename); Ok(()) }) From 526ff834633540081e4ba1a5eea183521c1b4d9e Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 May 2026 10:24:00 +0000 Subject: [PATCH 063/174] intrinsic-test: remove all indentation logic This isn't necessary when rustfmt is being used to make sure the generated sources are formatted. --- .../intrinsic-test/src/common/argument.rs | 12 ++++----- .../intrinsic-test/src/common/gen_rust.rs | 10 +++---- .../intrinsic-test/src/common/indentation.rs | 26 ------------------- .../src/common/intrinsic_helpers.rs | 22 +++++++--------- .../crates/intrinsic-test/src/common/mod.rs | 1 - 5 files changed, 17 insertions(+), 54 deletions(-) delete mode 100644 library/stdarch/crates/intrinsic-test/src/common/indentation.rs diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 885d5e998ef54..7a08a01246b81 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -4,7 +4,6 @@ use crate::common::intrinsic_helpers::TypeKind; use super::constraint::Constraint; use super::gen_rust::PASSES; -use super::indentation::Indentation; use super::intrinsic_helpers::IntrinsicTypeDefinition; /// An argument for the intrinsic. @@ -149,37 +148,36 @@ where pub fn gen_arg_rust( arg: &Argument, w: &mut impl std::io::Write, - indentation: Indentation, loads: u32, ) -> std::io::Result<()> { writeln!( w, - "{indentation}static {name}: [{ty}; {load_size}] = {values};\n", + "static {name}: [{ty}; {load_size}] = {values};\n", name = arg.rust_vals_array_name(), ty = arg.ty.rust_scalar_type(), load_size = arg.ty.num_lanes() * arg.ty.num_vectors() + loads - 1, - values = arg.ty.populate_random(indentation, loads) + values = arg.ty.populate_random(loads) ) } /// Creates a line for each argument that initializes the argument from array `[ARG]_VALS` at /// an offset `i` using a load intrinsic, in Rust. /// e.g `let a = vld1_u8(A_VALS.as_ptr().offset(i));` - pub fn load_values_rust(&self, indentation: Indentation) -> String { + pub fn load_values_rust(&self) -> String { self.iter() .filter(|&arg| !arg.has_constraint()) .enumerate() .map(|(idx, arg)| { if arg.is_simd() { format!( - "{indentation}let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", + "let {name} = {load}({vals_name}.as_ptr().add((i+{idx}) % {PASSES}) as _);\n", name = arg.generate_name(), vals_name = arg.rust_vals_array_name(), load = arg.ty.get_load_function(), ) } else { format!( - "{indentation}let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", + "let {name} = {vals_name}[(i+{idx}) % {PASSES}];\n", name = arg.generate_name(), vals_name = arg.rust_vals_array_name(), ) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 78777313a1494..15d7f0bec25a3 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -2,7 +2,6 @@ use std::process::Command; use itertools::Itertools; -use super::indentation::Indentation; use super::intrinsic_helpers::IntrinsicTypeDefinition; use crate::common::argument::ArgumentList; use crate::common::intrinsic::Intrinsic; @@ -124,7 +123,7 @@ pub fn write_lib_rs( let name = arg.rust_vals_array_name().to_string(); if seen.insert(name) { - ArgumentList::gen_arg_rust(arg, w, Indentation::default(), PASSES)?; + ArgumentList::gen_arg_rust(arg, w, PASSES)?; } } } @@ -223,9 +222,7 @@ fn generate_rust_test_loop( " }}", " }}", ), - loaded_args = intrinsic - .arguments - .load_values_rust(Indentation::default().nest_by(4)), + loaded_args = intrinsic.arguments.load_values_rust(), rust_args = intrinsic.arguments.as_call_param_rust(), c_args = intrinsic.arguments.as_c_call_param_rust(), passes = passes, @@ -305,9 +302,8 @@ pub fn write_build_rs( i = i )?; - let indentation = Indentation::default().nest_by(2); for flag in COMMON_FLAGS.iter().chain(arch_flags) { - writeln!(w, "{indentation}\"{flag}\",")?; + writeln!(w, "\"{flag}\",")?; } write!( diff --git a/library/stdarch/crates/intrinsic-test/src/common/indentation.rs b/library/stdarch/crates/intrinsic-test/src/common/indentation.rs deleted file mode 100644 index 9c2cc886e6544..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/common/indentation.rs +++ /dev/null @@ -1,26 +0,0 @@ -//! Basic code formatting tools. -//! -//! We don't need perfect formatting for the generated tests, but simple indentation can make -//! debugging a lot easier. - -#[derive(Copy, Clone, Debug, Default)] -pub struct Indentation(u32); - -impl Indentation { - pub fn nested(self) -> Self { - Self(self.0 + 1) - } - - pub fn nest_by(&self, additional_levels: u32) -> Self { - Self(self.0 + additional_levels) - } -} - -impl std::fmt::Display for Indentation { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - for _ in 0..self.0 { - write!(f, " ")?; - } - Ok(()) - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index ab4a565200bc8..9d09eff645385 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -5,7 +5,6 @@ use std::str::FromStr; use itertools::Itertools as _; -use super::indentation::Indentation; use super::values::value_for_array; #[derive(Debug, PartialEq, Copy, Clone)] @@ -154,7 +153,7 @@ impl IntrinsicType { self.ptr } - pub fn populate_random(&self, indentation: Indentation, loads: u32) -> String { + pub fn populate_random(&self, loads: u32) -> String { match self { IntrinsicType { bit_len: Some(bit_len @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 32 | 64)), @@ -164,9 +163,8 @@ impl IntrinsicType { vec_len, .. } => { - let body_indentation = indentation.nested(); format!( - "[\n{body}\n{indentation}]", + "[\n{body}\n]", body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) .format_with(",\n", |i, fmt| { let src = value_for_array(*bit_len, i); @@ -177,9 +175,9 @@ impl IntrinsicType { let mask = !0u64 >> (64 - *bit_len); let ones_compl = src ^ mask; let twos_compl = ones_compl + 1; - fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) + fmt(&format_args!("-{twos_compl:#x}")) } else { - fmt(&format_args!("{body_indentation}{src:#x}")) + fmt(&format_args!("{src:#x}")) } }) ) @@ -192,11 +190,10 @@ impl IntrinsicType { .. } => { format!( - "[\n{body}\n{indentation}]", + "[\n{body}\n]", body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) .format_with(",\n", |i, fmt| fmt(&format_args!( - "{indentation}f{bit_len}::from_bits({src:#x})", - indentation = indentation.nested(), + "f{bit_len}::from_bits({src:#x})", src = value_for_array(*bit_len, i) ))) ) @@ -208,10 +205,9 @@ impl IntrinsicType { vec_len, .. } => { - let body_indentation = indentation.nested(); let effective_bit_len = 32; format!( - "[\n{body}\n{indentation}]", + "[\n{body}\n]", body = (0..(vec_len.unwrap_or(1) * simd_len.unwrap_or(1) + loads - 1)) .format_with(",\n", |i, fmt| { let src = value_for_array(effective_bit_len, i); @@ -221,9 +217,9 @@ impl IntrinsicType { let mask = !0u64 >> (64 - effective_bit_len); let ones_compl = src ^ mask; let twos_compl = ones_compl + 1; - fmt(&format_args!("{body_indentation}-{twos_compl:#x}")) + fmt(&format_args!("-{twos_compl:#x}")) } else { - fmt(&format_args!("{body_indentation}{src:#x}")) + fmt(&format_args!("{src:#x}")) } }) ) diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 74d68838a4f35..86269bab338df 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -21,7 +21,6 @@ pub mod intrinsic_helpers; mod gen_c; mod gen_rust; -mod indentation; mod values; /// Architectures must support this trait From 18311c672b92df0b837e4c4a2e36b453a4b251bc Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 19 May 2026 14:26:53 +0200 Subject: [PATCH 064/174] fix running miri test when the working directory is not writable --- src/tools/miri/tests/pass-dep/libc/libc-fs.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs index 15b4f93446a43..9cc8685827077 100644 --- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs +++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs @@ -2,7 +2,7 @@ //@compile-flags: -Zmiri-disable-isolation use std::ffi::{CStr, CString, OsString}; -use std::fs::{File, canonicalize, remove_file}; +use std::fs::{File, canonicalize, create_dir, remove_dir, remove_file}; use std::io::{Error, ErrorKind, Write}; use std::os::unix::ffi::OsStrExt; use std::os::unix::io::AsRawFd; @@ -359,10 +359,17 @@ fn test_o_tmpfile_flag() { } fn test_posix_mkstemp() { + use std::env; use std::ffi::OsStr; use std::os::unix::io::FromRawFd; use std::path::Path; + // We want to test `mkstemp` on a relative name, so we cd to a tempdir and later cd back. + let old_cwd = env::current_dir().unwrap(); + let dir_path = utils::prepare_dir("miri_test_libc_readdir"); + create_dir(&dir_path).expect("create_dir failed"); + env::set_current_dir(&dir_path).unwrap(); + let valid_template = "fooXXXXXX"; // C needs to own this as `mkstemp(3)` says: // "Since it will be modified, `template` must not be a string constant, but @@ -370,9 +377,9 @@ fn test_posix_mkstemp() { // There seems to be no `as_mut_ptr` on `CString` so we need to use `into_raw`. let ptr = CString::new(valid_template).unwrap().into_raw(); let fd = unsafe { libc::mkstemp(ptr) }; + assert!(fd >= 0, "mkstemp failed"); // Take ownership back in Rust to not leak memory. let slice = unsafe { CString::from_raw(ptr) }; - assert!(fd > 0); let osstr = OsStr::from_bytes(slice.to_bytes()); let path: &Path = osstr.as_ref(); let name = path.to_string_lossy(); @@ -401,6 +408,8 @@ fn test_posix_mkstemp() { assert_eq!(e.raw_os_error(), Some(libc::EINVAL)); assert_eq!(e.kind(), std::io::ErrorKind::InvalidInput); } + + env::set_current_dir(old_cwd).unwrap(); } /// Test allocating variant of `realpath`. @@ -770,7 +779,6 @@ fn test_ioctl() { fn test_opendir_closedir() { // dir should exist - use std::fs::{create_dir, remove_dir}; let path = utils::prepare_dir("miri_test_libc_opendir_closedir"); create_dir(&path).expect("create_dir failed"); let cpath = CString::new(path.as_os_str().as_bytes()).expect("CString::new failed"); From 41859bdf5a4672e98b675a287f2958c77c5eafa3 Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Fri, 8 May 2026 09:06:13 +0800 Subject: [PATCH 065/174] loongarch: Use `intrinsics::simd` for vilv{h,l} --- .../src/loongarch64/lasx/generated.rs | 72 ------------------- .../src/loongarch64/lasx/portable.rs | 70 ++++++++++++++++++ .../src/loongarch64/lsx/generated.rs | 72 ------------------- .../core_arch/src/loongarch64/lsx/portable.rs | 56 +++++++++++++++ .../crates/stdarch-gen-loongarch/lasx.spec | 8 +++ .../crates/stdarch-gen-loongarch/lsx.spec | 8 +++ .../src/portable-intrinsics.txt | 16 +++++ 7 files changed, 158 insertions(+), 144 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index e05e19457319d..1fa156c85a422 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -155,22 +155,6 @@ unsafe extern "unadjusted" { fn __lasx_xvrepl128vei_w(a: __v8i32, b: u32) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvrepl128vei.d"] fn __lasx_xvrepl128vei_d(a: __v4i64, b: u32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvilvh.b"] - fn __lasx_xvilvh_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvilvh.h"] - fn __lasx_xvilvh_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvilvh.w"] - fn __lasx_xvilvh_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvilvh.d"] - fn __lasx_xvilvh_d(a: __v4i64, b: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvilvl.b"] - fn __lasx_xvilvl_b(a: __v32i8, b: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvilvl.h"] - fn __lasx_xvilvl_h(a: __v16i16, b: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvilvl.w"] - fn __lasx_xvilvl_w(a: __v8i32, b: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvilvl.d"] - fn __lasx_xvilvl_d(a: __v4i64, b: __v4i64) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvpackev.b"] fn __lasx_xvpackev_b(a: __v32i8, b: __v32i8) -> __v32i8; #[link_name = "llvm.loongarch.lasx.xvpackev.h"] @@ -1637,62 +1621,6 @@ pub fn lasx_xvrepl128vei_d(a: m256i) -> m256i { unsafe { transmute(__lasx_xvrepl128vei_d(transmute(a), IMM1)) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvh_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_b(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_h(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_w(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvilvl_d(a: m256i, b: m256i) -> m256i { - unsafe { transmute(__lasx_xvilvl_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs index 1d44f418bfbcd..0c583795af9b7 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs @@ -67,6 +67,68 @@ const unsafe fn simd_pickod_h(a: T, b: T) -> T { simd_shuffle!(b, a, [1, 3, 5, 7, 17, 19, 21, 23, 9, 11, 13, 15, 25, 27, 29, 31]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_b(a: T, b: T) -> T { + simd_shuffle!( + b, + a, + [ + 8, 40, 9, 41, 10, 42, 11, 43, 12, 44, 13, 45, 14, 46, 15, 47, + 24, 56, 25, 57, 26, 58, 27, 59, 28, 60, 29, 61, 30, 62, 31, 63 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [4, 20, 5, 21, 6, 22, 7, 23, 12, 28, 13, 29, 14, 30, 15, 31]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [2, 10, 3, 11, 6, 14, 7, 15]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [1, 5, 3, 7]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_b(a: T, b: T) -> T { + simd_shuffle!( + b, + a, + [ + 0, 32, 1, 33, 2, 34, 3, 35, 4, 36, 5, 37, 6, 38, 7, 39, + 16, 48, 17, 49, 18, 50, 19, 51, 20, 52, 21, 53, 22, 54, 23, 55 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 16, 1, 17, 2, 18, 3, 19, 8, 24, 9, 25, 10, 26, 11, 27]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 8, 1, 9, 4, 12, 5, 13]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 4, 2, 6]) +} + impl_vv!("lasx", lasx_xvpcnt_b, is::simd_ctpop, m256i, i8x32); impl_vv!("lasx", lasx_xvpcnt_h, is::simd_ctpop, m256i, i16x16); impl_vv!("lasx", lasx_xvpcnt_w, is::simd_ctpop, m256i, i32x8); @@ -230,6 +292,14 @@ impl_vvv!("lasx", lasx_xvpickod_b, simd_pickod_b, m256i, i8x32); impl_vvv!("lasx", lasx_xvpickod_h, simd_pickod_h, m256i, i16x16); impl_vvv!("lasx", lasx_xvpickod_w, simd_pickod_w, m256i, i32x8); impl_vvv!("lasx", lasx_xvpickod_d, simd_pickod_d, m256i, i64x4); +impl_vvv!("lasx", lasx_xvilvh_b, simd_ilvh_b, m256i, i8x32); +impl_vvv!("lasx", lasx_xvilvh_h, simd_ilvh_h, m256i, i16x16); +impl_vvv!("lasx", lasx_xvilvh_w, simd_ilvh_w, m256i, i32x8); +impl_vvv!("lasx", lasx_xvilvh_d, simd_ilvh_d, m256i, i64x4); +impl_vvv!("lasx", lasx_xvilvl_b, simd_ilvl_b, m256i, i8x32); +impl_vvv!("lasx", lasx_xvilvl_h, simd_ilvl_h, m256i, i16x16); +impl_vvv!("lasx", lasx_xvilvl_w, simd_ilvl_w, m256i, i32x8); +impl_vvv!("lasx", lasx_xvilvl_d, simd_ilvl_d, m256i, i64x4); impl_vuv!("lasx", lasx_xvslli_b, is::simd_shl, m256i, i8x32); impl_vuv!("lasx", lasx_xvslli_h, is::simd_shl, m256i, i16x16); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index 767be195292f2..bc08e0cea726e 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -163,22 +163,6 @@ unsafe extern "unadjusted" { fn __lsx_vreplvei_w(a: __v4i32, b: u32) -> __v4i32; #[link_name = "llvm.loongarch.lsx.vreplvei.d"] fn __lsx_vreplvei_d(a: __v2i64, b: u32) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vilvh.b"] - fn __lsx_vilvh_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vilvh.h"] - fn __lsx_vilvh_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vilvh.w"] - fn __lsx_vilvh_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vilvh.d"] - fn __lsx_vilvh_d(a: __v2i64, b: __v2i64) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vilvl.b"] - fn __lsx_vilvl_b(a: __v16i8, b: __v16i8) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vilvl.h"] - fn __lsx_vilvl_h(a: __v8i16, b: __v8i16) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vilvl.w"] - fn __lsx_vilvl_w(a: __v4i32, b: __v4i32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vilvl.d"] - fn __lsx_vilvl_d(a: __v2i64, b: __v2i64) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vpackev.b"] fn __lsx_vpackev_b(a: __v16i8, b: __v16i8) -> __v16i8; #[link_name = "llvm.loongarch.lsx.vpackev.h"] @@ -1577,62 +1561,6 @@ pub fn lsx_vreplvei_d(a: m128i) -> m128i { unsafe { transmute(__lsx_vreplvei_d(transmute(a), IMM1)) } } -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvh_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvh_d(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_b(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_b(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_h(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_h(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_w(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_w(transmute(a), transmute(b))) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vilvl_d(a: m128i, b: m128i) -> m128i { - unsafe { transmute(__lsx_vilvl_d(transmute(a), transmute(b))) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs index 24f9af851d8c1..99366dedb24c3 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs @@ -53,6 +53,54 @@ const unsafe fn simd_pickod_d(a: T, b: T) -> T { simd_shuffle!(b, a, [1, 3]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_b(a: T, b: T) -> T { + simd_shuffle!(b, a, [8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [4, 12, 5, 13, 6, 14, 7, 15]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [2, 6, 3, 7]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvh_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [1, 3]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_b(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_h(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 8, 1, 9, 2, 10, 3, 11]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_w(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 4, 1, 5]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { + simd_shuffle!(b, a, [0, 2]) +} + impl_vv!("lsx", lsx_vpcnt_b, is::simd_ctpop, m128i, i8x16); impl_vv!("lsx", lsx_vpcnt_h, is::simd_ctpop, m128i, i16x8); impl_vv!("lsx", lsx_vpcnt_w, is::simd_ctpop, m128i, i32x4); @@ -216,6 +264,14 @@ impl_vvv!("lsx", lsx_vpickod_b, simd_pickod_b, m128i, i8x16); impl_vvv!("lsx", lsx_vpickod_h, simd_pickod_h, m128i, i16x8); impl_vvv!("lsx", lsx_vpickod_w, simd_pickod_w, m128i, i32x4); impl_vvv!("lsx", lsx_vpickod_d, simd_pickod_d, m128i, i64x2); +impl_vvv!("lsx", lsx_vilvh_b, simd_ilvh_b, m128i, i8x16); +impl_vvv!("lsx", lsx_vilvh_h, simd_ilvh_h, m128i, i16x8); +impl_vvv!("lsx", lsx_vilvh_w, simd_ilvh_w, m128i, i32x4); +impl_vvv!("lsx", lsx_vilvh_d, simd_ilvh_d, m128i, i64x2); +impl_vvv!("lsx", lsx_vilvl_b, simd_ilvl_b, m128i, i8x16); +impl_vvv!("lsx", lsx_vilvl_h, simd_ilvl_h, m128i, i16x8); +impl_vvv!("lsx", lsx_vilvl_w, simd_ilvl_w, m128i, i32x4); +impl_vvv!("lsx", lsx_vilvl_d, simd_ilvl_d, m128i, i64x2); impl_vuv!("lsx", lsx_vslli_b, is::simd_shl, m128i, i8x16); impl_vuv!("lsx", lsx_vslli_h, is::simd_shl, m128i, i16x8); diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec index 867e071b62edc..5f85b1909db1f 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec @@ -1468,41 +1468,49 @@ asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvilvh_b +impl = portable name = lasx_xvilvh_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvilvh_h +impl = portable name = lasx_xvilvh_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvilvh_w +impl = portable name = lasx_xvilvh_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvilvh_d +impl = portable name = lasx_xvilvh_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI /// lasx_xvilvl_b +impl = portable name = lasx_xvilvl_b asm-fmts = xd, xj, xk data-types = V32QI, V32QI, V32QI /// lasx_xvilvl_h +impl = portable name = lasx_xvilvl_h asm-fmts = xd, xj, xk data-types = V16HI, V16HI, V16HI /// lasx_xvilvl_w +impl = portable name = lasx_xvilvl_w asm-fmts = xd, xj, xk data-types = V8SI, V8SI, V8SI /// lasx_xvilvl_d +impl = portable name = lasx_xvilvl_d asm-fmts = xd, xj, xk data-types = V4DI, V4DI, V4DI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec index b9df7bd96b9cb..6c554ac28d3a1 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec @@ -1488,41 +1488,49 @@ asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vilvh_b +impl = portable name = lsx_vilvh_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vilvh_h +impl = portable name = lsx_vilvh_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vilvh_w +impl = portable name = lsx_vilvh_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vilvh_d +impl = portable name = lsx_vilvh_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI /// lsx_vilvl_b +impl = portable name = lsx_vilvl_b asm-fmts = vd, vj, vk data-types = V16QI, V16QI, V16QI /// lsx_vilvl_h +impl = portable name = lsx_vilvl_h asm-fmts = vd, vj, vk data-types = V8HI, V8HI, V8HI /// lsx_vilvl_w +impl = portable name = lsx_vilvl_w asm-fmts = vd, vj, vk data-types = V4SI, V4SI, V4SI /// lsx_vilvl_d +impl = portable name = lsx_vilvl_d asm-fmts = vd, vj, vk data-types = V2DI, V2DI, V2DI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt index e07ac41f9c44e..d17d0833df77b 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt @@ -239,6 +239,14 @@ lsx_vpickod_b lsx_vpickod_h lsx_vpickod_w lsx_vpickod_d +lsx_vilvh_b +lsx_vilvh_h +lsx_vilvh_w +lsx_vilvh_d +lsx_vilvl_b +lsx_vilvl_h +lsx_vilvl_w +lsx_vilvl_d # LASX intrinsics lasx_xvsll_b @@ -475,3 +483,11 @@ lasx_xvpickod_b lasx_xvpickod_h lasx_xvpickod_w lasx_xvpickod_d +lasx_xvilvh_b +lasx_xvilvh_h +lasx_xvilvh_w +lasx_xvilvh_d +lasx_xvilvl_b +lasx_xvilvl_h +lasx_xvilvl_w +lasx_xvilvl_d From afedb8727e5ade1e789758028bedfc427ff737e9 Mon Sep 17 00:00:00 2001 From: abdul2801 Date: Tue, 19 May 2026 19:29:13 +0530 Subject: [PATCH 066/174] Update turbofish.rs --- tests/rustdoc-html/jump-to-def/turbofish.rs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/tests/rustdoc-html/jump-to-def/turbofish.rs b/tests/rustdoc-html/jump-to-def/turbofish.rs index 1ad7a112a4343..a2e8caa38bc9e 100644 --- a/tests/rustdoc-html/jump-to-def/turbofish.rs +++ b/tests/rustdoc-html/jump-to-def/turbofish.rs @@ -6,12 +6,10 @@ #![crate_name = "foo"] //@ has 'src/foo/turbofish.rs.html' -use std::marker::PhantomData; +use std::marker::PhantomData as TheOne; -pub fn foo() { - // `PhantomData::` — `PhantomData` must be linked despite the turbofish. - type TheOne = PhantomData<()>; - //@ has - '//a[@href="#13"]' 'TheOne' - let _: TheOne:: = PhantomData; +pub fn foo() { + //@ has - '//a[@href="{{channel}}/core/marker/struct.PhantomData.html"]' 'TheOne' + let _: TheOne::; } From 81e2d25b2ef827a460e9c0af48de282c7e3cfcb9 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Tue, 19 May 2026 16:27:56 +0200 Subject: [PATCH 067/174] Remove stale RTN FIXME for assoc item constraint fallback --- compiler/rustc_ast_lowering/src/lib.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs index 3da133f5e1136..a916ee1f143bd 100644 --- a/compiler/rustc_ast_lowering/src/lib.rs +++ b/compiler/rustc_ast_lowering/src/lib.rs @@ -1197,8 +1197,6 @@ impl<'hir> LoweringContext<'_, 'hir> { } } else { self.emit_bad_parenthesized_trait_in_assoc_ty(data); - // FIXME(return_type_notation): we could issue a feature error - // if the parens are empty and there's no return type. self.lower_angle_bracketed_parameter_data( &data.as_angle_bracketed_args(), ParamMode::Explicit, From fcb580ca5fdfc3d7dfbda221957e4f9d72d1e621 Mon Sep 17 00:00:00 2001 From: kcx1 Date: Mon, 18 May 2026 18:21:12 -0400 Subject: [PATCH 068/174] docs: remove duplicate "hermit" entry in OS constants list --- library/std/src/env.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index 047dfbe1a2333..e2b78887f73a9 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -1091,7 +1091,6 @@ pub mod consts { /// * `"uefi"` /// * `"fuchsia"` /// * `"haiku"` - /// * `"hermit"` /// * `"watchos"` /// * `"visionos"` /// * `"tvos"` From 16b12e39f3b6407c2134e61582f892c71b90cf8a Mon Sep 17 00:00:00 2001 From: kcx1 Date: Tue, 19 May 2026 16:47:43 -0400 Subject: [PATCH 069/174] tidy: alphabetize os constants list --- library/std/src/env.rs | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/library/std/src/env.rs b/library/std/src/env.rs index e2b78887f73a9..150ce9c1b9a96 100644 --- a/library/std/src/env.rs +++ b/library/std/src/env.rs @@ -1072,40 +1072,42 @@ pub mod consts { /// ///
Full list of possible values /// - /// * `"linux"` - /// * `"windows"` - /// * `"macos"` - /// * `"android"` - /// * `"ios"` - /// * `"openbsd"` - /// * `"freebsd"` - /// * `"netbsd"` - /// * `"wasi"` - /// * `"hermit"` + // tidy-alphabetical-start /// * `"aix"` + /// * `"android"` /// * `"apple"` /// * `"dragonfly"` /// * `"emscripten"` /// * `"espidf"` /// * `"fortanix"` - /// * `"uefi"` + /// * `"freebsd"` /// * `"fuchsia"` /// * `"haiku"` - /// * `"watchos"` - /// * `"visionos"` - /// * `"tvos"` + /// * `"hermit"` /// * `"horizon"` /// * `"hurd"` /// * `"illumos"` + /// * `"ios"` /// * `"l4re"` + /// * `"linux"` + /// * `"macos"` + /// * `"netbsd"` /// * `"nto"` + /// * `"openbsd"` /// * `"redox"` /// * `"solaris"` /// * `"solid_asp3"` + /// * `"tvos"` + /// * `"uefi"` /// * `"vexos"` + /// * `"visionos"` /// * `"vita"` /// * `"vxworks"` + /// * `"wasi"` + /// * `"watchos"` + /// * `"windows"` /// * `"xous"` + // tidy-alphabetical-end /// ///
#[stable(feature = "env", since = "1.0.0")] From de28a95577ab43e7635ca1b9edc40ccdb9fa46c7 Mon Sep 17 00:00:00 2001 From: Andrew Gallant Date: Tue, 19 May 2026 07:15:07 -0400 Subject: [PATCH 070/174] Add an example to the `NumBuffer` documentations We specifically call out that the sign is written for negative integers. This would also ideally be called out in `::format_into`, but the macro infrastructure for the implementation unfortunately doesn't really make this easy as far as I can tell. --- library/core/src/fmt/num_buffer.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/library/core/src/fmt/num_buffer.rs b/library/core/src/fmt/num_buffer.rs index 055c50833d94d..cc3680b8a42be 100644 --- a/library/core/src/fmt/num_buffer.rs +++ b/library/core/src/fmt/num_buffer.rs @@ -34,6 +34,22 @@ impl_NumBufferTrait! { /// A buffer wrapper of which the internal size is based on the maximum /// number of digits the associated integer can have. +/// +/// # Examples +/// +/// ``` +/// #![feature(int_format_into)] +/// use core::fmt::NumBuffer; +/// +/// let mut buf = NumBuffer::new(); +/// let n1 = 1972u32; +/// assert_eq!(n1.format_into(&mut buf), "1972"); +/// +/// // Formatting a negative integer includes the sign. +/// let mut buf = NumBuffer::new(); +/// let n2 = -1972i32; +/// assert_eq!(n2.format_into(&mut buf), "-1972"); +/// ``` #[unstable(feature = "int_format_into", issue = "138215")] pub struct NumBuffer { // FIXME: Once const generics feature is working, use `T::BUF_SIZE` instead of 40. From 6a59ea72374f638e556148ff90a80380b414d100 Mon Sep 17 00:00:00 2001 From: dianqk Date: Wed, 20 May 2026 02:06:44 +0800 Subject: [PATCH 071/174] Update LLVM to 22.1.6 --- .gitmodules | 2 +- src/llvm-project | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitmodules b/.gitmodules index c39fabac55f3b..9f9d573668245 100644 --- a/.gitmodules +++ b/.gitmodules @@ -25,7 +25,7 @@ [submodule "src/llvm-project"] path = src/llvm-project url = https://github.com/rust-lang/llvm-project.git - branch = rustc/22.1-2026-03-22 + branch = rustc/22.1-2026-05-19 shallow = true [submodule "src/doc/embedded-book"] path = src/doc/embedded-book diff --git a/src/llvm-project b/src/llvm-project index eaab4d9841b9a..08c84e69a84d9 160000 --- a/src/llvm-project +++ b/src/llvm-project @@ -1 +1 @@ -Subproject commit eaab4d9841b9a8a12783d927b2df2291c1c79269 +Subproject commit 08c84e69a84d95936296dfcab0e38b34100725d5 From b75c53288e7784abdada2705c272f297e850cbd1 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Wed, 20 May 2026 08:35:27 +0300 Subject: [PATCH 072/174] Visit delegation's qself under elided-infer lifetime rib too --- compiler/rustc_resolve/src/late.rs | 10 ++-- .../wrong-lifetime-rib-ice-156342.rs | 17 ------- .../wrong-lifetime-rib-ice-156342.stderr | 19 -------- tests/ui/delegation/wrong-lifetime-rib.rs | 32 +++++++++++++ tests/ui/delegation/wrong-lifetime-rib.stderr | 46 +++++++++++++++++++ 5 files changed, 83 insertions(+), 41 deletions(-) delete mode 100644 tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs delete mode 100644 tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr create mode 100644 tests/ui/delegation/wrong-lifetime-rib.rs create mode 100644 tests/ui/delegation/wrong-lifetime-rib.stderr diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 21936a0111c62..21b36f1934c3a 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3912,13 +3912,13 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { PathSource::Delegation, ); - if let Some(qself) = &delegation.qself { - self.visit_ty(&qself.ty); - } - // Create lifetimes not with `LifetimeRibKind::Generics` but with `LifetimeRibKind::Elided`, - // as we are not processing generic params but generic args in a future function call (#156342). + // as we are not processing generic params but generic args in a future call (#156342, #156758). self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + if let Some(qself) = &delegation.qself { + this.visit_ty(&qself.ty); + } + this.visit_path(&delegation.path); }); diff --git a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs b/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs deleted file mode 100644 index aad697c8c229e..0000000000000 --- a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.rs +++ /dev/null @@ -1,17 +0,0 @@ -#![feature(fn_delegation)] -#![feature(type_info)] - -use std::mem::type_info::Trait; - -impl Trait { -//~^ ERROR: cannot define inherent `impl` for a type outside of the crate where the type is defined - reuse None::<&()>; - //~^ ERROR: expected function, found unit variant `None` -} - -fn foo() {} - -reuse foo::<&&&&&&&&&&()> as foo1; -reuse foo::<&std::borrow::Cow<'_, &()>> as foo2; - -fn main() {} diff --git a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr b/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr deleted file mode 100644 index dbe4823ae0c97..0000000000000 --- a/tests/ui/delegation/wrong-lifetime-rib-ice-156342.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0423]: expected function, found unit variant `None` - --> $DIR/wrong-lifetime-rib-ice-156342.rs:8:11 - | -LL | reuse None::<&()>; - | ^^^^^^^^^^^ not a function - -error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined - --> $DIR/wrong-lifetime-rib-ice-156342.rs:6:1 - | -LL | impl Trait { - | ^^^^^^^^^^ impl for type defined outside of crate - | - = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it - = note: for more details about the orphan rules, see - -error: aborting due to 2 previous errors - -Some errors have detailed explanations: E0116, E0423. -For more information about an error, try `rustc --explain E0116`. diff --git a/tests/ui/delegation/wrong-lifetime-rib.rs b/tests/ui/delegation/wrong-lifetime-rib.rs new file mode 100644 index 0000000000000..14d62cbaa41fa --- /dev/null +++ b/tests/ui/delegation/wrong-lifetime-rib.rs @@ -0,0 +1,32 @@ +//@ edition:2024 + +#![feature(fn_delegation)] +#![feature(type_info)] + +mod ice_156342 { + use std::mem::type_info::Trait; + + impl Trait { + //~^ ERROR: cannot define inherent `impl` for a type outside of the crate where the type is defined + reuse None::<&()>; + //~^ ERROR: expected function, found unit variant `None` + } + + fn foo() {} + + reuse foo::<&&&&&&&&&&()> as foo1; + reuse foo::<&std::borrow::Cow<'_, &()>> as foo2; +} + +mod ice_156758 { + trait X {} + type Project = (); + type Ty = (); + + impl X { //~ ERROR: expected a type, found a trait + reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; + //~^ ERROR: ambiguous associated type + } +} + +fn main() {} diff --git a/tests/ui/delegation/wrong-lifetime-rib.stderr b/tests/ui/delegation/wrong-lifetime-rib.stderr new file mode 100644 index 0000000000000..2c9594af581ce --- /dev/null +++ b/tests/ui/delegation/wrong-lifetime-rib.stderr @@ -0,0 +1,46 @@ +error[E0423]: expected function, found unit variant `None` + --> $DIR/wrong-lifetime-rib.rs:11:15 + | +LL | reuse None::<&()>; + | ^^^^^^^^^^^ not a function + +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:26:10 + | +LL | impl X { + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | impl dyn X { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl X for /* Type */ { + | ++++++++++++++ + +error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined + --> $DIR/wrong-lifetime-rib.rs:9:5 + | +LL | impl Trait { + | ^^^^^^^^^^ impl for type defined outside of crate + | + = help: consider defining a trait and implementing it for the type or using a newtype wrapper like `struct MyType(ExternalType);` and implement it + = note: for more details about the orphan rules, see + +error[E0223]: ambiguous associated type + --> $DIR/wrong-lifetime-rib.rs:27:16 + | +LL | reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; + | ^^^^^^^^^^^^^^^^ + | +help: if there were a trait named `Example` with associated type `Ty` implemented for `&()`, you could use the fully-qualified path + | +LL - reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; +LL + reuse<<<&() as Example>::Ty> :: Ty as Iterator>::next; + | + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0116, E0223, E0423, E0782. +For more information about an error, try `rustc --explain E0116`. From 658e9fb052a8c1d4c4a940b9b83c8523714decca Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Wed, 20 May 2026 02:18:16 -0400 Subject: [PATCH 073/174] fix do_not_recommend span fallback for proc macros --- .../traits/fulfillment_errors.rs | 1 + .../auxiliary/proc_macro_repro.rs | 37 +++++++++++++++++++ .../proc-macro-span.current.stderr | 11 ++++++ .../proc-macro-span.next.stderr | 11 ++++++ .../do_not_recommend/proc-macro-span.rs | 16 ++++++++ 5 files changed, 76 insertions(+) create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr create mode 100644 tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 51a4186658c62..4d051a370c065 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -827,6 +827,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { // Keep more precise spans that still point within the parent obligation, // but do not let hidden impl details move the span outside of it. if code == *root_obligation.cause.code() + && root_obligation.cause.span.eq_ctxt(obligation.cause.span) && !root_obligation.cause.span.contains(obligation.cause.span) { obligation.cause.span = root_obligation.cause.span; diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs new file mode 100644 index 0000000000000..36fe0795c33a7 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/auxiliary/proc_macro_repro.rs @@ -0,0 +1,37 @@ +#![feature(proc_macro_quote)] + +extern crate proc_macro; + +use proc_macro::{TokenStream, TokenTree, quote}; + +#[proc_macro_attribute] +pub fn repro(_args: TokenStream, input: TokenStream) -> TokenStream { + // Parse input that looks like `fn f(arg: &mut Arg);` + let mut input = input.into_iter(); + assert_eq!(input.next().unwrap().to_string(), "fn"); + assert_eq!(input.next().unwrap().to_string(), "f"); + let TokenTree::Group(group) = input.next().unwrap() else { unreachable!() }; + let mut input = group.stream().into_iter(); + assert_eq!(input.next().unwrap().to_string(), "arg"); + assert_eq!(input.next().unwrap().to_string(), ":"); + let arg: TokenStream = input.collect(); + + // Emit output: + quote! { + const _: fn() = { + #[diagnostic::on_unimplemented( + message = "mutable reference to C++ type requires a pin -- use Pin<&mut Arg>", + label = "use `Pin<&mut Arg>`" + )] + trait ReferenceToUnpin_Arg { + fn check_unpin() {} + } + #[diagnostic::do_not_recommend] + impl< + 'a, + T: ?::core::marker::Sized + ::core::marker::Unpin, + > ReferenceToUnpin_Arg for &'a mut T {} + <$arg as ReferenceToUnpin_Arg>::check_unpin + }; + } +} diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr new file mode 100644 index 0000000000000..1236ac5dfe1c2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.current.stderr @@ -0,0 +1,11 @@ +error[E0277]: mutable reference to C++ type requires a pin -- use Pin<&mut Arg> + --> $DIR/proc-macro-span.rs:15:11 + | +LL | fn f(arg: &mut Arg); + | ^^^^^^^^ use `Pin<&mut Arg>` + | + = help: the trait `ReferenceToUnpin_Arg` is not implemented for `&mut Arg` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr new file mode 100644 index 0000000000000..1236ac5dfe1c2 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.next.stderr @@ -0,0 +1,11 @@ +error[E0277]: mutable reference to C++ type requires a pin -- use Pin<&mut Arg> + --> $DIR/proc-macro-span.rs:15:11 + | +LL | fn f(arg: &mut Arg); + | ^^^^^^^^ use `Pin<&mut Arg>` + | + = help: the trait `ReferenceToUnpin_Arg` is not implemented for `&mut Arg` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs new file mode 100644 index 0000000000000..3d133813f6e05 --- /dev/null +++ b/tests/ui/diagnostic_namespace/do_not_recommend/proc-macro-span.rs @@ -0,0 +1,16 @@ +//@ revisions: current next +//@ ignore-compare-mode-next-solver (explicit revisions) +//@ compile-flags: --crate-type=lib +//@[next] compile-flags: -Znext-solver +//@ edition: 2024 +//@ proc-macro: proc_macro_repro.rs + +// Regression test for https://github.com/rust-lang/rust/issues/156759. + +extern crate proc_macro_repro; + +struct Arg(std::marker::PhantomPinned); + +#[proc_macro_repro::repro] +fn f(arg: &mut Arg); +//~^ ERROR mutable reference to C++ type requires a pin From fe3cff925ad1b778fe80e1bec8e9142de2a75a77 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 7 May 2026 13:13:19 +0100 Subject: [PATCH 074/174] intrinsic-test: document arm intrinsic filtering Improve documentation for Arm intrinsic filtering - why we test some intrinsics and not others. --- library/stdarch/crates/intrinsic-test/src/arm/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 9bf6c95ffdcb3..80f5ae17d7dd7 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -46,15 +46,19 @@ impl SupportedArchitectureTest for ArmArchitectureTest { let intrinsics = intrinsics .into_iter() - // Not sure how we would compare intrinsic that returns void. + // Skip intrinsics that don't return a value. .filter(|i| i.results.kind() != TypeKind::Void) + // Skip bfloat intrinsics - not currently supported .filter(|i| i.results.kind() != TypeKind::BFloat) .filter(|i| !i.arguments.iter().any(|a| a.ty.kind() == TypeKind::BFloat)) // Skip pointers for now, we would probably need to look at the return // type to work out how many elements we need to point to. .filter(|i| !i.arguments.iter().any(|a| a.is_ptr())) + // Skip intrinsics with 128-bit elements (e.g. `p128`) .filter(|i| !i.arguments.iter().any(|a| a.ty.inner_size() == 128)) + // Skip intrinsics from `--skip` .filter(|i| !cli_options.skip.contains(&i.name)) + // Skip A64-specific intrinsics on A32 .filter(|i| !(a32 && i.arch_tags == vec!["A64".to_string()])) .take(sample_size) .collect::>(); From 1db61fea3b6c959e02273a509a3800a0249044f3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 11:07:23 +0000 Subject: [PATCH 075/174] intrinsic-test: document `--sample-percentage` This flag was missing documentation but the other flags had documentation --- library/stdarch/crates/intrinsic-test/src/common/cli.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/library/stdarch/crates/intrinsic-test/src/common/cli.rs b/library/stdarch/crates/intrinsic-test/src/common/cli.rs index f407b5ceb7d48..07f94eba18f33 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/cli.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/cli.rs @@ -19,6 +19,7 @@ pub struct Cli { #[arg(long)] pub target: String, + /// Percentage of intrinsics to test (used to limit testing to keep CI times manageable) #[arg(long, default_value_t = 100u8)] pub sample_percentage: u8, } From d009a44975bbe4e6995c7c478486da55784bc30d Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 12:54:38 +0000 Subject: [PATCH 076/174] intrinsic-test: document `iter_specializations` It isn't necessarily obvious what this function does --- .../crates/intrinsic-test/src/common/constraint.rs | 6 +++--- .../crates/intrinsic-test/src/common/intrinsic.rs | 11 +++++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs index 5984e0fcc22f9..c78eb3541cfdc 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs @@ -1,10 +1,10 @@ use serde::Deserialize; use std::ops::Range; -/// Describes the values to test for a const generic parameter. +/// Describes the values to test for a const generic parameter #[derive(Debug, PartialEq, Clone, Deserialize)] pub enum Constraint { - /// Test a single value. + /// Test a single value Equal(i64), /// Test a range of values, e.g. `0..16`. Range(Range), @@ -13,7 +13,7 @@ pub enum Constraint { } impl Constraint { - /// Iterate over the values of this constraint. + /// Returns an iterator over the values of this constraint pub fn iter<'a>(&'a self) -> impl Iterator + 'a { match self { Constraint::Equal(i) => std::slice::Iter::default().copied().chain(*i..*i + 1), diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs index 76e5959153d07..d69644388a830 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic.rs @@ -19,6 +19,10 @@ pub struct Intrinsic { pub arch_tags: Vec, } +/// Invokes `f` for each combination of the values in the constraint ranges. +/// +/// For example, given `constraints=[Equal(0), Range(1..2), Set([3, 4])]` and `imm_values=[]`, this +/// produces the four calls to `f`: `f([0, 1, 3])`, `f([0, 1, 4])`, `f([0, 2, 3])`, `f([0, 2, 4])`. fn recurse_specializations<'a, E>( constraints: &mut (impl Iterator + Clone), imm_values: &mut Vec, @@ -37,6 +41,13 @@ fn recurse_specializations<'a, E>( } impl Intrinsic { + /// Invokes `f` for "specialisation" of the intrinsic - a specific instantiation of the + /// constant generics of the intrinsic. `f` takes a slice where the `i`th element corresponds + /// to the value of the `i`th const generic argument of the intrinsic. + /// + /// For an intrinsic with three arguments with constraints `Equal(0)`, `Range(1..2)`, + /// `Set([3, 4])` respectively, this would produce four calls to `f`: `f(0, 1, 3)`, + /// `f(0, 1, 4)`, `f(0, 2, 3)`, `f(0, 2, 4)`. pub fn iter_specializations( &self, mut f: impl FnMut(&[i64]) -> Result<(), E>, From 938f85ac35b90c7ae0a62fb4d246f117a3173594 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 12:54:38 +0000 Subject: [PATCH 077/174] intrinsic-test: document `common::argument` module --- .../intrinsic-test/src/common/argument.rs | 69 +++++++++++++++++-- 1 file changed, 62 insertions(+), 7 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/argument.rs b/library/stdarch/crates/intrinsic-test/src/common/argument.rs index 7a08a01246b81..25207a8c458fd 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/argument.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/argument.rs @@ -52,7 +52,8 @@ where self.constraint.is_some() } - /// The name (e.g. "A_VALS" or "a_vals") for the array of possible test inputs. + /// Returns a string with the name of the static variable containing test values for intrinsic + /// arguments of this type. pub(crate) fn rust_vals_array_name(&self) -> impl std::fmt::Display { let loads = crate::common::gen_rust::PASSES; format!( @@ -62,12 +63,15 @@ where ) } + /// Should this argument be passed by reference in C wrapper function declarations? + /// + /// SIMD types and `f16` are currently passed by reference. pub(crate) fn pass_by_ref(&self) -> bool { - // pass SIMD types and `f16` by reference self.is_simd() || (self.ty.kind() == TypeKind::Float && self.ty.inner_size() == 16) } } +/// Arguments of an intrinsic - including parameters that end up being const generics. #[derive(Debug, PartialEq, Clone)] pub struct ArgumentList { pub args: Vec>, @@ -77,6 +81,11 @@ impl ArgumentList where T: IntrinsicTypeDefinition, { + /// Returns a string with the arguments in `self` as a parameter list for a wrapper fn + /// definition in C (e.g. `$ty1 $arg1, $ty2 $arg2`). + /// + /// Skips arguments with constraints - which correspond to arguments that must take immediates - + /// as a different C definition will be generated for each value of these being tested. pub fn as_non_imm_arglist_c(&self) -> String { self.iter() .filter(|arg| !arg.has_constraint()) @@ -90,6 +99,11 @@ where .to_string() } + /// Returns a string with the arguments in `self` as a parameter list for a Rust declaration of + /// a C wrapper fn (e.g. `$arg1: $ty1, $arg2: $ty2`). + /// + /// Skips arguments with constraints - which correspond to arguments that must take immediates - + /// as a different C definition will be generated for each value of these being tested. pub fn as_non_imm_arglist_rust(&self) -> String { self.iter() .filter(|arg| !arg.has_constraint()) @@ -107,6 +121,8 @@ where .to_string() } + /// Returns a string with the arguments in `self` being passed to an intrinsic call in C + /// (e.g. `$arg1, 2 /* imm_args[0] */, $arg3` where `$arg2` has a constraint). pub fn as_call_params_c(&self, imm_args: &[i64]) -> String { let mut imm_args = imm_args.iter(); self.iter() @@ -123,8 +139,9 @@ where .to_string() } - /// Converts the argument list into the call parameters for a Rust function. - /// e.g. this would generate something like `a, b, c` + /// Returns a string with the arguments in `self` being passed to an intrinsic call in Rust. + /// (e.g. `$arg1, $arg3` where `$arg2` has a constraint and so corresponds to a const generic + /// parameter). pub fn as_call_param_rust(&self) -> String { self.iter() .filter(|a| !a.has_constraint()) @@ -132,6 +149,9 @@ where .join(", ") } + /// Returns a string with the arguments in `self` being passed to the declaration of a C wrapper + /// fn from Rust (e.g. `$arg1, $arg3` (where `$arg2` has a constraint and so corresponds to a + /// const generic parameter). pub fn as_c_call_param_rust(&self) -> String { self.iter() .filter(|a| !a.has_constraint()) @@ -145,6 +165,26 @@ where .join("") } + /// Returns a string defining a static variable with test values used for all intrinsics with + /// arguments of `arg`'s type. + /// + /// e.g. + /// ```rust,ignore + /// static U8_20: [u8; 20] = [ + /// 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xf0, + /// 0x80, 0x3b, 0xff, + /// ]; + /// ``` + /// + /// `num_lanes * num_vectors + loads - 1` elements are present in the array, which is sufficient + /// for a `loads` number of `num_lanes * num_vectors` windows into the array to be loaded: + /// + /// ```text + /// [0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa, 0xb, 0xc, 0xd, 0xe, 0xf, 0xf0, 0x80, 0x3b, 0xff] + /// ^^^^^^^^^^^^^^^^^^^ first window of `num_lanes * num_vectors` elements (e.g. four elements) + /// ^^^^^^^^^^^^^^^^^^ second window + /// `loads`th window ^^^^^^^^^^^^^^^^^^^^^^ + /// ``` pub fn gen_arg_rust( arg: &Argument, w: &mut impl std::io::Write, @@ -160,9 +200,23 @@ where ) } - /// Creates a line for each argument that initializes the argument from array `[ARG]_VALS` at - /// an offset `i` using a load intrinsic, in Rust. - /// e.g `let a = vld1_u8(A_VALS.as_ptr().offset(i));` + /// Returns a string defining a local variable for each argument and loading a value into each + /// using a load intrinsic. + /// + /// e.g. + /// ```rust,ignore + /// let a = vld1_u8(I16_23.as_ptr().offset((i + 0 /* idx */) % 20 /* PASSES */)); + /// ```` + /// + /// The generator will have already generated arrays of appropriate length with values that can + /// be used for testing (see the `gen_args_rust` function). + /// + /// Each load is assumed to have a variable `i` in scope which comes from a loop which repeats + /// the testing of the intrinsic for different values - each subsequent `i` shifts the window + /// of values being loaded along the pre-prepared array. + /// + /// Each subsequent argument's first window is started one element further into the array + /// then the previous. pub fn load_values_rust(&self) -> String { self.iter() .filter(|&arg| !arg.has_constraint()) @@ -186,6 +240,7 @@ where .collect() } + /// Returns an iterator over the contained arguments pub fn iter(&self) -> std::slice::Iter<'_, Argument> { self.args.iter() } From ad2d7e97a82fcc1300fc3a5dbdff0aa99ac117cb Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 May 2026 20:20:15 +0000 Subject: [PATCH 078/174] intrinsic-test: document `common::values` --- .../crates/intrinsic-test/src/common/values.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/values.rs b/library/stdarch/crates/intrinsic-test/src/common/values.rs index 6c94ef2c22e1d..01dc0713f0f00 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/values.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/values.rs @@ -1,6 +1,12 @@ -/// Get a single value for an argument values array in a determistic way. -/// * `bits`: The number of bits for the type, only 8, 16, 32, 64 are valid values -/// * `index`: The position in the array we are generating for +/// Returns a bit pattern for a value being output into a array of test values. Bit patterns come +/// from one of many constant arrays of test values. The specific constant array used depends on +/// the number of bits - `bits` - of the type having test values generated for it. This function +/// is called repeatedly with incrementing values of `index` to produce an entire array of test +/// values. +/// +/// Each constant array of bit patterns should ideally be at least the length of the largest array +/// of test values that will be requested (e.g. 51 for a `poly8x8x4` when `PASSES=20`: +/// `(8 * 4) + 20 - 1`), otherwise values will be repeated. pub fn value_for_array(bits: u32, index: u32) -> u64 { let index = index as usize; match bits { From 78dd79cdb7cfd6c1dcb976d354e896811fcf5ac3 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 May 2026 20:20:15 +0000 Subject: [PATCH 079/174] intrinsic-test: document `common::intrinsic_helpers` --- .../src/common/intrinsic_helpers.rs | 54 ++++++++++++------- 1 file changed, 36 insertions(+), 18 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index 9d09eff645385..cb8740fa07001 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -68,7 +68,7 @@ impl fmt::Display for TypeKind { } impl TypeKind { - /// Gets the type part of a c typedef for a type that's in the form of {type}{size}_t. + /// Returns the type component of a C typedef for a type of the form of `{type}{size}_t` pub fn c_prefix(&self) -> &str { match self { Self::Float => "float", @@ -82,7 +82,7 @@ impl TypeKind { } } - /// Gets the rust prefix for the type kind i.e. i, u, f. + /// Returns the Rust prefix for this type kind i.e. `i`, `u`, or `f`. pub fn rust_prefix(&self) -> &str { match self { Self::BFloat => "bf", @@ -100,35 +100,43 @@ impl TypeKind { #[derive(Debug, PartialEq, Clone)] pub struct IntrinsicType { + /// Is this an immediate? pub constant: bool, - /// whether this object is a const pointer + /// Is this is a const pointer to the type? pub ptr_constant: bool, + /// Is this is a pointer to the type? pub ptr: bool, + /// Element type (e.g. `TypeKind::Int(Sign::Unsigned)` for `uint64x2_t`). pub kind: TypeKind, - /// The bit length of this type (e.g. 32 for u32). + + /// Number of bits of this type (e.g. 32 for `u32`). pub bit_len: Option, - /// Length of the SIMD vector (i.e. 4 for uint32x4_t), A value of `None` - /// means this is not a simd type. A `None` can be assumed to be 1, - /// although in some places a distinction is needed between `u64` and - /// `uint64x1_t` this signals that. + /// Length of a SIMD vector (i.e. 4 for `uint32x4_t`). + /// + /// A value of `None` means this is not a SIMD type. The number of lanes of a type with + /// `simd_len=None` can be assumed to be one, though it is important to maintain a distinction + /// between `simd_len=None` and `simd_len=Some(1)` so as to differentiate between `u64` and + /// `uint64x1_t`. pub simd_len: Option, - /// The number of rows for SIMD matrices (i.e. 2 for uint8x8x2_t). - /// A value of `None` represents a type that does not contain any - /// rows encoded in the type (e.g. uint8x8_t). - /// A value of `None` can be assumed to be 1 though. + /// Number of rows of a SIMD matrix (i.e. 2 for `uint8x8x2_t`). + /// + /// A value of `None` means this is not a SIMD matrix (e.g. `uint8x8_t`). The number of rows of + /// a type with `vec_len=None` can be assumed to be one. pub vec_len: Option, } impl IntrinsicType { + /// Returns the element type pub fn kind(&self) -> TypeKind { self.kind } + /// Returns the number of bits of the type (with a minimum of `8`) pub fn inner_size(&self) -> u32 { if let Some(bl) = self.bit_len { cmp::max(bl, 8) @@ -137,22 +145,34 @@ impl IntrinsicType { } } + /// Returns the number of lanes of the type pub fn num_lanes(&self) -> u32 { self.simd_len.unwrap_or(1) } + /// Returns the number of vectors of the type pub fn num_vectors(&self) -> u32 { self.vec_len.unwrap_or(1) } + /// Returns `true` if this represents a SIMD vector pub fn is_simd(&self) -> bool { self.simd_len.is_some() || self.vec_len.is_some() } + /// Returns `true` if this is a pointer pub fn is_ptr(&self) -> bool { self.ptr } + /// Returns the elements used in the test value arrays in `gen_arg_rust`. Uses the same + /// `num_lanes * num_vectors + loads - 1` arithmetic to produce the number of values that + /// `ArgumentList::gen_arg_rust` expects and `ArgumentList::load_values_rust` needs. + /// + /// Each value in the array starts as a bit pattern from `common::values::value_from_array` + /// which is then printed as a hex value in the generated code (and if identified as a negative + /// value, with the appropriate minus and corrected hex pattern). Calls to `fN::from_bits` are + /// generated for floats. pub fn populate_random(&self, loads: u32) -> String { match self { IntrinsicType { @@ -231,18 +251,16 @@ impl IntrinsicType { pub trait IntrinsicTypeDefinition: Deref { /// Determines the load function for this type. - /// can be implemented in an `impl` block fn get_load_function(&self) -> String; - /// Gets a string containing the typename for this type in C format. - /// can be directly defined in `impl` blocks + /// Gets a string containing the typename for this type in C. fn c_type(&self) -> String; - /// Gets a string containing the typename for this type in Rust format. - /// can be directly defined in `impl` blocks + /// Gets a string containing the typename for this type in Rust. fn rust_type(&self) -> String; - /// To enable architecture-specific logic + /// Gets a string containing the name of the scalar type corresponding to this type if it is a + /// vector. fn rust_scalar_type(&self) -> String { if self.is_simd() { format!( From 4c0577ac8daf01641c05501a4d1b0c5ce9e377a8 Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 May 2026 20:20:15 +0000 Subject: [PATCH 080/174] intrinsic-test: document `common::gen_c` --- .../stdarch/crates/intrinsic-test/src/common/gen_c.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs index bdf6f68d58cc2..24756324c48e4 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_c.rs @@ -4,6 +4,16 @@ use crate::common::intrinsic::Intrinsic; use super::intrinsic_helpers::IntrinsicTypeDefinition; +/// Generates a C source file containing wrapper functions around each specialisation of each +/// intrinsic (that is, intrinsics with specific values for the the immediate arguments). Each +/// wrapper function is invoked via FFI from the Rust binary doing the testing. +/// +/// e.g. +/// ```c +/// void __crc32cd_wrapper(uint32_t* __dst, uint32_t a, uint64_t b) { +/// *__dst = __crc32cd(a, b); +/// } +/// ``` pub fn write_wrapper_c( w: &mut impl std::io::Write, notice: &str, From dac92d0b49c67beb455930cbc65ef332865674ab Mon Sep 17 00:00:00 2001 From: David Wood Date: Tue, 19 May 2026 20:20:15 +0000 Subject: [PATCH 081/174] intrinsic-test: document `common::gen_rust` --- .../intrinsic-test/src/common/gen_rust.rs | 31 +++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 15d7f0bec25a3..039e78f5778bf 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -7,10 +7,13 @@ use crate::common::argument::ArgumentList; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; -// The number of times each intrinsic will be called. +// The number of times each intrinsic will be called - influences the generation of the +// test arrays to minimise repeated testing of the same test values. pub(crate) const PASSES: u32 = 20; -// we need a reflexive equality relation, so treat NaNs as equal +/// Rust definitions that are included verbatim in the generated source. In particular, defines +/// a wrapper around float types that defines `NaN`s to be equal reflexively to enable +/// comparison of results that use floats types. const COMMON_RUST_DEFINITIONS: &str = r#" macro_rules! wrap_partialeq { ($($wrapper:ident ($inner:ty)),*) => {$( @@ -53,6 +56,13 @@ pub fn run_rustfmt(source_path: &str) { } } +/// Writes a `Cargo.toml` containing a workspace with `module_count` members to `w`. +/// +/// e.g. +/// ```toml +/// [workspace] +/// members = [ "mod_0", "mod_1" ] +/// ``` pub fn write_bin_cargo_toml( w: &mut impl std::io::Write, module_count: usize, @@ -64,6 +74,8 @@ pub fn write_bin_cargo_toml( writeln!(w, "]") } +/// Writes a `Cargo.toml` for a crate with name `name` to `w` that will contain a single Rust source +/// file with a subset of the testing being generated. pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io::Result<()> { write!( w, @@ -90,6 +102,8 @@ pub fn write_lib_cargo_toml(w: &mut impl std::io::Write, name: &str) -> std::io: ) } +/// Writes a Rust source file into `w` with common definitions, static arrays with test values, +/// declarations of C wrapper functions for FFI and Rust test functions. pub fn write_lib_rs( w: &mut impl std::io::Write, notice: &str, @@ -138,6 +152,13 @@ pub fn write_lib_rs( Ok(()) } +/// Writes the body of an intrinsic test to `w` for `intrinsic`. +/// +/// Each specialisation of the intrinsic (i.e. specific instantiations of the immediate arguments +/// of the intrinsic) is added to an array of specialisations. Each specialisation is tested +/// (first loop) `PASSES` number of times (second loop). For a given iteration of a given +/// specialisation, test values are loaded for each argument and passed to the Rust intrinsic +/// and the C wrapper function, and the results are compared. fn generate_rust_test_loop( w: &mut impl std::io::Write, intrinsic: &Intrinsic, @@ -231,6 +252,8 @@ fn generate_rust_test_loop( ) } +/// Writes a test function for an given intrinsic to `w`, with a body generated by +/// `generate_rust_test_loop`. fn create_rust_test( w: &mut impl std::io::Write, intrinsic: &Intrinsic, @@ -250,6 +273,8 @@ fn create_rust_test( Ok(()) } +/// Writes an `extern "C"` block with function declarations for each of the C wrapper functions into +/// `w`. pub fn write_bindings_rust( w: &mut impl std::io::Write, i: usize, @@ -283,6 +308,8 @@ pub fn write_bindings_rust( writeln!(w, "}}") } +/// Writes a `build.rs` into `w` for each test crate that compiles the corresponding C source code +/// with wrapper functions. pub fn write_build_rs( w: &mut impl std::io::Write, i: usize, From d4e4f9a0eeef6d91d3097aec1e94bf237ce2d4a6 Mon Sep 17 00:00:00 2001 From: fallofpheonix Date: Wed, 20 May 2026 14:20:48 +0530 Subject: [PATCH 082/174] Use print instead of po in debuginfo path test --- tests/debuginfo/path.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/debuginfo/path.rs b/tests/debuginfo/path.rs index 27b518fd89751..547ab4d43b7cb 100644 --- a/tests/debuginfo/path.rs +++ b/tests/debuginfo/path.rs @@ -7,13 +7,9 @@ //@ lldb-command:run //@ lldb-command:print pathbuf -//@ lldb-check:[...] "/some/path" { inner = "/some/path" { inner = { inner = size=10 { [0] = '/' [1] = 's' [2] = 'o' [3] = 'm' [4] = 'e' [5] = '/' [6] = 'p' [7] = 'a' [8] = 't' [9] = 'h' } } } } -//@ lldb-command:po pathbuf -//@ lldb-check:"/some/path" +//@ lldb-check:[...] "/some/path" [...] //@ lldb-command:print path -//@ lldb-check:[...] "/some/path" { data_ptr = [...] length = 10 } -//@ lldb-command:po path -//@ lldb-check:"/some/path" +//@ lldb-check:[...] "/some/path" [...] use std::path::Path; From 173bad72c34f8d0ce750a3b0734f19d78f626362 Mon Sep 17 00:00:00 2001 From: 42triangles <42triangles@tutanota.com> Date: Tue, 19 May 2026 13:54:09 +0200 Subject: [PATCH 083/174] fix E0371 description --- .../rustc_error_codes/src/error_codes/E0371.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/compiler/rustc_error_codes/src/error_codes/E0371.md b/compiler/rustc_error_codes/src/error_codes/E0371.md index a44721346e20d..aacaee0c1b6e0 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0371.md +++ b/compiler/rustc_error_codes/src/error_codes/E0371.md @@ -7,10 +7,18 @@ trait Foo { fn foo(&self) { } } trait Bar: Foo { } trait Baz: Bar { } -impl Bar for Baz { } // error, `Baz` implements `Bar` by definition -impl Foo for Baz { } // error, `Baz` implements `Bar` which implements `Foo` -impl Baz for Baz { } // error, `Baz` (trivially) implements `Baz` -impl Baz for Bar { } // Note: This is OK +impl Bar for dyn Baz { } // error, `Baz` implements `Bar` by definition +impl Foo for dyn Baz { } // error, `Baz` implements `Bar` which implements `Foo` +impl Baz for dyn Baz { } // error, `Baz` (trivially) implements `Baz` +``` + +This is okay, because `Bar` does not implement `Baz` by definition: + +``` +# trait Foo { fn foo(&self) { } } +# trait Bar: Foo { } +# trait Baz: Bar { } +impl Baz for dyn Bar { } // Note: This is OK ``` When `Trait2` is a subtrait of `Trait1` (for example, when `Trait2` has a From 536b7fb266f7b19d934e84bfe205a9887b5f4fd9 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 7 May 2026 13:09:16 +0100 Subject: [PATCH 084/174] intrinsic-test: simplify `ArmIntrinsicType` `ArmIntrinsicType` does not need a `target` field and `parse_intrinsic_type` can be a free function that returns an `IntrinsicType` which can be wrapped in an `ArmIntrinsicType` by the caller. --- .../intrinsic-test/src/arm/intrinsic.rs | 9 +- .../intrinsic-test/src/arm/json_parser.rs | 19 +-- .../crates/intrinsic-test/src/arm/mod.rs | 4 +- .../crates/intrinsic-test/src/arm/types.rs | 130 ++++++++---------- 4 files changed, 75 insertions(+), 87 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs index 29343bee4c300..a54e5857192e0 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/intrinsic.rs @@ -2,21 +2,18 @@ use crate::common::intrinsic_helpers::IntrinsicType; use std::ops::{Deref, DerefMut}; #[derive(Debug, Clone, PartialEq)] -pub struct ArmIntrinsicType { - pub data: IntrinsicType, - pub target: String, -} +pub struct ArmIntrinsicType(pub IntrinsicType); impl Deref for ArmIntrinsicType { type Target = IntrinsicType; fn deref(&self) -> &Self::Target { - &self.data + &self.0 } } impl DerefMut for ArmIntrinsicType { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.data + &mut self.0 } } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index c1563a7364ce7..5a86abdc6d1a6 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -1,4 +1,5 @@ use super::intrinsic::ArmIntrinsicType; +use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; use crate::common::intrinsic::Intrinsic; @@ -58,7 +59,6 @@ struct JsonIntrinsic { pub fn get_neon_intrinsics( filename: &Path, - target: &str, ) -> Result>, Box> { let file = std::fs::File::open(filename)?; let reader = std::io::BufReader::new(file); @@ -68,7 +68,7 @@ pub fn get_neon_intrinsics( .into_iter() .filter_map(|intr| { if intr.simd_isa == "Neon" { - Some(json_to_intrinsic(intr, target).expect("Couldn't parse JSON")) + Some(json_to_intrinsic(intr).expect("Couldn't parse JSON")) } else { None } @@ -79,11 +79,10 @@ pub fn get_neon_intrinsics( fn json_to_intrinsic( mut intr: JsonIntrinsic, - target: &str, ) -> Result, Box> { let name = intr.name.replace(['[', ']'], ""); - let results = ArmIntrinsicType::from_c(&intr.return_type.value, target)?; + let result_ty = ArmIntrinsicType(parse_intrinsic_type(&intr.return_type.value)?); let args = intr .arguments @@ -95,16 +94,18 @@ fn json_to_intrinsic( let metadata = metadata.and_then(|a| a.remove(arg_name)); let arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let ty = ArmIntrinsicType::from_c(type_name, target) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let arg_ty = ArmIntrinsicType( + parse_intrinsic_type(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")), + ); let mut arg = - Argument::::new(i, String::from(arg_name), ty, constraint); + Argument::::new(i, String::from(arg_name), arg_ty, constraint); // The JSON doesn't list immediates as const let IntrinsicType { ref mut constant, .. - } = arg.ty.data; + } = *arg.ty; if arg.name.starts_with("imm") { *constant = true } @@ -117,7 +118,7 @@ fn json_to_intrinsic( Ok(Intrinsic { name, arguments, - results, + results: result_ty, arch_tags: intr.architectures, }) } diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 80f5ae17d7dd7..8935b3ca66f0a 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -35,8 +35,8 @@ impl SupportedArchitectureTest for ArmArchitectureTest { fn create(cli_options: ProcessedCli) -> Self { let a32 = cli_options.target.starts_with("armv7"); - let mut intrinsics = get_neon_intrinsics(&cli_options.filename, &cli_options.target) - .expect("Error parsing input file"); + let mut intrinsics = + get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); intrinsics.sort_by(|a, b| a.name.cmp(&b.name)); intrinsics.dedup(); diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index e9614eba218cb..40035b35d5544 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -42,7 +42,7 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { simd_len, vec_len, .. - } = &self.data + } = **self { let quad = if simd_len.unwrap_or(1) * bl > 64 { "q" @@ -69,79 +69,69 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { } } -impl ArmIntrinsicType { - pub fn from_c(s: &str, target: &str) -> Result { - const CONST_STR: &str = "const"; - if let Some(s) = s.strip_suffix('*') { - let (s, constant) = match s.trim().strip_suffix(CONST_STR) { - Some(stripped) => (stripped, true), - None => (s, false), +pub fn parse_intrinsic_type(s: &str) -> Result { + const CONST_STR: &str = "const"; + if let Some(s) = s.strip_suffix('*') { + let (s, constant) = match s.trim().strip_suffix(CONST_STR) { + Some(stripped) => (stripped, true), + None => (s, false), + }; + let s = s.trim_end(); + let mut ty = parse_intrinsic_type(s)?; + ty.ptr = true; + ty.ptr_constant = constant; + Ok(ty) + } else { + // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] + let (mut s, constant) = match s.strip_prefix(CONST_STR) { + Some(stripped) => (stripped.trim(), true), + None => (s, false), + }; + s = s.strip_suffix("_t").unwrap_or(s); + let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] + let start = parts.next().ok_or("Impossible to parse type")?; + if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { + let (arg_kind, bit_len) = start.split_at(digit_start); + let arg_kind = arg_kind.parse::()?; + let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; + let simd_len = match parts.next() { + Some(part) => Some( + part.parse::() + .map_err(|_| "Couldn't parse simd_len: {part}")?, + ), + None => None, }; - let s = s.trim_end(); - let temp_return = ArmIntrinsicType::from_c(s, target); - temp_return.map(|mut op| { - op.ptr = true; - op.ptr_constant = constant; - op + let vec_len = match parts.next() { + Some(part) => Some( + part.parse::() + .map_err(|_| "Couldn't parse vec_len: {part}")?, + ), + None => None, + }; + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind: arg_kind, + bit_len: Some(bit_len), + simd_len, + vec_len, }) } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - let (mut s, constant) = match s.strip_prefix(CONST_STR) { - Some(stripped) => (stripped.trim(), true), - None => (s, false), + let kind = start.parse::()?; + let bit_len = match kind { + TypeKind::Int(_) => Some(32), + _ => None, }; - s = s.strip_suffix("_t").unwrap_or(s); - let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] - let start = parts.next().ok_or("Impossible to parse type")?; - if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { - let (arg_kind, bit_len) = start.split_at(digit_start); - let arg_kind = arg_kind.parse::()?; - let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; - let simd_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse simd_len: {part}")?, - ), - None => None, - }; - let vec_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse vec_len: {part}")?, - ), - None => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }, - target: target.to_string(), - }) - } else { - let kind = start.parse::()?; - let bit_len = match kind { - TypeKind::Int(_) => Some(32), - _ => None, - }; - Ok(ArmIntrinsicType { - data: IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: start.parse::()?, - bit_len, - simd_len: None, - vec_len: None, - }, - target: target.to_string(), - }) - } + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind: start.parse::()?, + bit_len, + simd_len: None, + vec_len: None, + }) } } } From 75a35f45455298cc9504c52316516f7f406b17e1 Mon Sep 17 00:00:00 2001 From: David Wood Date: Thu, 7 May 2026 15:05:56 +0000 Subject: [PATCH 085/174] intrinsic-test: parsing of SVE types Updates `parse_intrinsic_type` to support SVE intrinsic types, with the limited changes required to `IntrinsicType` and relevant users of the `simd_len` to preserve existing behaviour, assuming the tool never attempts to generate tests for SVE intrinsics (which it doesn't yet) --- .../crates/intrinsic-test/src/arm/types.rs | 177 +++++++++++------- .../src/common/intrinsic_helpers.rs | 78 +++++--- .../crates/intrinsic-test/src/x86/types.rs | 8 +- 3 files changed, 170 insertions(+), 93 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/types.rs b/library/stdarch/crates/intrinsic-test/src/arm/types.rs index 40035b35d5544..cd420f10678fc 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/types.rs @@ -1,5 +1,7 @@ use super::intrinsic::ArmIntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; impl IntrinsicTypeDefinition for ArmIntrinsicType { /// Gets a string containing the typename for this type in C format. @@ -9,8 +11,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{prefix}{bit_len}_t"), - (Some(simd), None) => format!("{prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -25,8 +33,14 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let Some(bit_len) = self.bit_len { match (self.simd_len, self.vec_len) { (None, None) => format!("{rust_prefix}{bit_len}"), - (Some(simd), None) => format!("{c_prefix}{bit_len}x{simd}_t"), - (Some(simd), Some(vec)) => format!("{c_prefix}{bit_len}x{simd}x{vec}_t"), + (Some(SimdLen::Fixed(simd)), None) => format!("{c_prefix}{bit_len}x{simd}_t"), + (Some(SimdLen::Fixed(simd)), Some(vec)) => { + format!("{c_prefix}{bit_len}x{simd}x{vec}_t") + } + (Some(SimdLen::Scalable), None) => format!("sv{c_prefix}{bit_len}_t"), + (Some(SimdLen::Scalable), Some(vec)) => { + format!("sv{c_prefix}{bit_len}x{vec}_t") + } (None, Some(_)) => todo!("{self:#?}"), // Likely an invalid case } } else { @@ -39,16 +53,11 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { if let IntrinsicType { kind: k, bit_len: Some(bl), - simd_len, vec_len, .. } = **self { - let quad = if simd_len.unwrap_or(1) * bl > 64 { - "q" - } else { - "" - }; + let quad = if self.num_lanes() * bl > 64 { "q" } else { "" }; format!( "vld{len}{quad}_{type}{size}", @@ -71,67 +80,97 @@ impl IntrinsicTypeDefinition for ArmIntrinsicType { pub fn parse_intrinsic_type(s: &str) -> Result { const CONST_STR: &str = "const"; + const ENUM_STR: &str = "enum "; + + // Recurse to handle pointers.. if let Some(s) = s.strip_suffix('*') { - let (s, constant) = match s.trim().strip_suffix(CONST_STR) { - Some(stripped) => (stripped, true), - None => (s, false), + let s = s.trim(); + let (s, constant) = if s.ends_with(CONST_STR) || s.starts_with(CONST_STR) { + ( + s.trim_start_matches(CONST_STR).trim_end_matches(CONST_STR), + true, + ) + } else { + (s, false) }; - let s = s.trim_end(); - let mut ty = parse_intrinsic_type(s)?; + + let mut ty = parse_intrinsic_type(s.trim())?; ty.ptr = true; ty.ptr_constant = constant; - Ok(ty) - } else { - // [const ]TYPE[{bitlen}[x{simdlen}[x{vec_len}]]][_t] - let (mut s, constant) = match s.strip_prefix(CONST_STR) { - Some(stripped) => (stripped.trim(), true), - None => (s, false), - }; - s = s.strip_suffix("_t").unwrap_or(s); - let mut parts = s.split('x'); // [[{bitlen}], [{simdlen}], [{vec_len}] ] - let start = parts.next().ok_or("Impossible to parse type")?; - if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { - let (arg_kind, bit_len) = start.split_at(digit_start); - let arg_kind = arg_kind.parse::()?; - let bit_len = bit_len.parse::().map_err(|err| err.to_string())?; - let simd_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse simd_len: {part}")?, - ), - None => None, - }; - let vec_len = match parts.next() { - Some(part) => Some( - part.parse::() - .map_err(|_| "Couldn't parse vec_len: {part}")?, - ), - None => None, - }; - Ok(IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: arg_kind, - bit_len: Some(bit_len), - simd_len, - vec_len, - }) - } else { - let kind = start.parse::()?; - let bit_len = match kind { - TypeKind::Int(_) => Some(32), - _ => None, - }; - Ok(IntrinsicType { - ptr: false, - ptr_constant: false, - constant, - kind: start.parse::()?, - bit_len, - simd_len: None, - vec_len: None, - }) - } + return Ok(ty); } + + // [const ][sv]TYPE[{element_bits}[x{num_lanes}[x{num_vecs}]]][_t] + // | [enum ]TYPE + let (mut s, constant) = match (s.strip_prefix(CONST_STR), s.strip_prefix(ENUM_STR)) { + (Some(const_strip), _) => (const_strip, true), + (_, Some(enum_strip)) => (enum_strip, true), + (None, None) => (s, false), + }; + s = s.trim(); + s = s.strip_suffix("_t").unwrap_or(s); + + // Consider the following types as examples: + // A) `svuint32x3_t` + // B) `float16x4x2_t` + // C) `svbool_t` + + let sve = s.starts_with("sv"); + + let mut parts = s.split('x'); + let start = parts.next().ok_or("failed to parse type")?; + + // Continuing the previous examples.. + // A) kind=TypeKind::Int(Sign::Unsigned), bit_len=Some(32) + // B) kind=TypeKind::Float, bit_len=Some(16) + // C) kind=TypeKind::Bool, bit_len=None + let (kind, bit_len) = if let Some(digit_start) = start.find(|c: char| c.is_ascii_digit()) { + let (element_kind, element_bits) = start.split_at(digit_start); + let element_kind = element_kind.parse::()?; + let element_bits = element_bits.parse::().map_err(|err| err.to_string())?; + (element_kind, Some(element_bits)) + } else { + let element_kind = start.parse::()?; + (element_kind, None) + }; + + let bit_len = match (bit_len, kind) { + (None, TypeKind::SvPattern | TypeKind::SvPrefetchOp | TypeKind::Int(_)) => Some(32), + (None, TypeKind::Bool) => Some(8), + _ => bit_len, + }; + + // Continuing the previous examples.. + // A) second_len=Some(3) + // B) second_len=Some(4) + // C) second_len=None + let second_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse second part of type") + }); + + // Continuing the previous examples.. + // A) third_len=None + // B) third_len=Some(2) + // C) third_len=None + let third_len = parts.next().map(|part| { + part.parse::() + .expect("failed to parse third part of type") + }); + + let (simd_len, vec_len) = if sve { + (Some(SimdLen::Scalable), second_len) + } else { + (second_len.map(SimdLen::Fixed), third_len) + }; + + Ok(IntrinsicType { + ptr: false, + ptr_constant: false, + constant, + kind, + bit_len, + simd_len, + vec_len, + }) } diff --git a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs index cb8740fa07001..a894d5c0164e1 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/intrinsic_helpers.rs @@ -15,6 +15,7 @@ pub enum Sign { #[derive(Debug, PartialEq, Copy, Clone)] pub enum TypeKind { + Bool, BFloat, Float, Int(Sign), @@ -23,6 +24,8 @@ pub enum TypeKind { Void, Mask, Vector, + SvPattern, + SvPrefetchOp, } impl FromStr for TypeKind { @@ -30,17 +33,22 @@ impl FromStr for TypeKind { fn from_str(s: &str) -> Result { match s { - "bfloat" | "BF16" => Ok(Self::BFloat), - "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), - "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { + "svbool" | "bool" => Ok(Self::Bool), + "svbfloat" | "bfloat" | "BF16" => Ok(Self::BFloat), + "svfloat" | "float" | "double" | "FP16" | "FP32" | "FP64" => Ok(Self::Float), + "svint" | "int" | "long" | "short" | "SI8" | "SI16" | "SI32" | "SI64" => { Ok(Self::Int(Sign::Signed)) } "poly" => Ok(Self::Poly), "char" => Ok(Self::Char(Sign::Signed)), - "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => Ok(Self::Int(Sign::Unsigned)), + "svuint" | "uint" | "unsigned" | "UI8" | "UI16" | "UI32" | "UI64" => { + Ok(Self::Int(Sign::Unsigned)) + } "void" => Ok(Self::Void), "MASK" => Ok(Self::Mask), "M128" | "M256" | "M512" => Ok(Self::Vector), + "svpattern" => Ok(Self::SvPattern), + "svprfop" => Ok(Self::SvPrefetchOp), _ => Err(format!("Impossible to parse argument kind {s}")), } } @@ -52,6 +60,7 @@ impl fmt::Display for TypeKind { f, "{}", match self { + Self::Bool => "bool", Self::BFloat => "bfloat", Self::Float => "float", Self::Int(Sign::Signed) => "int", @@ -62,6 +71,8 @@ impl fmt::Display for TypeKind { Self::Char(Sign::Unsigned) => "unsigned char", Self::Mask => "mask", Self::Vector => "vector", + Self::SvPattern => "svpattern", + Self::SvPrefetchOp => "svprfop", } ) } @@ -71,6 +82,7 @@ impl TypeKind { /// Returns the type component of a C typedef for a type of the form of `{type}{size}_t` pub fn c_prefix(&self) -> &str { match self { + Self::Bool => "bool", Self::Float => "float", Self::Int(Sign::Signed) => "int", Self::Int(Sign::Unsigned) => "uint", @@ -98,6 +110,21 @@ impl TypeKind { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum SimdLen { + Scalable, + Fixed(u32), +} + +impl std::fmt::Display for SimdLen { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::Scalable => unimplemented!(), + Self::Fixed(len) => ::fmt(len, f), + } + } +} + #[derive(Debug, PartialEq, Clone)] pub struct IntrinsicType { /// Is this an immediate? @@ -115,13 +142,13 @@ pub struct IntrinsicType { /// Number of bits of this type (e.g. 32 for `u32`). pub bit_len: Option, - /// Length of a SIMD vector (i.e. 4 for `uint32x4_t`). + /// Length of a SIMD vector (i.e. `Fixed(4)` for `uint32x4_t`). /// /// A value of `None` means this is not a SIMD type. The number of lanes of a type with /// `simd_len=None` can be assumed to be one, though it is important to maintain a distinction - /// between `simd_len=None` and `simd_len=Some(1)` so as to differentiate between `u64` and - /// `uint64x1_t`. - pub simd_len: Option, + /// between `simd_len=None` and `simd_len=Some(Fixed(1))` so as to differentiate between `u64` + /// and `uint64x1_t`. A value of `Some(Scalable)` indicates that this is a scalable vector. + pub simd_len: Option, /// Number of rows of a SIMD matrix (i.e. 2 for `uint8x8x2_t`). /// @@ -147,7 +174,13 @@ impl IntrinsicType { /// Returns the number of lanes of the type pub fn num_lanes(&self) -> u32 { - self.simd_len.unwrap_or(1) + self.simd_len + .as_ref() + .map(|len| match len { + SimdLen::Scalable => unimplemented!(), + SimdLen::Fixed(len) => *len, + }) + .unwrap_or(1) } /// Returns the number of vectors of the type @@ -179,14 +212,14 @@ impl IntrinsicType { bit_len: Some(bit_len @ (1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 16 | 32 | 64)), kind: kind @ (TypeKind::Int(_) | TypeKind::Poly | TypeKind::Char(_) | TypeKind::Mask), - simd_len, vec_len, .. } => { format!( "[\n{body}\n]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(*bit_len, i); assert!(src == 0 || src.ilog2() < *bit_len); if *kind == TypeKind::Int(Sign::Signed) && (src >> (*bit_len - 1)) != 0 @@ -199,37 +232,39 @@ impl IntrinsicType { } else { fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } IntrinsicType { kind: TypeKind::Float, bit_len: Some(bit_len @ (16 | 32 | 64)), - simd_len, vec_len, .. } => { format!( "[\n{body}\n]", - body = (0..(simd_len.unwrap_or(1) * vec_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| fmt(&format_args!( + body = (0..(self.num_lanes() * vec_len.unwrap_or(1) + loads - 1)).format_with( + ",\n", + |i, fmt| fmt(&format_args!( "f{bit_len}::from_bits({src:#x})", src = value_for_array(*bit_len, i) - ))) + )) + ) ) } IntrinsicType { kind: TypeKind::Vector, bit_len: Some(128 | 256 | 512), - simd_len, vec_len, .. } => { let effective_bit_len = 32; format!( "[\n{body}\n]", - body = (0..(vec_len.unwrap_or(1) * simd_len.unwrap_or(1) + loads - 1)) - .format_with(",\n", |i, fmt| { + body = (0..(vec_len.unwrap_or(1) * self.num_lanes() + loads - 1)).format_with( + ",\n", + |i, fmt| { let src = value_for_array(effective_bit_len, i); assert!(src == 0 || src.ilog2() < effective_bit_len); if (src >> (effective_bit_len - 1)) != 0 { @@ -241,7 +276,8 @@ impl IntrinsicType { } else { fmt(&format_args!("{src:#x}")) } - }) + } + ) ) } _ => unimplemented!("populate random: {self:#?}"), diff --git a/library/stdarch/crates/intrinsic-test/src/x86/types.rs b/library/stdarch/crates/intrinsic-test/src/x86/types.rs index c6ea15e150752..a0e14c77d6b5e 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/types.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/types.rs @@ -3,7 +3,9 @@ use std::str::FromStr; use itertools::Itertools; use super::intrinsic::X86IntrinsicType; -use crate::common::intrinsic_helpers::{IntrinsicType, IntrinsicTypeDefinition, Sign, TypeKind}; +use crate::common::intrinsic_helpers::{ + IntrinsicType, IntrinsicTypeDefinition, Sign, SimdLen, TypeKind, +}; use crate::x86::xml_parser::Parameter; impl IntrinsicTypeDefinition for X86IntrinsicType { @@ -187,7 +189,7 @@ impl X86IntrinsicType { Ok(num_bits) => self .data .bit_len - .and_then(|bit_len| Some(num_bits / bit_len)), + .and_then(|bit_len| Some(SimdLen::Fixed(num_bits / bit_len))), Err(_) => None, }; } @@ -297,7 +299,7 @@ impl X86IntrinsicType { // - _mm512_conj_pch if param.type_data == "__m512h" && param.etype == "FP32" { data.bit_len = Some(16); - data.simd_len = Some(32); + data.simd_len = Some(SimdLen::Fixed(32)); } let mut result = X86IntrinsicType { From 810bd04ff80abf1398f3b8d51e899e15864c4b91 Mon Sep 17 00:00:00 2001 From: David Wood Date: Fri, 8 May 2026 15:30:20 +0000 Subject: [PATCH 086/174] intrinsic-test: add SVE-specific constraints --- .../intrinsic-test/src/arm/json_parser.rs | 35 ++++++++++++++----- .../intrinsic-test/src/common/constraint.rs | 27 +++++++++++--- 2 files changed, 50 insertions(+), 12 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index 5a86abdc6d1a6..fb0d16154b181 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -3,7 +3,7 @@ use crate::arm::types::parse_intrinsic_type; use crate::common::argument::{Argument, ArgumentList}; use crate::common::constraint::Constraint; use crate::common::intrinsic::Intrinsic; -use crate::common::intrinsic_helpers::IntrinsicType; +use crate::common::intrinsic_helpers::{IntrinsicType, TypeKind}; use serde::Deserialize; use serde_json::Value; use std::collections::HashMap; @@ -90,18 +90,37 @@ fn json_to_intrinsic( .enumerate() .map(|(i, arg)| { let (type_name, arg_name) = Argument::::type_and_name_from_c(&arg); + + let arg_ty = parse_intrinsic_type(type_name) + .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); + let metadata = intr.args_prep.as_mut(); let metadata = metadata.and_then(|a| a.remove(arg_name)); let arg_prep: Option = metadata.and_then(|a| a.try_into().ok()); - let constraint: Option = arg_prep.and_then(|a| a.try_into().ok()); - let arg_ty = ArmIntrinsicType( - parse_intrinsic_type(type_name) - .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")), + let constraint: Option = + arg_prep.and_then(|a| a.try_into().ok()).or_else(|| { + if arg_ty.kind() == TypeKind::SvPattern { + Some(Constraint::SvPattern) + } else if arg_ty.kind() == TypeKind::SvPrefetchOp { + Some(Constraint::SvPrefetchOp) + } else if arg_name == "imm_rotation" { + if name.starts_with("svcadd_") || name.starts_with("svqcadd_") { + Some(Constraint::SvImmRotationAdd) + } else { + Some(Constraint::SvImmRotation) + } + } else { + None + } + }); + + let mut arg = Argument::::new( + i, + String::from(arg_name), + ArmIntrinsicType(arg_ty), + constraint, ); - let mut arg = - Argument::::new(i, String::from(arg_name), arg_ty, constraint); - // The JSON doesn't list immediates as const let IntrinsicType { ref mut constant, .. diff --git a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs index c78eb3541cfdc..ab52d866ab20a 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/constraint.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/constraint.rs @@ -10,15 +10,34 @@ pub enum Constraint { Range(Range), /// Test discrete values, e.g. `vec![1, 2, 4, 8]`. Set(Vec), + /// Values of `core::arch::aarch64::svpattern` + SvPattern, + /// Values of `core::arch::aarch64::svprfop` + SvPrefetchOp, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation. + SvImmRotation, + // Values of the `imm_rotation` argument in SVE intrinsics where arguments contain complex + // pairs and `imm_rotation` corresponds to the rotation (this variant is specifically for + // `svcadd` and `svqcadd` where only 90 and 270 are valid arguments). + SvImmRotationAdd, } impl Constraint { /// Returns an iterator over the values of this constraint - pub fn iter<'a>(&'a self) -> impl Iterator + 'a { + pub fn iter(&self) -> Box + '_> { match self { - Constraint::Equal(i) => std::slice::Iter::default().copied().chain(*i..*i + 1), - Constraint::Range(range) => std::slice::Iter::default().copied().chain(range.clone()), - Constraint::Set(items) => items.iter().copied().chain(std::ops::Range::default()), + Constraint::Equal(i) => Box::new(std::iter::once(*i)), + Constraint::Range(range) => Box::new(range.clone()), + Constraint::Set(items) => Box::new(items.iter().copied().chain(Range::default())), + // These values are discriminants of the `svpattern` enum + Constraint::SvPattern => Box::new((0..=13).chain(29..=31)), + // These values are discriminants of the `svprfop` enum + Constraint::SvPrefetchOp => Box::new((0..=5).chain(8..=14)), + // Valid rotations for intrinsics operating on complex pairs: 0, 90, 180, 270 + Constraint::SvImmRotation => Box::new((0..=270).step_by(90)), + // Valid rotations for `svcadd` and `svqcadd`: 0, 270 + Constraint::SvImmRotationAdd => Box::new((90..=270).step_by(180)), } } } From dfdde18e56268c613f33ae86b3695b0c790cbb70 Mon Sep 17 00:00:00 2001 From: David Wood Date: Wed, 20 May 2026 11:09:10 +0000 Subject: [PATCH 087/174] intrinsic-test: remove `arm::argument` module This module only defined a single function that could easily just be inlined into its only caller. --- .../crates/intrinsic-test/src/arm/argument.rs | 15 --------------- .../crates/intrinsic-test/src/arm/json_parser.rs | 8 +++++++- .../stdarch/crates/intrinsic-test/src/arm/mod.rs | 1 - 3 files changed, 7 insertions(+), 17 deletions(-) delete mode 100644 library/stdarch/crates/intrinsic-test/src/arm/argument.rs diff --git a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs b/library/stdarch/crates/intrinsic-test/src/arm/argument.rs deleted file mode 100644 index c43609bb2db0d..0000000000000 --- a/library/stdarch/crates/intrinsic-test/src/arm/argument.rs +++ /dev/null @@ -1,15 +0,0 @@ -use crate::arm::intrinsic::ArmIntrinsicType; -use crate::common::argument::Argument; - -// This functionality is present due to the nature -// of how intrinsics are defined in the JSON source -// of ARM intrinsics. -impl Argument { - pub fn type_and_name_from_c(arg: &str) -> (&str, &str) { - let split_index = arg - .rfind([' ', '*']) - .expect("Couldn't split type and argname"); - - (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) - } -} diff --git a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs index fb0d16154b181..06cf78a422285 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/json_parser.rs @@ -89,7 +89,13 @@ fn json_to_intrinsic( .into_iter() .enumerate() .map(|(i, arg)| { - let (type_name, arg_name) = Argument::::type_and_name_from_c(&arg); + let (type_name, arg_name) = { + let split_index = arg + .rfind([' ', '*']) + .expect("Couldn't split type and argname"); + + (arg[..split_index + 1].trim_end(), &arg[split_index + 1..]) + }; let arg_ty = parse_intrinsic_type(type_name) .unwrap_or_else(|_| panic!("Failed to parse argument '{arg}'")); diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 8935b3ca66f0a..9936f8abcbb1c 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -1,4 +1,3 @@ -mod argument; mod config; mod intrinsic; mod json_parser; From 9c9f439997acda5719b470c8ec79b7db39cf21ea Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Wed, 20 May 2026 20:45:36 +0200 Subject: [PATCH 088/174] Make attr template suggestions preserve attribute safety. --- compiler/rustc_feature/src/builtin_attrs.rs | 8 ++++---- tests/ui/attributes/malformed-attrs.stderr | 11 +++++------ 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index fa37466d4d5cc..47194e874c66e 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -135,23 +135,23 @@ impl AttributeTemplate { if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{maybe_unsafe_start}{start}{name}{end}{maybe_unsafe_end}")); + suggestions.push(format!("{start}{maybe_unsafe_start}{name}{maybe_unsafe_end}{end}")); } if let Some(descr) = self.list { for descr in descr { suggestions.push(format!( - "{maybe_unsafe_start}{start}{name}{macro_call}({descr}){end}{maybe_unsafe_end}" + "{start}{maybe_unsafe_start}{name}{macro_call}({descr}){maybe_unsafe_end}{end}" )); } } suggestions.extend(self.one_of.iter().map(|&word| { - format!("{maybe_unsafe_start}{start}{name}({word}){end}{maybe_unsafe_end}") + format!("{start}{maybe_unsafe_start}{name}({word}){maybe_unsafe_end}{end}") })); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { suggestions.push(format!( - "{maybe_unsafe_start}{start}{name} = \"{descr}\"{end}{maybe_unsafe_end}" + "{start}{maybe_unsafe_start}{name} = \"{descr}\"{maybe_unsafe_end}{end}" )); } } diff --git a/tests/ui/attributes/malformed-attrs.stderr b/tests/ui/attributes/malformed-attrs.stderr index 784cbb38aaedd..4c9673bf80562 100644 --- a/tests/ui/attributes/malformed-attrs.stderr +++ b/tests/ui/attributes/malformed-attrs.stderr @@ -159,9 +159,8 @@ LL | #[unsafe(export_name)] | help: must be of the form | -LL - #[unsafe(export_name)] -LL + #[export_name = "name"] - | +LL | #[unsafe(export_name = "name")] + | ++++++++ error: `rustc_allow_const_fn_unstable` expects a list of feature names --> $DIR/malformed-attrs.rs:31:1 @@ -330,7 +329,7 @@ LL | #[unsafe(naked())] help: must be of the form | LL - #[unsafe(naked())] -LL + #[naked] +LL + #[unsafe(naked)] | error[E0565]: malformed `track_caller` attribute input @@ -651,7 +650,7 @@ LL | #[unsafe(ffi_pure = 1)] help: must be of the form | LL - #[unsafe(ffi_pure = 1)] -LL + #[ffi_pure] +LL + #[unsafe(ffi_pure)] | error[E0539]: malformed `link_ordinal` attribute input @@ -677,7 +676,7 @@ LL | #[unsafe(ffi_const = 1)] help: must be of the form | LL - #[unsafe(ffi_const = 1)] -LL + #[ffi_const] +LL + #[unsafe(ffi_const)] | error[E0539]: malformed `linkage` attribute input From 7c7e9cd820282c0fe634aa22e1db96749cacc8af Mon Sep 17 00:00:00 2001 From: WANG Rui Date: Mon, 11 May 2026 19:07:38 +0800 Subject: [PATCH 089/174] loongarch: Use `intrinsics::simd` for vrepl{128}ve{0,i} --- .../src/loongarch64/lasx/generated.rs | 89 ------------------- .../src/loongarch64/lasx/portable.rs | 85 ++++++++++++++++++ .../src/loongarch64/lsx/generated.rs | 44 --------- .../core_arch/src/loongarch64/lsx/portable.rs | 28 ++++++ .../crates/core_arch/src/loongarch64/simd.rs | 14 +++ .../crates/stdarch-gen-loongarch/lasx.spec | 9 ++ .../crates/stdarch-gen-loongarch/lsx.spec | 4 + .../src/portable-intrinsics.txt | 13 +++ 8 files changed, 153 insertions(+), 133 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs index 1fa156c85a422..ec7693e27747a 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/generated.rs @@ -147,14 +147,6 @@ unsafe extern "unadjusted" { fn __lasx_xvhsubw_wu_hu(a: __v16u16, b: __v16u16) -> __v8i32; #[link_name = "llvm.loongarch.lasx.xvhsubw.du.wu"] fn __lasx_xvhsubw_du_wu(a: __v8u32, b: __v8u32) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.b"] - fn __lasx_xvrepl128vei_b(a: __v32i8, b: u32) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.h"] - fn __lasx_xvrepl128vei_h(a: __v16i16, b: u32) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.w"] - fn __lasx_xvrepl128vei_w(a: __v8i32, b: u32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvrepl128vei.d"] - fn __lasx_xvrepl128vei_d(a: __v4i64, b: u32) -> __v4i64; #[link_name = "llvm.loongarch.lasx.xvpackev.b"] fn __lasx_xvpackev_b(a: __v32i8, b: __v32i8) -> __v32i8; #[link_name = "llvm.loongarch.lasx.xvpackev.h"] @@ -525,16 +517,6 @@ unsafe extern "unadjusted" { fn __lasx_xvstx(a: __v32i8, b: *mut i8, c: i64); #[link_name = "llvm.loongarch.lasx.xvextl.qu.du"] fn __lasx_xvextl_qu_du(a: __v4u64) -> __v4u64; - #[link_name = "llvm.loongarch.lasx.xvreplve0.b"] - fn __lasx_xvreplve0_b(a: __v32i8) -> __v32i8; - #[link_name = "llvm.loongarch.lasx.xvreplve0.h"] - fn __lasx_xvreplve0_h(a: __v16i16) -> __v16i16; - #[link_name = "llvm.loongarch.lasx.xvreplve0.w"] - fn __lasx_xvreplve0_w(a: __v8i32) -> __v8i32; - #[link_name = "llvm.loongarch.lasx.xvreplve0.d"] - fn __lasx_xvreplve0_d(a: __v4i64) -> __v4i64; - #[link_name = "llvm.loongarch.lasx.xvreplve0.q"] - fn __lasx_xvreplve0_q(a: __v32i8) -> __v32i8; #[link_name = "llvm.loongarch.lasx.vext2xv.h.b"] fn __lasx_vext2xv_h_b(a: __v32i8) -> __v16i16; #[link_name = "llvm.loongarch.lasx.vext2xv.w.h"] @@ -1585,42 +1567,6 @@ pub fn lasx_xvhsubw_du_wu(a: m256i, b: m256i) -> m256i { unsafe { transmute(__lasx_xvhsubw_du_wu(transmute(a), transmute(b))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_b(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lasx_xvrepl128vei_b(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_h(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lasx_xvrepl128vei_h(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_w(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM2, 2); - unsafe { transmute(__lasx_xvrepl128vei_w(transmute(a), IMM2)) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvrepl128vei_d(a: m256i) -> m256i { - static_assert_uimm_bits!(IMM1, 1); - unsafe { transmute(__lasx_xvrepl128vei_d(transmute(a), IMM1)) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -2990,41 +2936,6 @@ pub fn lasx_xvextl_qu_du(a: m256i) -> m256i { unsafe { transmute(__lasx_xvextl_qu_du(transmute(a))) } } -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_b(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_b(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_h(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_h(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_w(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_w(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_d(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_d(transmute(a))) } -} - -#[inline] -#[target_feature(enable = "lasx")] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lasx_xvreplve0_q(a: m256i) -> m256i { - unsafe { transmute(__lasx_xvreplve0_q(transmute(a))) } -} - #[inline] #[target_feature(enable = "lasx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs index 0c583795af9b7..aa4144cbb4da2 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lasx/portable.rs @@ -129,6 +129,82 @@ pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { simd_shuffle!(b, a, [0, 4, 2, 6]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_b(a: T) -> T { + simd_shuffle!( + a, + a, + [ + I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, + I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, + I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16, I + 16 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_h(a: T) -> T { + simd_shuffle!( + a, + a, + [ + I, I, I, I, I, I, I, I, + I + 8, I + 8, I + 8, I + 8, I + 8, I + 8, I + 8, I + 8 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_w(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I + 4, I + 4, I + 4, I + 4]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_d(a: T) -> T { + simd_shuffle!(a, a, [I, I, I + 2, I + 2]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_b(a: T) -> T { + simd_shuffle!( + a, + a, + [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + ] + ) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_h(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_w(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0, 0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_d(a: T) -> T { + simd_shuffle!(a, a, [0, 0, 0, 0]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(super) const unsafe fn simd_replve0_q(a: T) -> T { + simd_shuffle!(a, a, [0, 1, 0, 1]) +} + impl_vv!("lasx", lasx_xvpcnt_b, is::simd_ctpop, m256i, i8x32); impl_vv!("lasx", lasx_xvpcnt_h, is::simd_ctpop, m256i, i16x16); impl_vv!("lasx", lasx_xvpcnt_w, is::simd_ctpop, m256i, i32x8); @@ -143,6 +219,11 @@ impl_vv!("lasx", lasx_xvneg_w, is::simd_neg, m256i, i32x8); impl_vv!("lasx", lasx_xvneg_d, is::simd_neg, m256i, i64x4); impl_vv!("lasx", lasx_xvfsqrt_s, is::simd_fsqrt, m256, f32x8); impl_vv!("lasx", lasx_xvfsqrt_d, is::simd_fsqrt, m256d, f64x4); +impl_vv!("lasx", lasx_xvreplve0_b, simd_replve0_b, m256i, i8x32); +impl_vv!("lasx", lasx_xvreplve0_h, simd_replve0_h, m256i, i16x16); +impl_vv!("lasx", lasx_xvreplve0_w, simd_replve0_w, m256i, i32x8); +impl_vv!("lasx", lasx_xvreplve0_d, simd_replve0_d, m256i, i64x4); +impl_vv!("lasx", lasx_xvreplve0_q, simd_replve0_q, m256i, i64x4); impl_gv!("lasx", lasx_xvreplgr2vr_b, ls::simd_splat, m256i, i8x32, i32); impl_gv!("lasx", lasx_xvreplgr2vr_h, ls::simd_splat, m256i, i16x16, i32); @@ -333,6 +414,10 @@ impl_vuv!("lasx", lasx_xvmini_bu, cs::simd_imin, m256i, u8x32, 5); impl_vuv!("lasx", lasx_xvmini_hu, cs::simd_imin, m256i, u16x16, 5); impl_vuv!("lasx", lasx_xvmini_wu, cs::simd_imin, m256i, u32x8, 5); impl_vuv!("lasx", lasx_xvmini_du, cs::simd_imin, m256i, u64x4, 5); +impl_vuv!("lasx", lasx_xvrepl128vei_b, simd_replvei_b, m256i, i8x32, 4, const); +impl_vuv!("lasx", lasx_xvrepl128vei_h, simd_replvei_h, m256i, i16x16, 3, const); +impl_vuv!("lasx", lasx_xvrepl128vei_w, simd_replvei_w, m256i, i32x8, 2, const); +impl_vuv!("lasx", lasx_xvrepl128vei_d, simd_replvei_d, m256i, i64x4, 1, const); impl_vug!("lasx", lasx_xvpickve2gr_w, is::simd_extract, m256i, i32x8, i32, 3); impl_vug!("lasx", lasx_xvpickve2gr_d, is::simd_extract, m256i, i64x4, i64, 2); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs index bc08e0cea726e..9f13d2b3c43b4 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/generated.rs @@ -155,14 +155,6 @@ unsafe extern "unadjusted" { fn __lsx_vreplve_w(a: __v4i32, b: i32) -> __v4i32; #[link_name = "llvm.loongarch.lsx.vreplve.d"] fn __lsx_vreplve_d(a: __v2i64, b: i32) -> __v2i64; - #[link_name = "llvm.loongarch.lsx.vreplvei.b"] - fn __lsx_vreplvei_b(a: __v16i8, b: u32) -> __v16i8; - #[link_name = "llvm.loongarch.lsx.vreplvei.h"] - fn __lsx_vreplvei_h(a: __v8i16, b: u32) -> __v8i16; - #[link_name = "llvm.loongarch.lsx.vreplvei.w"] - fn __lsx_vreplvei_w(a: __v4i32, b: u32) -> __v4i32; - #[link_name = "llvm.loongarch.lsx.vreplvei.d"] - fn __lsx_vreplvei_d(a: __v2i64, b: u32) -> __v2i64; #[link_name = "llvm.loongarch.lsx.vpackev.b"] fn __lsx_vpackev_b(a: __v16i8, b: __v16i8) -> __v16i8; #[link_name = "llvm.loongarch.lsx.vpackev.h"] @@ -1525,42 +1517,6 @@ pub fn lsx_vreplve_d(a: m128i, b: i32) -> m128i { unsafe { transmute(__lsx_vreplve_d(transmute(a), transmute(b))) } } -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_b(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM4, 4); - unsafe { transmute(__lsx_vreplvei_b(transmute(a), IMM4)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_h(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM3, 3); - unsafe { transmute(__lsx_vreplvei_h(transmute(a), IMM3)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_w(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM2, 2); - unsafe { transmute(__lsx_vreplvei_w(transmute(a), IMM2)) } -} - -#[inline] -#[target_feature(enable = "lsx")] -#[rustc_legacy_const_generics(1)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn lsx_vreplvei_d(a: m128i) -> m128i { - static_assert_uimm_bits!(IMM1, 1); - unsafe { transmute(__lsx_vreplvei_d(transmute(a), IMM1)) } -} - #[inline] #[target_feature(enable = "lsx")] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs index 99366dedb24c3..2d24256838b21 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/lsx/portable.rs @@ -101,6 +101,30 @@ pub(crate) const unsafe fn simd_ilvl_d(a: T, b: T) -> T { simd_shuffle!(b, a, [0, 2]) } +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_b(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I, I, I, I, I, I, I, I, I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_h(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I, I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_w(a: T) -> T { + simd_shuffle!(a, a, [I, I, I, I]) +} + +#[inline(always)] +#[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +pub(crate) const unsafe fn simd_replvei_d(a: T) -> T { + simd_shuffle!(a, a, [I, I]) +} + impl_vv!("lsx", lsx_vpcnt_b, is::simd_ctpop, m128i, i8x16); impl_vv!("lsx", lsx_vpcnt_h, is::simd_ctpop, m128i, i16x8); impl_vv!("lsx", lsx_vpcnt_w, is::simd_ctpop, m128i, i32x4); @@ -305,6 +329,10 @@ impl_vuv!("lsx", lsx_vmini_bu, cs::simd_imin, m128i, u8x16, 5); impl_vuv!("lsx", lsx_vmini_hu, cs::simd_imin, m128i, u16x8, 5); impl_vuv!("lsx", lsx_vmini_wu, cs::simd_imin, m128i, u32x4, 5); impl_vuv!("lsx", lsx_vmini_du, cs::simd_imin, m128i, u64x2, 5); +impl_vuv!("lsx", lsx_vreplvei_b, simd_replvei_b, m128i, i8x16, 4, const); +impl_vuv!("lsx", lsx_vreplvei_h, simd_replvei_h, m128i, i16x8, 3, const); +impl_vuv!("lsx", lsx_vreplvei_w, simd_replvei_w, m128i, i32x4, 2, const); +impl_vuv!("lsx", lsx_vreplvei_d, simd_replvei_d, m128i, i64x2, 1, const); impl_vug!("lsx", lsx_vpickve2gr_b, is::simd_extract, m128i, i8x16, i32, 4); impl_vug!("lsx", lsx_vpickve2gr_h, is::simd_extract, m128i, i16x8, i32, 3); diff --git a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs index b4ec6881c36ab..580ca1ce7e804 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs @@ -257,6 +257,20 @@ macro_rules! impl_vuv { } } }; + ($ft:literal, $name:ident, $op:ident, $oty:ty, $ity:ident, $ibs:expr, const) => { + #[inline] + #[target_feature(enable = $ft)] + #[rustc_legacy_const_generics(1)] + #[unstable(feature = "stdarch_loongarch", issue = "117427")] + pub fn $name(a: $oty) -> $oty { + static_assert_uimm_bits!(IMM, $ibs); + unsafe { + let a: $ity = transmute(a); + let r: $ity = $op::(a); + transmute(r) + } + } + }; } pub(super) use impl_vuv; diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec index 5f85b1909db1f..c74199c6a6e4c 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lasx.spec @@ -1400,21 +1400,25 @@ asm-fmts = xd, xj, xk data-types = UV4DI, UV4DI, UV4DI /// lasx_xvrepl128vei_b +impl = portable name = lasx_xvrepl128vei_b asm-fmts = xd, xj, ui4 data-types = V32QI, V32QI, UQI /// lasx_xvrepl128vei_h +impl = portable name = lasx_xvrepl128vei_h asm-fmts = xd, xj, ui3 data-types = V16HI, V16HI, UQI /// lasx_xvrepl128vei_w +impl = portable name = lasx_xvrepl128vei_w asm-fmts = xd, xj, ui2 data-types = V8SI, V8SI, UQI /// lasx_xvrepl128vei_d +impl = portable name = lasx_xvrepl128vei_d asm-fmts = xd, xj, ui1 data-types = V4DI, V4DI, UQI @@ -2693,26 +2697,31 @@ asm-fmts = xd, rj, ui2 data-types = V4DI, V4DI, DI, UQI /// lasx_xvreplve0_b +impl = portable name = lasx_xvreplve0_b asm-fmts = xd, xj data-types = V32QI, V32QI /// lasx_xvreplve0_h +impl = portable name = lasx_xvreplve0_h asm-fmts = xd, xj data-types = V16HI, V16HI /// lasx_xvreplve0_w +impl = portable name = lasx_xvreplve0_w asm-fmts = xd, xj data-types = V8SI, V8SI /// lasx_xvreplve0_d +impl = portable name = lasx_xvreplve0_d asm-fmts = xd, xj data-types = V4DI, V4DI /// lasx_xvreplve0_q +impl = portable name = lasx_xvreplve0_q asm-fmts = xd, xj data-types = V32QI, V32QI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec index 6c554ac28d3a1..c59f56a902d13 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec +++ b/library/stdarch/crates/stdarch-gen-loongarch/lsx.spec @@ -1420,21 +1420,25 @@ asm-fmts = vd, vj, rk data-types = V2DI, V2DI, SI /// lsx_vreplvei_b +impl = portable name = lsx_vreplvei_b asm-fmts = vd, vj, ui4 data-types = V16QI, V16QI, UQI /// lsx_vreplvei_h +impl = portable name = lsx_vreplvei_h asm-fmts = vd, vj, ui3 data-types = V8HI, V8HI, UQI /// lsx_vreplvei_w +impl = portable name = lsx_vreplvei_w asm-fmts = vd, vj, ui2 data-types = V4SI, V4SI, UQI /// lsx_vreplvei_d +impl = portable name = lsx_vreplvei_d asm-fmts = vd, vj, ui1 data-types = V2DI, V2DI, UQI diff --git a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt index d17d0833df77b..228c9140fc6aa 100644 --- a/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt +++ b/library/stdarch/crates/stdarch-gen-loongarch/src/portable-intrinsics.txt @@ -247,6 +247,10 @@ lsx_vilvl_b lsx_vilvl_h lsx_vilvl_w lsx_vilvl_d +lsx_vreplvei_b +lsx_vreplvei_h +lsx_vreplvei_w +lsx_vreplvei_d # LASX intrinsics lasx_xvsll_b @@ -491,3 +495,12 @@ lasx_xvilvl_b lasx_xvilvl_h lasx_xvilvl_w lasx_xvilvl_d +lasx_xvrepl128vei_b +lasx_xvrepl128vei_h +lasx_xvrepl128vei_w +lasx_xvrepl128vei_d +lasx_xvreplve0_b +lasx_xvreplve0_h +lasx_xvreplve0_w +lasx_xvreplve0_d +lasx_xvreplve0_q From 970cb15506aedd6e488aaef29a9c27b3f45d70f8 Mon Sep 17 00:00:00 2001 From: Aria Givens Date: Thu, 21 May 2026 10:53:02 +0200 Subject: [PATCH 090/174] Use ast node instead of boolean --- Cargo.lock | 1 + .../rustc_attr_parsing/src/attributes/cfg.rs | 4 +-- compiler/rustc_attr_parsing/src/context.rs | 14 +++------- compiler/rustc_feature/Cargo.toml | 1 + compiler/rustc_feature/src/builtin_attrs.rs | 26 +++++++++++-------- 5 files changed, 22 insertions(+), 24 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0f6368e4497cb..e117397bd11b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4010,6 +4010,7 @@ dependencies = [ name = "rustc_feature" version = "0.0.0" dependencies = [ + "rustc_ast", "rustc_data_structures", "rustc_hir", "rustc_span", diff --git a/compiler/rustc_attr_parsing/src/attributes/cfg.rs b/compiler/rustc_attr_parsing/src/attributes/cfg.rs index bc014ae278183..4c5616084cb0b 100644 --- a/compiler/rustc_attr_parsing/src/attributes/cfg.rs +++ b/compiler/rustc_attr_parsing/src/attributes/cfg.rs @@ -329,7 +329,7 @@ pub fn parse_cfg_attr( Err(e) => { let suggestions = CFG_ATTR_TEMPLATE.suggestions( AttrSuggestionStyle::Attribute(cfg_attr.style), - matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + cfg_attr.get_normal_item().unsafety, sym::cfg_attr, ); e.with_span_suggestions( @@ -365,7 +365,7 @@ pub fn parse_cfg_attr( suggestions: session_diagnostics::AttributeParseErrorSuggestions::CreatedByTemplate( CFG_ATTR_TEMPLATE.suggestions( AttrSuggestionStyle::Attribute(cfg_attr.style), - matches!(cfg_attr.get_normal_item().unsafety, rustc_ast::Safety::Unsafe(_)), + cfg_attr.get_normal_item().unsafety, sym::cfg_attr, ), ), diff --git a/compiler/rustc_attr_parsing/src/context.rs b/compiler/rustc_attr_parsing/src/context.rs index 2d5f46b3fb8f2..750fe10a483fc 100644 --- a/compiler/rustc_attr_parsing/src/context.rs +++ b/compiler/rustc_attr_parsing/src/context.rs @@ -358,7 +358,7 @@ pub struct AcceptContext<'f, 'sess> { pub(crate) template: &'f AttributeTemplate, /// The safety attribute (if any) applied to the attribute. - pub(crate) attr_safety: rustc_ast::Safety, + pub(crate) attr_safety: Safety, /// The name of the attribute we're currently accepting. pub(crate) attr_path: AttrPath, @@ -876,11 +876,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions( - style, - matches!(self.attr_safety, rustc_ast::Safety::Unsafe(_)), - &self.attr_path, - ) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } } @@ -1071,11 +1067,7 @@ impl<'a, 'f, 'sess: 'f> AttributeDiagnosticContext<'a, 'f, 'sess> { ParsedDescription::Macro => AttrSuggestionStyle::Macro, }; - self.template.suggestions( - style, - matches!(self.attr_safety, Safety::Unsafe(_)), - &self.attr_path, - ) + self.template.suggestions(style, self.attr_safety, &self.attr_path) } /// Error that a string literal was expected. /// You can optionally give the literal you did find (which you found not to be a string literal) diff --git a/compiler/rustc_feature/Cargo.toml b/compiler/rustc_feature/Cargo.toml index a4746ac455c1d..3cd88cc4bc116 100644 --- a/compiler/rustc_feature/Cargo.toml +++ b/compiler/rustc_feature/Cargo.toml @@ -5,6 +5,7 @@ edition = "2024" [dependencies] # tidy-alphabetical-start +rustc_ast = { path = "../rustc_ast" } rustc_data_structures = { path = "../rustc_data_structures" } rustc_hir = { path = "../rustc_hir" } rustc_span = { path = "../rustc_span" } diff --git a/compiler/rustc_feature/src/builtin_attrs.rs b/compiler/rustc_feature/src/builtin_attrs.rs index 47194e874c66e..34e94497c8a66 100644 --- a/compiler/rustc_feature/src/builtin_attrs.rs +++ b/compiler/rustc_feature/src/builtin_attrs.rs @@ -3,6 +3,7 @@ use std::sync::LazyLock; use AttributeGate::*; +use rustc_ast::ast::Safety; use rustc_data_structures::fx::FxHashMap; use rustc_hir::AttrStyle; use rustc_span::{Symbol, sym}; @@ -118,7 +119,7 @@ impl AttributeTemplate { pub fn suggestions( &self, style: AttrSuggestionStyle, - wrap_with_unsafe: bool, + safety: Safety, name: impl std::fmt::Display, ) -> Vec { let (start, macro_call, end) = match style { @@ -130,29 +131,32 @@ impl AttributeTemplate { let mut suggestions = vec![]; - let (maybe_unsafe_start, maybe_unsafe_end) = - if wrap_with_unsafe { ("unsafe(", ")") } else { ("", "") }; + let (safety_start, safety_end) = match safety { + Safety::Unsafe(_) => ("unsafe(", ")"), + _ => ("", ""), + }; if self.word { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); - suggestions.push(format!("{start}{maybe_unsafe_start}{name}{maybe_unsafe_end}{end}")); + suggestions.push(format!("{start}{safety_start}{name}{safety_end}{end}")); } if let Some(descr) = self.list { for descr in descr { suggestions.push(format!( - "{start}{maybe_unsafe_start}{name}{macro_call}({descr}){maybe_unsafe_end}{end}" + "{start}{safety_start}{name}{macro_call}({descr}){safety_end}{end}" )); } } - suggestions.extend(self.one_of.iter().map(|&word| { - format!("{start}{maybe_unsafe_start}{name}({word}){maybe_unsafe_end}{end}") - })); + suggestions.extend( + self.one_of + .iter() + .map(|&word| format!("{start}{safety_start}{name}({word}){safety_end}{end}")), + ); if let Some(descr) = self.name_value_str { debug_assert!(macro_call.is_empty(), "Macro suggestions use list style"); for descr in descr { - suggestions.push(format!( - "{start}{maybe_unsafe_start}{name} = \"{descr}\"{maybe_unsafe_end}{end}" - )); + suggestions + .push(format!("{start}{safety_start}{name} = \"{descr}\"{safety_end}{end}")); } } suggestions.sort(); From c9a560b30da5cdb7ad1dff4dbfb4fe84fc29f8ce Mon Sep 17 00:00:00 2001 From: Makai Date: Tue, 21 Apr 2026 20:46:59 +0800 Subject: [PATCH 091/174] Implement fast path for `derive(PartialOrd)` --- .../src/deriving/cmp/partial_ord.rs | 54 ++++++++- compiler/rustc_expand/src/base.rs | 2 + compiler/rustc_resolve/src/lib.rs | 4 + compiler/rustc_resolve/src/macros.rs | 12 ++ .../mir-opt/pre-codegen/derived_ord_debug.rs | 4 +- ...p.runtime-optimized.after.panic-unwind.mir | 41 +------ tests/ui/derives/deriving-all-codegen.stdout | 109 +++--------------- .../deriving-with-repr-packed-move-errors.rs | 2 - ...riving-with-repr-packed-move-errors.stderr | 47 +++----- tests/ui/range/range_traits-1.rs | 18 +-- tests/ui/range/range_traits-1.stderr | 78 +------------ tests/ui/stats/macro-stats.stderr | 2 +- tests/ui/type/pattern_types/derives_fail.rs | 1 - .../ui/type/pattern_types/derives_fail.stderr | 13 +-- 14 files changed, 111 insertions(+), 276 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index d9d7dd9c36208..57387e2407f45 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,4 +1,4 @@ -use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety}; +use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, sym}; use thin_vec::thin_vec; @@ -41,6 +41,35 @@ pub(crate) fn expand_deriving_partial_ord( } else { true }; + let substructure = combine_substructure(Box::new(|cx, span, substr| { + cs_partial_cmp(cx, span, substr, discr_then_data) + })); + let (is_simple, substructure) = match item { + Annotatable::Item(annitem) => match &annitem.kind { + ItemKind::Struct(_, ast::Generics { params, .. }, _) + | ItemKind::Enum(_, ast::Generics { params, .. }, _) + if let container_id = cx.current_expansion.id.expn_data().parent.expect_local() + && cx.resolver.has_derive_ord(container_id) + && !params + .iter() + .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) => + { + ( + true, + combine_substructure(Box::new(|cx, span, _| { + cs_partial_cmp_simple( + cx, + span, + cx.expr_ident(span, Ident::new(sym::other, span)), + ) + })), + ) + } + _ => (false, substructure), + }, + _ => (false, substructure), + }; + let partial_cmp_def = MethodDef { name: sym::partial_cmp, generics: Bounds::empty(), @@ -49,9 +78,7 @@ pub(crate) fn expand_deriving_partial_ord( ret_ty, attributes: thin_vec![cx.attr_word(sym::inline, span)], fieldless_variants_strategy: FieldlessVariantsStrategy::Unify, - combine_substructure: combine_substructure(Box::new(|cx, span, substr| { - cs_partial_cmp(cx, span, substr, discr_then_data) - })), + combine_substructure: substructure, }; let trait_def = TraitDef { @@ -68,7 +95,21 @@ pub(crate) fn expand_deriving_partial_ord( safety: Safety::Default, document: true, }; - trait_def.expand(cx, mitem, item, push) + trait_def.expand_ext(cx, mitem, item, push, is_simple) +} + +// Special case for the type deriving both `PartialOrd` and `Ord`. Builds: +// ``` +// Some(self.cmp(other)) +// ``` +fn cs_partial_cmp_simple(cx: &ExtCtxt<'_>, span: Span, other_expr: Box) -> BlockOrExpr { + let cmp_expr = cx.expr_method_call( + span, + cx.expr_self(span), + Ident::new(sym::cmp, span), + thin_vec![other_expr], + ); + BlockOrExpr::new_expr(cx.expr_some(span, cmp_expr)) } fn cs_partial_cmp( @@ -98,7 +139,8 @@ fn cs_partial_cmp( |cx, fold| match fold { CsFold::Single(field) => { let [other_expr] = &field.other_selflike_exprs[..] else { - cx.dcx().span_bug(field.span, "not exactly 2 arguments in `derive(Ord)`"); + cx.dcx() + .span_bug(field.span, "not exactly 2 arguments in `derive(PartialOrd)`"); }; let args = thin_vec![field.self_expr.clone(), other_expr.clone()]; cx.expr_call_global(field.span, partial_cmp_path.clone(), args) diff --git a/compiler/rustc_expand/src/base.rs b/compiler/rustc_expand/src/base.rs index 7f1b58dd1de08..9f474fbeb7650 100644 --- a/compiler/rustc_expand/src/base.rs +++ b/compiler/rustc_expand/src/base.rs @@ -1117,6 +1117,8 @@ pub trait ResolverExpand { // Resolver interfaces for specific built-in macros. /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Copy` inside it? fn has_derive_copy(&self, expn_id: LocalExpnId) -> bool; + /// Does `#[derive(...)]` attribute with the given `ExpnId` have built-in `Ord` inside it? + fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool; /// Resolve paths inside the `#[derive(...)]` attribute with the given `ExpnId`. fn resolve_derives( &mut self, diff --git a/compiler/rustc_resolve/src/lib.rs b/compiler/rustc_resolve/src/lib.rs index b0df15bfde8bd..f5f4c9e6e2580 100644 --- a/compiler/rustc_resolve/src/lib.rs +++ b/compiler/rustc_resolve/src/lib.rs @@ -1323,7 +1323,10 @@ impl ExternPreludeEntry<'_> { struct DeriveData { resolutions: Vec, helper_attrs: Vec<(usize, IdentKey, Span)>, + // if this list keeps getting extended, we could use `bitflags`, + // something like what [`rustc_type_ir::flags::TypeFlags`] is doing. has_derive_copy: bool, + has_derive_ord: bool, } pub struct ResolverOutputs<'tcx> { @@ -1467,6 +1470,7 @@ pub struct Resolver<'ra, 'tcx> { /// Derive macros cannot modify the item themselves and have to store the markers in the global /// context, so they attach the markers to derive container IDs using this resolver table. containers_deriving_copy: FxHashSet = default::fx_hash_set(), + containers_deriving_ord: FxHashSet = default::fx_hash_set(), /// Parent scopes in which the macros were invoked. /// FIXME: `derives` are missing in these parent scopes and need to be taken from elsewhere. invocation_parent_scopes: FxHashMap> = default::fx_hash_map(), diff --git a/compiler/rustc_resolve/src/macros.rs b/compiler/rustc_resolve/src/macros.rs index 378826c60f571..2da26e05863fd 100644 --- a/compiler/rustc_resolve/src/macros.rs +++ b/compiler/rustc_resolve/src/macros.rs @@ -379,6 +379,10 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { self.containers_deriving_copy.contains(&expn_id) } + fn has_derive_ord(&self, expn_id: LocalExpnId) -> bool { + self.containers_deriving_ord.contains(&expn_id) + } + fn resolve_derives( &mut self, expn_id: LocalExpnId, @@ -398,6 +402,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { resolutions: derive_paths(), helper_attrs: Vec::new(), has_derive_copy: false, + has_derive_ord: false, }); let parent_scope = self.invocation_parent_scopes[&expn_id]; for (i, resolution) in entry.resolutions.iter_mut().enumerate() { @@ -420,6 +425,7 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { ); } entry.has_derive_copy |= ext.builtin_name == Some(sym::Copy); + entry.has_derive_ord |= ext.builtin_name == Some(sym::Ord); ext } Ok(_) | Err(Determinacy::Determined) => self.dummy_ext(MacroKind::Derive), @@ -455,6 +461,12 @@ impl<'ra, 'tcx> ResolverExpand for Resolver<'ra, 'tcx> { if entry.has_derive_copy || self.has_derive_copy(parent_scope.expansion) { self.containers_deriving_copy.insert(expn_id); } + // Similar to the above `Copy` and `Clone` case, the code generated for + // `derive(PartialOrd)` changes if `derive(Ord)` is also present. + // FIXME(makai410): this also doesn't work with `#[derive(PartialOrd)] #[derive(Ord)]`. + if entry.has_derive_ord || self.has_derive_ord(parent_scope.expansion) { + self.containers_deriving_ord.insert(expn_id); + } assert!(self.derive_data.is_empty()); self.derive_data = derive_data; Ok(()) diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.rs b/tests/mir-opt/pre-codegen/derived_ord_debug.rs index 82851509d666f..f37e7ac7f5614 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.rs +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.rs @@ -10,8 +10,8 @@ pub struct MultiField(char, i16); // EMIT_MIR derived_ord_debug.{impl#1}-cmp.runtime-optimized.after.mir // CHECK-LABEL: partial_cmp(_1: &MultiField, _2: &MultiField) -> Option -// CHECK: = ::partial_cmp( -// CHECK: = ::partial_cmp( +// CHECK: = ::cmp( +// CHECK: = Option::::Some( // CHECK-LABEL: cmp(_1: &MultiField, _2: &MultiField) -> std::cmp::Ordering // CHECK: = ::cmp( diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir index cca704662dd70..94c79b30e4377 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-unwind.mir @@ -4,49 +4,14 @@ fn ::partial_cmp(_1: &MultiField, debug self => _1; debug other => _2; let mut _0: std::option::Option; - let _3: &char; - let _4: &char; - let mut _5: std::option::Option; - let mut _6: isize; - let mut _7: i8; - let _8: &i16; - let _9: &i16; - scope 1 { - debug cmp => _5; - } + let mut _3: std::cmp::Ordering; bb0: { - _3 = &((*_1).0: char); - _4 = &((*_2).0: char); - _5 = ::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind continue]; + _3 = ::cmp(copy _1, copy _2) -> [return: bb1, unwind continue]; } bb1: { - _6 = discriminant(_5); - switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6]; - } - - bb2: { - _7 = discriminant(((_5 as Some).0: std::cmp::Ordering)); - switchInt(move _7) -> [0: bb3, otherwise: bb4]; - } - - bb3: { - _8 = &((*_1).1: i16); - _9 = &((*_2).1: i16); - _0 = ::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind continue]; - } - - bb4: { - _0 = copy _5; - goto -> bb5; - } - - bb5: { + _0 = Option::::Some(move _3); return; } - - bb6: { - unreachable; - } } diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 94e8b886436df..6b65aff93625d 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -72,7 +72,7 @@ impl ::core::cmp::PartialOrd for Empty { #[inline] fn partial_cmp(&self, other: &Empty) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -151,11 +151,7 @@ impl ::core::cmp::PartialOrd for Point { #[inline] fn partial_cmp(&self, other: &Point) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&self.x, &other.x) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - ::core::cmp::PartialOrd::partial_cmp(&self.y, &other.y), - cmp => cmp, - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -239,13 +235,7 @@ impl ::core::cmp::PartialOrd for PackedPoint { #[inline] fn partial_cmp(&self, other: &PackedPoint) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&{ self.x }, &{ other.x }) - { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - ::core::cmp::PartialOrd::partial_cmp(&{ self.y }, - &{ other.y }), - cmp => cmp, - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -322,7 +312,7 @@ impl ::core::cmp::PartialOrd for TupleSingleField { #[inline] fn partial_cmp(&self, other: &TupleSingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -397,7 +387,7 @@ impl ::core::cmp::PartialOrd for SingleField { #[inline] fn partial_cmp(&self, other: &SingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.foo, &other.foo) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -502,47 +492,7 @@ impl ::core::cmp::PartialOrd for Big { #[inline] fn partial_cmp(&self, other: &Big) -> ::core::option::Option<::core::cmp::Ordering> { - match ::core::cmp::PartialOrd::partial_cmp(&self.b1, &other.b1) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) => - match ::core::cmp::PartialOrd::partial_cmp(&self.b2, - &other.b2) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b3, - &other.b3) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b4, - &other.b4) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b5, - &other.b5) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b6, - &other.b6) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - match ::core::cmp::PartialOrd::partial_cmp(&self.b7, - &other.b7) { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => - ::core::cmp::PartialOrd::partial_cmp(&self.b8, &other.b8), - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - }, - cmp => cmp, - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -766,7 +716,7 @@ impl ::core::cmp::PartialOrd for Unsized { #[inline] fn partial_cmp(&self, other: &Unsized) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1066,7 +1016,7 @@ impl ::core::cmp::PartialOrd for Enum0 { #[inline] fn partial_cmp(&self, other: &Enum0) -> ::core::option::Option<::core::cmp::Ordering> { - match *self {} + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1138,10 +1088,7 @@ impl ::core::cmp::PartialOrd for Enum1 { #[inline] fn partial_cmp(&self, other: &Enum1) -> ::core::option::Option<::core::cmp::Ordering> { - match (self, other) { - (Enum1::Single { x: __self_0 }, Enum1::Single { x: __arg1_0 }) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1202,7 +1149,7 @@ impl ::core::cmp::PartialOrd for Fieldless1 { #[inline] fn partial_cmp(&self, other: &Fieldless1) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1279,9 +1226,7 @@ impl ::core::cmp::PartialOrd for Fieldless { #[inline] fn partial_cmp(&self, other: &Fieldless) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, &__arg1_discr) + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1393,23 +1338,7 @@ impl ::core::cmp::PartialOrd for Mixed { #[inline] fn partial_cmp(&self, other: &Mixed) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - match (self, other) { - (Mixed::R(__self_0), Mixed::R(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Mixed::S { d1: __self_0, d2: __self_1 }, Mixed::S { - d1: __arg1_0, d2: __arg1_1 }) => - match ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0) - { - ::core::option::Option::Some(::core::cmp::Ordering::Equal) - => ::core::cmp::PartialOrd::partial_cmp(__self_1, __arg1_1), - cmp => cmp, - }, - _ => - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, - &__arg1_discr), - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] @@ -1591,19 +1520,7 @@ impl ::core::cmp::PartialOrd for Fielded { #[inline] fn partial_cmp(&self, other: &Fielded) -> ::core::option::Option<::core::cmp::Ordering> { - let __self_discr = ::core::intrinsics::discriminant_value(self); - let __arg1_discr = ::core::intrinsics::discriminant_value(other); - match (self, other) { - (Fielded::X(__self_0), Fielded::X(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Fielded::Y(__self_0), Fielded::Y(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - (Fielded::Z(__self_0), Fielded::Z(__arg1_0)) => - ::core::cmp::PartialOrd::partial_cmp(__self_0, __arg1_0), - _ => - ::core::cmp::PartialOrd::partial_cmp(&__self_discr, - &__arg1_discr), - } + ::core::option::Option::Some(self.cmp(other)) } } #[automatically_derived] diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs index 17aa750332c40..6ff81739cd9a8 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.rs +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.rs @@ -18,8 +18,6 @@ struct StructA(String); //~| ERROR: cannot move out of a shared reference [E0507] //~| ERROR: cannot move out of a shared reference [E0507] //~| ERROR: cannot move out of a shared reference [E0507] -//~| ERROR: cannot move out of a shared reference [E0507] -//~| ERROR: cannot move out of a shared reference [E0507] // Unrelated impl: additinal diagnostic should NOT be emitted diff --git a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr index e857b308d531f..a40068171212e 100644 --- a/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr +++ b/tests/ui/derives/deriving-with-repr-packed-move-errors.stderr @@ -29,27 +29,6 @@ LL | struct StructA(String); = note: `#[derive(PartialEq)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error[E0507]: cannot move out of a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 - | -LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] - | ---------- in this derive macro expansion -LL | struct StructA(String); - | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait - | - = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - -error[E0507]: cannot move out of a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 - | -LL | #[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Default)] - | ---------- in this derive macro expansion -LL | struct StructA(String); - | ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait - | - = note: `#[derive(PartialOrd)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` - error[E0507]: cannot move out of a shared reference --> $DIR/deriving-with-repr-packed-move-errors.rs:13:16 | @@ -92,7 +71,7 @@ LL | struct StructA(String); = note: `#[derive(Clone)]` triggers a move because taking references to the fields of a packed struct is undefined behaviour error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:28:9 + --> $DIR/deriving-with-repr-packed-move-errors.rs:26:9 | LL | self.0 | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -103,7 +82,7 @@ LL | self.0.clone() | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:38:20 + --> $DIR/deriving-with-repr-packed-move-errors.rs:36:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -114,7 +93,7 @@ LL | let x = &{ self.0.clone() }; | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:45:12 + --> $DIR/deriving-with-repr-packed-move-errors.rs:43:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -125,7 +104,7 @@ LL | ({ self.0.clone() }) == ({ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:45:28 + --> $DIR/deriving-with-repr-packed-move-errors.rs:43:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -136,7 +115,7 @@ LL | ({ self.0 }) == ({ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:53:36 + --> $DIR/deriving-with-repr-packed-move-errors.rs:51:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -147,7 +126,7 @@ LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:53:49 + --> $DIR/deriving-with-repr-packed-move-errors.rs:51:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -158,7 +137,7 @@ LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:68:20 + --> $DIR/deriving-with-repr-packed-move-errors.rs:66:20 | LL | let x = &{ self.0 }; | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -169,7 +148,7 @@ LL | let x = &{ self.0.clone() }; | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:75:12 + --> $DIR/deriving-with-repr-packed-move-errors.rs:73:12 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -180,7 +159,7 @@ LL | ({ self.0.clone() }) == ({ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:75:28 + --> $DIR/deriving-with-repr-packed-move-errors.rs:73:28 | LL | ({ self.0 }) == ({ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -191,7 +170,7 @@ LL | ({ self.0 }) == ({ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `self` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:83:36 + --> $DIR/deriving-with-repr-packed-move-errors.rs:81:36 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^ move occurs because `self.0` has type `String`, which does not implement the `Copy` trait @@ -202,7 +181,7 @@ LL | PartialOrd::partial_cmp(&{ self.0.clone() }, &{ other.0 }) | ++++++++ error[E0507]: cannot move out of `other` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:83:49 + --> $DIR/deriving-with-repr-packed-move-errors.rs:81:49 | LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0 }) | ^^^^^^^ move occurs because `other.0` has type `String`, which does not implement the `Copy` trait @@ -213,7 +192,7 @@ LL | PartialOrd::partial_cmp(&{ self.0 }, &{ other.0.clone() }) | ++++++++ error[E0507]: cannot move out of `arg` which is behind a shared reference - --> $DIR/deriving-with-repr-packed-move-errors.rs:92:5 + --> $DIR/deriving-with-repr-packed-move-errors.rs:90:5 | LL | arg.0 | ^^^^^ move occurs because `arg.0` has type `String`, which does not implement the `Copy` trait @@ -223,6 +202,6 @@ help: consider cloning the value if the performance cost is acceptable LL | arg.0.clone() | ++++++++ -error: aborting due to 21 previous errors +error: aborting due to 19 previous errors For more information about this error, try `rustc --explain E0507`. diff --git a/tests/ui/range/range_traits-1.rs b/tests/ui/range/range_traits-1.rs index e28e47435c2c2..63e22b7420368 100644 --- a/tests/ui/range/range_traits-1.rs +++ b/tests/ui/range/range_traits-1.rs @@ -3,23 +3,17 @@ use std::ops::*; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] struct AllTheRanges { a: Range, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord b: RangeTo, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord c: RangeFrom, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord d: RangeFull, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord e: RangeInclusive, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord f: RangeToInclusive, - //~^ ERROR can't compare - //~| ERROR Ord + //~^ ERROR Ord } fn main() {} diff --git a/tests/ui/range/range_traits-1.stderr b/tests/ui/range/range_traits-1.stderr index ab1035778cd6c..3a200adfc96f8 100644 --- a/tests/ui/range/range_traits-1.stderr +++ b/tests/ui/range/range_traits-1.stderr @@ -1,69 +1,3 @@ -error[E0277]: can't compare `std::ops::Range` with `std::ops::Range` - --> $DIR/range_traits-1.rs:5:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -LL | struct AllTheRanges { -LL | a: Range, - | ^^^^^^^^^^^^^^^ no implementation for `std::ops::Range < std::ops::Range` and `std::ops::Range > std::ops::Range` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::Range` - -error[E0277]: can't compare `std::ops::RangeTo` with `std::ops::RangeTo` - --> $DIR/range_traits-1.rs:8:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | b: RangeTo, - | ^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeTo < std::ops::RangeTo` and `std::ops::RangeTo > std::ops::RangeTo` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeTo` - -error[E0277]: can't compare `std::ops::RangeFrom` with `std::ops::RangeFrom` - --> $DIR/range_traits-1.rs:11:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | c: RangeFrom, - | ^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeFrom < std::ops::RangeFrom` and `std::ops::RangeFrom > std::ops::RangeFrom` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFrom` - -error[E0277]: can't compare `std::ops::RangeFull` with `std::ops::RangeFull` - --> $DIR/range_traits-1.rs:14:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | d: RangeFull, - | ^^^^^^^^^^^^ no implementation for `std::ops::RangeFull < std::ops::RangeFull` and `std::ops::RangeFull > std::ops::RangeFull` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeFull` - -error[E0277]: can't compare `std::ops::RangeInclusive` with `std::ops::RangeInclusive` - --> $DIR/range_traits-1.rs:17:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | e: RangeInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeInclusive < std::ops::RangeInclusive` and `std::ops::RangeInclusive > std::ops::RangeInclusive` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeInclusive` - -error[E0277]: can't compare `std::ops::RangeToInclusive` with `std::ops::RangeToInclusive` - --> $DIR/range_traits-1.rs:20:5 - | -LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] - | ---------- in this derive macro expansion -... -LL | f: RangeToInclusive, - | ^^^^^^^^^^^^^^^^^^^^^^^^^^ no implementation for `std::ops::RangeToInclusive < std::ops::RangeToInclusive` and `std::ops::RangeToInclusive > std::ops::RangeToInclusive` - | - = help: the trait `PartialOrd` is not implemented for `std::ops::RangeToInclusive` - error[E0277]: the trait bound `std::ops::Range: Ord` is not satisfied --> $DIR/range_traits-1.rs:5:5 | @@ -74,7 +8,7 @@ LL | a: Range, | ^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::Range` error[E0277]: the trait bound `std::ops::RangeTo: Ord` is not satisfied - --> $DIR/range_traits-1.rs:8:5 + --> $DIR/range_traits-1.rs:7:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -83,7 +17,7 @@ LL | b: RangeTo, | ^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeTo` error[E0277]: the trait bound `std::ops::RangeFrom: Ord` is not satisfied - --> $DIR/range_traits-1.rs:11:5 + --> $DIR/range_traits-1.rs:9:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -92,7 +26,7 @@ LL | c: RangeFrom, | ^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFrom` error[E0277]: the trait bound `std::ops::RangeFull: Ord` is not satisfied - --> $DIR/range_traits-1.rs:14:5 + --> $DIR/range_traits-1.rs:11:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -101,7 +35,7 @@ LL | d: RangeFull, | ^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeFull` error[E0277]: the trait bound `std::ops::RangeInclusive: Ord` is not satisfied - --> $DIR/range_traits-1.rs:17:5 + --> $DIR/range_traits-1.rs:13:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -110,7 +44,7 @@ LL | e: RangeInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeInclusive` error[E0277]: the trait bound `std::ops::RangeToInclusive: Ord` is not satisfied - --> $DIR/range_traits-1.rs:20:5 + --> $DIR/range_traits-1.rs:15:5 | LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] | --- in this derive macro expansion @@ -118,6 +52,6 @@ LL | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] LL | f: RangeToInclusive, | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Ord` is not implemented for `std::ops::RangeToInclusive` -error: aborting due to 12 previous errors +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index e46e7e02862fa..ba2f840f72593 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -3,7 +3,6 @@ macro-stats MACRO EXPANSION STATS: macro_stats macro-stats Macro Name Uses Lines Avg Lines Bytes Avg Bytes macro-stats ----------------------------------------------------------------------------------- macro-stats #[derive(Clone)] 8 67 8.4 1_879 234.9 -macro-stats #[derive(PartialOrd)] 1 17 17.0 675 675.0 macro-stats #[derive(Hash)] 2 17 8.5 565 282.5 macro-stats q! 1 26 26.0 519 519.0 macro-stats #[derive(Ord)] 1 15 15.0 503 503.0 @@ -11,6 +10,7 @@ macro-stats #[derive(Default)] 2 16 8.0 macro-stats #[derive(Eq)] 1 11 11.0 312 312.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 +macro-stats #[derive(PartialOrd)] 1 8 8.0 235 235.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 macro-stats p! 1 3 3.0 32 32.0 macro-stats trait_impl_tys! 1 2 2.0 28 28.0 diff --git a/tests/ui/type/pattern_types/derives_fail.rs b/tests/ui/type/pattern_types/derives_fail.rs index a3fbad6672071..a7d6a49c4b2d0 100644 --- a/tests/ui/type/pattern_types/derives_fail.rs +++ b/tests/ui/type/pattern_types/derives_fail.rs @@ -14,7 +14,6 @@ struct Nanoseconds(NanoI32); //~| ERROR: the trait bound `(i32) is 0..=999999999: Ord` is not satisfied //~| ERROR: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied //~| ERROR: the trait bound `(i32) is 0..=999999999: Default` is not satisfied -//~| ERROR: can't compare `(i32) is 0..=999999999` with `_` //~| ERROR: `==` cannot be applied type NanoI32 = crate::pattern_type!(i32 is 0..=999_999_999); diff --git a/tests/ui/type/pattern_types/derives_fail.stderr b/tests/ui/type/pattern_types/derives_fail.stderr index 45c9bae1f2806..b79b34adda52f 100644 --- a/tests/ui/type/pattern_types/derives_fail.stderr +++ b/tests/ui/type/pattern_types/derives_fail.stderr @@ -37,17 +37,6 @@ LL | #[repr(transparent)] LL | struct Nanoseconds(NanoI32); | ^^^^^^^ the trait `Ord` is not implemented for `(i32) is 0..=999999999` -error[E0277]: can't compare `(i32) is 0..=999999999` with `_` - --> $DIR/derives_fail.rs:11:20 - | -LL | #[derive(Clone, Copy, PartialEq, Eq, Debug, Ord, PartialOrd, Hash, Default)] - | ---------- in this derive macro expansion -LL | #[repr(transparent)] -LL | struct Nanoseconds(NanoI32); - | ^^^^^^^ no implementation for `(i32) is 0..=999999999 < _` and `(i32) is 0..=999999999 > _` - | - = help: the trait `PartialOrd<_>` is not implemented for `(i32) is 0..=999999999` - error[E0277]: the trait bound `(i32) is 0..=999999999: Hash` is not satisfied --> $DIR/derives_fail.rs:11:20 | @@ -66,7 +55,7 @@ LL | #[repr(transparent)] LL | struct Nanoseconds(NanoI32); | ^^^^^^^ the trait `Default` is not implemented for `(i32) is 0..=999999999` -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors Some errors have detailed explanations: E0277, E0369. For more information about an error, try `rustc --explain E0277`. From 802938ca79a3442ed85e6a01f80db6b718d0bfcf Mon Sep 17 00:00:00 2001 From: Makai Date: Wed, 22 Apr 2026 14:14:09 +0800 Subject: [PATCH 092/174] Use UFCS for `Ord::cmp` --- .../src/deriving/cmp/partial_ord.rs | 11 +- tests/ui/derives/deriving-all-codegen.rs | 18 +++ tests/ui/derives/deriving-all-codegen.stdout | 112 ++++++++++++++++-- tests/ui/stats/macro-stats.stderr | 2 +- 4 files changed, 122 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 57387e2407f45..fcb6cb605e158 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -100,15 +100,12 @@ pub(crate) fn expand_deriving_partial_ord( // Special case for the type deriving both `PartialOrd` and `Ord`. Builds: // ``` -// Some(self.cmp(other)) +// Some(::core::cmp::Ord::cmp(self, other)) // ``` fn cs_partial_cmp_simple(cx: &ExtCtxt<'_>, span: Span, other_expr: Box) -> BlockOrExpr { - let cmp_expr = cx.expr_method_call( - span, - cx.expr_self(span), - Ident::new(sym::cmp, span), - thin_vec![other_expr], - ); + let ord_cmp_path = cx.std_path(&[sym::cmp, sym::Ord, sym::cmp]); + let cmp_expr = + cx.expr_call_global(span, ord_cmp_path, thin_vec![cx.expr_self(span), other_expr]); BlockOrExpr::new_expr(cx.expr_some(span, cmp_expr)) } diff --git a/tests/ui/derives/deriving-all-codegen.rs b/tests/ui/derives/deriving-all-codegen.rs index 9f21831960499..343d4095da470 100644 --- a/tests/ui/derives/deriving-all-codegen.rs +++ b/tests/ui/derives/deriving-all-codegen.rs @@ -235,3 +235,21 @@ struct FooCopyAndClone(i32); #[derive(Clone)] #[derive(Copy)] struct FooCloneAndCopy(i32); + +#[derive(PartialOrd, Ord)] +struct FooPartialOrdOrd(i32); + +#[derive(Ord, PartialOrd)] +struct FooOrdPartialOrd(i32); + +#[derive(Ord)] +#[derive(PartialOrd)] +struct FooOrdBeforePartialOrd(i32); + +// FIXME: this case should also have a trivial `PartialOrd` impl. +#[derive(PartialOrd)] +#[derive(Ord)] +struct FooPartialOrdBeforeOrd(i32); + +#[derive(PartialOrd, Ord)] +struct UnitStruct; diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 6b65aff93625d..7221f83ce32f6 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -72,7 +72,7 @@ impl ::core::cmp::PartialOrd for Empty { #[inline] fn partial_cmp(&self, other: &Empty) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -151,7 +151,7 @@ impl ::core::cmp::PartialOrd for Point { #[inline] fn partial_cmp(&self, other: &Point) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -235,7 +235,7 @@ impl ::core::cmp::PartialOrd for PackedPoint { #[inline] fn partial_cmp(&self, other: &PackedPoint) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -312,7 +312,7 @@ impl ::core::cmp::PartialOrd for TupleSingleField { #[inline] fn partial_cmp(&self, other: &TupleSingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -387,7 +387,7 @@ impl ::core::cmp::PartialOrd for SingleField { #[inline] fn partial_cmp(&self, other: &SingleField) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -492,7 +492,7 @@ impl ::core::cmp::PartialOrd for Big { #[inline] fn partial_cmp(&self, other: &Big) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -716,7 +716,7 @@ impl ::core::cmp::PartialOrd for Unsized { #[inline] fn partial_cmp(&self, other: &Unsized) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1016,7 +1016,7 @@ impl ::core::cmp::PartialOrd for Enum0 { #[inline] fn partial_cmp(&self, other: &Enum0) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1088,7 +1088,7 @@ impl ::core::cmp::PartialOrd for Enum1 { #[inline] fn partial_cmp(&self, other: &Enum1) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1149,7 +1149,7 @@ impl ::core::cmp::PartialOrd for Fieldless1 { #[inline] fn partial_cmp(&self, other: &Fieldless1) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1226,7 +1226,7 @@ impl ::core::cmp::PartialOrd for Fieldless { #[inline] fn partial_cmp(&self, other: &Fieldless) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1338,7 +1338,7 @@ impl ::core::cmp::PartialOrd for Mixed { #[inline] fn partial_cmp(&self, other: &Mixed) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1520,7 +1520,7 @@ impl ::core::cmp::PartialOrd for Fielded { #[inline] fn partial_cmp(&self, other: &Fielded) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(self.cmp(other)) + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) } } #[automatically_derived] @@ -1766,3 +1766,89 @@ impl ::core::clone::Clone for FooCloneAndCopy { FooCloneAndCopy(::core::clone::Clone::clone(&self.0)) } } + +struct FooPartialOrdOrd(i32); +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooPartialOrdOrd { + #[inline] + fn partial_cmp(&self, other: &FooPartialOrdOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for FooPartialOrdOrd { + #[inline] + fn cmp(&self, other: &FooPartialOrdOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +struct FooOrdPartialOrd(i32); +#[automatically_derived] +impl ::core::cmp::Ord for FooOrdPartialOrd { + #[inline] + fn cmp(&self, other: &FooOrdPartialOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooOrdPartialOrd { + #[inline] + fn partial_cmp(&self, other: &FooOrdPartialOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} + +struct FooOrdBeforePartialOrd(i32); +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooOrdBeforePartialOrd { + #[inline] + fn partial_cmp(&self, other: &FooOrdBeforePartialOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for FooOrdBeforePartialOrd { + #[inline] + fn cmp(&self, other: &FooOrdBeforePartialOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} + +// FIXME: this case should also have a trivial `PartialOrd` impl. +struct FooPartialOrdBeforeOrd(i32); +#[automatically_derived] +impl ::core::cmp::Ord for FooPartialOrdBeforeOrd { + #[inline] + fn cmp(&self, other: &FooPartialOrdBeforeOrd) -> ::core::cmp::Ordering { + ::core::cmp::Ord::cmp(&self.0, &other.0) + } +} +#[automatically_derived] +impl ::core::cmp::PartialOrd for FooPartialOrdBeforeOrd { + #[inline] + fn partial_cmp(&self, other: &FooPartialOrdBeforeOrd) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::cmp::PartialOrd::partial_cmp(&self.0, &other.0) + } +} + +struct UnitStruct; +#[automatically_derived] +impl ::core::cmp::PartialOrd for UnitStruct { + #[inline] + fn partial_cmp(&self, other: &UnitStruct) + -> ::core::option::Option<::core::cmp::Ordering> { + ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + } +} +#[automatically_derived] +impl ::core::cmp::Ord for UnitStruct { + #[inline] + fn cmp(&self, other: &UnitStruct) -> ::core::cmp::Ordering { + ::core::cmp::Ordering::Equal + } +} diff --git a/tests/ui/stats/macro-stats.stderr b/tests/ui/stats/macro-stats.stderr index ba2f840f72593..c70895067eb5f 100644 --- a/tests/ui/stats/macro-stats.stderr +++ b/tests/ui/stats/macro-stats.stderr @@ -10,7 +10,7 @@ macro-stats #[derive(Default)] 2 16 8.0 macro-stats #[derive(Eq)] 1 11 11.0 312 312.0 macro-stats #[derive(Debug)] 1 8 8.0 277 277.0 macro-stats #[derive(PartialEq)] 1 9 9.0 267 267.0 -macro-stats #[derive(PartialOrd)] 1 8 8.0 235 235.0 +macro-stats #[derive(PartialOrd)] 1 8 8.0 254 254.0 macro-stats #[derive(Copy)] 1 2 2.0 61 61.0 macro-stats p! 1 3 3.0 32 32.0 macro-stats trait_impl_tys! 1 2 2.0 28 28.0 From d436194c35447f91965eb383ffb2e199bfc628cf Mon Sep 17 00:00:00 2001 From: Makai Date: Wed, 22 Apr 2026 14:46:15 +0800 Subject: [PATCH 093/174] Avoid regression in `derive(PartialOrd)` for unit structs --- .../src/deriving/cmp/partial_ord.rs | 38 +++++++++---------- tests/ui/derives/deriving-all-codegen.stdout | 4 +- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index fcb6cb605e158..b618580d8afb7 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -1,7 +1,7 @@ use rustc_ast::{ExprKind, ItemKind, MetaItem, PatKind, Safety, ast}; use rustc_expand::base::{Annotatable, ExtCtxt}; use rustc_span::{Ident, Span, sym}; -use thin_vec::thin_vec; +use thin_vec::{ThinVec, thin_vec}; use crate::deriving::generic::ty::*; use crate::deriving::generic::*; @@ -41,33 +41,33 @@ pub(crate) fn expand_deriving_partial_ord( } else { true }; - let substructure = combine_substructure(Box::new(|cx, span, substr| { + + let container_id = cx.current_expansion.id.expn_data().parent.expect_local(); + let has_derive_ord = cx.resolver.has_derive_ord(container_id); + let is_simple_candidate = |params: &ThinVec| -> bool { + has_derive_ord + && !params.iter().any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) + }; + + let default_substructure = combine_substructure(Box::new(|cx, span, substr| { cs_partial_cmp(cx, span, substr, discr_then_data) })); + let simple_substructure = combine_substructure(Box::new(|cx, span, _| { + cs_partial_cmp_simple(cx, span, cx.expr_ident(span, Ident::new(sym::other, span))) + })); let (is_simple, substructure) = match item { Annotatable::Item(annitem) => match &annitem.kind { + // For unit structs, the default generated code is better. + ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure), ItemKind::Struct(_, ast::Generics { params, .. }, _) | ItemKind::Enum(_, ast::Generics { params, .. }, _) - if let container_id = cx.current_expansion.id.expn_data().parent.expect_local() - && cx.resolver.has_derive_ord(container_id) - && !params - .iter() - .any(|param| matches!(param.kind, ast::GenericParamKind::Type { .. })) => + if is_simple_candidate(params) => { - ( - true, - combine_substructure(Box::new(|cx, span, _| { - cs_partial_cmp_simple( - cx, - span, - cx.expr_ident(span, Ident::new(sym::other, span)), - ) - })), - ) + (true, simple_substructure) } - _ => (false, substructure), + _ => (false, default_substructure), }, - _ => (false, substructure), + _ => (false, default_substructure), }; let partial_cmp_def = MethodDef { diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 7221f83ce32f6..55c1286800c54 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -72,7 +72,7 @@ impl ::core::cmp::PartialOrd for Empty { #[inline] fn partial_cmp(&self, other: &Empty) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } #[automatically_derived] @@ -1842,7 +1842,7 @@ impl ::core::cmp::PartialOrd for UnitStruct { #[inline] fn partial_cmp(&self, other: &UnitStruct) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } #[automatically_derived] From caabc4b695e462dc11426eddbe8d8eb76db9a176 Mon Sep 17 00:00:00 2001 From: Makai Date: Wed, 22 Apr 2026 15:11:07 +0800 Subject: [PATCH 094/174] Avoid regression in `derive(PartialOrd)` for zero-variant enums --- .../rustc_builtin_macros/src/deriving/cmp/partial_ord.rs | 5 ++++- tests/ui/derives/deriving-all-codegen.stdout | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index b618580d8afb7..5a66c2871db2e 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -57,8 +57,11 @@ pub(crate) fn expand_deriving_partial_ord( })); let (is_simple, substructure) = match item { Annotatable::Item(annitem) => match &annitem.kind { - // For unit structs, the default generated code is better. + // For unit structs/zero-variant enums, the default generated code is better. ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure), + ItemKind::Enum(.., enum_def) if enum_def.variants.is_empty() => { + (false, default_substructure) + } ItemKind::Struct(_, ast::Generics { params, .. }, _) | ItemKind::Enum(_, ast::Generics { params, .. }, _) if is_simple_candidate(params) => diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 55c1286800c54..0fbcda77bba90 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -1016,7 +1016,7 @@ impl ::core::cmp::PartialOrd for Enum0 { #[inline] fn partial_cmp(&self, other: &Enum0) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + match *self {} } } #[automatically_derived] From 018ac99bc091116cabe17b5d989ce2a2b7071e9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Ml=C3=A1dek?= Date: Wed, 20 May 2026 00:13:35 +0200 Subject: [PATCH 095/174] tests: fix pub-priv-dep test expectation --- .../pub-priv-dep/shared_both_private.rs | 7 +++---- .../pub-priv-dep/shared_both_private.stderr | 20 +++++++++++++++++++ 2 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 tests/ui/privacy/pub-priv-dep/shared_both_private.stderr diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs index 20a4b85c01e8d..06debee1ff9e8 100644 --- a/tests/ui/privacy/pub-priv-dep/shared_both_private.rs +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.rs @@ -1,7 +1,6 @@ //@ aux-crate:priv:shared=shared.rs -//@ aux-crate:reexport=reexport.rs +//@ aux-crate:priv:reexport=reexport.rs //@ compile-flags: -Zunstable-options -//@ check-pass // A shared dependency, where a private dependency reexports a public dependency. // @@ -21,12 +20,12 @@ extern crate shared; extern crate reexport; -// FIXME: This should trigger. pub fn leaks_priv() -> shared::Shared { + //~^ ERROR type `Shared` from private dependency 'shared' in public interface shared::Shared } -// FIXME: This should trigger. pub fn leaks_priv_reexport() -> reexport::Shared { + //~^ ERROR type `Shared` from private dependency 'shared' in public interface reexport::Shared } diff --git a/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr b/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr new file mode 100644 index 0000000000000..b26e026b2d67b --- /dev/null +++ b/tests/ui/privacy/pub-priv-dep/shared_both_private.stderr @@ -0,0 +1,20 @@ +error: type `Shared` from private dependency 'shared' in public interface + --> $DIR/shared_both_private.rs:23:1 + | +LL | pub fn leaks_priv() -> shared::Shared { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the lint level is defined here + --> $DIR/shared_both_private.rs:18:9 + | +LL | #![deny(exported_private_dependencies)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: type `Shared` from private dependency 'shared' in public interface + --> $DIR/shared_both_private.rs:28:1 + | +LL | pub fn leaks_priv_reexport() -> reexport::Shared { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 2 previous errors + From 2efbdb9a47057f4397ef26557e3d1fe8e3df9fa1 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 21 May 2026 14:37:26 +0200 Subject: [PATCH 096/174] Treat THIR reborrow as rvalue during MIR place categorization --- .../src/builder/expr/as_place.rs | 10 ++--- .../src/builder/expr/category.rs | 7 +++- .../generic-reborrow-rvalue-contract.rs | 40 +++++++++++++++++++ 3 files changed, 49 insertions(+), 8 deletions(-) create mode 100644 tests/ui/reborrow/generic-reborrow-rvalue-contract.rs diff --git a/compiler/rustc_mir_build/src/builder/expr/as_place.rs b/compiler/rustc_mir_build/src/builder/expr/as_place.rs index ff7e518f91a81..e92f74722626b 100644 --- a/compiler/rustc_mir_build/src/builder/expr/as_place.rs +++ b/compiler/rustc_mir_build/src/builder/expr/as_place.rs @@ -583,6 +583,10 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { | ExprKind::ThreadLocalRef(_) | ExprKind::Call { .. } | ExprKind::ByUse { .. } + // A reborrow is an rvalue. If a place is needed for it, materialize + // the rvalue in a temporary instead of treating the reborrow + // expression itself as an assignable place. + | ExprKind::Reborrow { .. } | ExprKind::WrapUnsafeBinder { .. } => { // these are not places, so we need to make a temporary. debug_assert!(!matches!(Category::of(&expr.kind), Some(Category::Place))); @@ -590,12 +594,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { let temp = unpack!(block = this.as_temp(block, temp_lifetime, expr_id, mutability)); block.and(PlaceBuilder::from(temp)) } - ExprKind::Reborrow { .. } => { - // FIXME(reborrow): it should currently be impossible to end up evaluating a - // Reborrow expression as a place. That might not in the future, but what this then - // evaluates to requires further thought. - unreachable!(); - } } } diff --git a/compiler/rustc_mir_build/src/builder/expr/category.rs b/compiler/rustc_mir_build/src/builder/expr/category.rs index eb6a0754358d1..1a2f0a791b697 100644 --- a/compiler/rustc_mir_build/src/builder/expr/category.rs +++ b/compiler/rustc_mir_build/src/builder/expr/category.rs @@ -43,8 +43,7 @@ impl Category { | ExprKind::PlaceTypeAscription { .. } | ExprKind::ValueTypeAscription { .. } | ExprKind::PlaceUnwrapUnsafeBinder { .. } - | ExprKind::ValueUnwrapUnsafeBinder { .. } - | ExprKind::Reborrow { .. } => Some(Category::Place), + | ExprKind::ValueUnwrapUnsafeBinder { .. } => Some(Category::Place), ExprKind::LogicalOp { .. } | ExprKind::Match { .. } @@ -70,6 +69,10 @@ impl Category { | ExprKind::Repeat { .. } | ExprKind::Assign { .. } | ExprKind::AssignOp { .. } + // A reborrow expression produces a value represented in MIR as + // `Rvalue::Reborrow`. Its source may be a place, but the reborrow + // expression itself does not denote an assignable place. + | ExprKind::Reborrow { .. } | ExprKind::ThreadLocalRef(_) | ExprKind::WrapUnsafeBinder { .. } => Some(Category::Rvalue(RvalueFunc::AsRvalue)), diff --git a/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs new file mode 100644 index 0000000000000..a33091fc9632d --- /dev/null +++ b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs @@ -0,0 +1,40 @@ +//@ check-pass + +#![feature(reborrow)] + +use std::marker::{CoerceShared, Reborrow}; + +// Regression test for rust-lang/rust#156482. +// This used to ICE in MIR building when a THIR `ExprKind::Reborrow` +// was categorized as a place but `expr_as_place` treated it as unreachable. +struct Thing<'a>(&'a ()); + +impl<'a> Reborrow for Thing<'a> {} + +fn foo(x: Thing<'_>) { + let _: Thing<'_> = x; +} + +#[allow(unused)] +struct CustomMut<'a, T>(&'a mut T); +impl<'a, T> Reborrow for CustomMut<'a, T> {} +impl<'a, T> CoerceShared> for CustomMut<'a, T> {} + +#[allow(unused)] +struct CustomRef<'a, T>(&'a T); +impl<'a, T> Clone for CustomRef<'a, T> { + fn clone(&self) -> Self { + Self(self.0) + } +} +impl<'a, T> Copy for CustomRef<'a, T> {} + +fn main() { + let a = CustomMut(&mut ()); + + let _: CustomMut<'_, ()> = a; + let _: CustomMut<'_, ()> = a; + + let _: CustomRef<'_, ()> = a; + let _: CustomRef<'_, ()> = a; +} From d9ab6d6e2d90ca2c08249d1a2981402dba806ed9 Mon Sep 17 00:00:00 2001 From: Bennet Blischke Date: Thu, 21 May 2026 18:07:22 +0200 Subject: [PATCH 097/174] Correct doc of Vec::into_array: Returns Err(Self) instead of None --- library/alloc/src/vec/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index 27f85666d719a..de04eaf97226a 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1740,10 +1740,13 @@ impl Vec { } } - /// Converts the Vec into a boxed array. This conversion will discard any spare capacity, if there is any, see [`Vec::shrink_to_fit`]. + /// Converts the Vec into a boxed array. This conversion will discard any spare capacity, + /// if there is any, see [`Vec::shrink_to_fit`]. /// If you merely wish for a reference to an array, use [`as_array`](https://doc.rust-lang.org/stable/std/primitive.slice.html#method.as_array). /// - /// If `N` is not exactly equal to [`Vec::len`], then this method returns `None`. + /// # Errors + /// + /// Returns the original `Vec` in the `Err` variant if [`Vec::len`] does not equal `N`. /// /// # Examples /// From 4bd7165aa50a675e9a36b7fccb3a413d658c9c3f Mon Sep 17 00:00:00 2001 From: Adam Gemmell Date: Thu, 21 May 2026 17:13:31 +0100 Subject: [PATCH 098/174] Fix chunk count --- .../stdarch/crates/intrinsic-test/src/common/mod.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index 86269bab338df..b5f8dd5aba151 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -42,11 +42,11 @@ pub trait SupportedArchitectureTest { fn arch_flags(&self) -> Vec<&str>; fn generate_c_file(&self) { - let (chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); + let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); std::fs::create_dir_all("c_programs").unwrap(); self.intrinsics() - .par_chunks(chunk_size) + .par_chunks(max_chunk_size) .enumerate() .map(|(i, chunk)| { let c_filename = format!("c_programs/wrapper_{i}.c"); @@ -62,13 +62,13 @@ pub trait SupportedArchitectureTest { std::fs::create_dir_all("rust_programs").unwrap(); - let (chunk_size, chunk_count) = manual_chunk(self.intrinsics().len()); + let (max_chunk_size, chunk_count) = manual_chunk(self.intrinsics().len()); let mut cargo = File::create("rust_programs/Cargo.toml").unwrap(); write_bin_cargo_toml(&mut cargo, chunk_count).unwrap(); self.intrinsics() - .chunks(chunk_size) + .chunks(max_chunk_size) .enumerate() .map(|(i, chunk)| { std::fs::create_dir_all(format!("rust_programs/mod_{i}/src"))?; @@ -109,5 +109,7 @@ pub trait SupportedArchitectureTest { pub fn manual_chunk(intrinsic_count: usize) -> (usize, usize) { let ncores = std::thread::available_parallelism().unwrap().into(); - (intrinsic_count.div_ceil(ncores), ncores) + let max_intrinsics_per_chunk = intrinsic_count.div_ceil(ncores); + let number_of_chunks = intrinsic_count.div_ceil(max_intrinsics_per_chunk); + (max_intrinsics_per_chunk, number_of_chunks) } From ac4c70b3269910fd72f9c2e057427ac71345000d Mon Sep 17 00:00:00 2001 From: Adam Gemmell Date: Thu, 21 May 2026 17:13:42 +0100 Subject: [PATCH 099/174] Don't run doctests --- library/stdarch/ci/intrinsic-test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 1f3a2caf50654..ded53f1a1f332 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -57,4 +57,4 @@ case "${TARGET}" in ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" +cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" --tests From 152145546bc9ee1cbddd1837c3c010363771ec70 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 21 May 2026 22:15:03 +0200 Subject: [PATCH 100/174] Add share_trait feature gate --- compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + tests/ui/feature-gates/feature-gate-share-trait.rs | 5 +++++ 3 files changed, 8 insertions(+) create mode 100644 tests/ui/feature-gates/feature-gate-share-trait.rs diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8202f4c31c910..afb535112e11e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -711,6 +711,8 @@ declare_features! ( (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. (unstable, sanitize, "1.91.0", Some(39699)), + /// Allows use of the `Share` trait. + (unstable, share_trait, "CURRENT_RUSTC_VERSION", Some(156756)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Target features on sparc. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72339efd0a132..3182574049388 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1841,6 +1841,7 @@ symbols! { sha512_sm_x86, shadow_call_stack, shallow, + share_trait, shl, shl_assign, shorter_tail_lifetimes, diff --git a/tests/ui/feature-gates/feature-gate-share-trait.rs b/tests/ui/feature-gates/feature-gate-share-trait.rs new file mode 100644 index 0000000000000..664093948e6e0 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-share-trait.rs @@ -0,0 +1,5 @@ +//@ check-pass + +#![feature(share_trait)] + +fn main() {} From e5a67968e609c49472990768ddbc9547880490bb Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 21 May 2026 22:25:59 +0200 Subject: [PATCH 101/174] Add unstable Share trait --- library/core/src/clone.rs | 24 +++++++++ library/core/src/prelude/v1.rs | 2 + tests/ui/README.md | 4 ++ .../ui/share-trait/share-trait-no-feature.rs | 20 +++++++ .../share-trait/share-trait-no-feature.stderr | 53 +++++++++++++++++++ .../share-trait/share-trait-not-in-prelude.rs | 9 ++++ .../share-trait-not-in-prelude.stderr | 14 +++++ .../share-trait/share-trait-requires-clone.rs | 10 ++++ .../share-trait-requires-clone.stderr | 17 ++++++ tests/ui/share-trait/share-trait.rs | 28 ++++++++++ 10 files changed, 181 insertions(+) create mode 100644 tests/ui/share-trait/share-trait-no-feature.rs create mode 100644 tests/ui/share-trait/share-trait-no-feature.stderr create mode 100644 tests/ui/share-trait/share-trait-not-in-prelude.rs create mode 100644 tests/ui/share-trait/share-trait-not-in-prelude.stderr create mode 100644 tests/ui/share-trait/share-trait-requires-clone.rs create mode 100644 tests/ui/share-trait/share-trait-requires-clone.stderr create mode 100644 tests/ui/share-trait/share-trait.rs diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index f2fa6fd0ca3e9..ab7e85a6aabaf 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -290,6 +290,30 @@ pub macro Clone($item:item) { /* compiler built-in */ } +/// A trait for types whose [`Clone`] operation creates another alias to the same value. +/// +/// `Share` marks types where cloning preserves the identity of the underlying value instead of +/// creating an independent owned copy. The distinction is semantic, not cost-based: implementing +/// `Share` does not merely mean that cloning is cheap, constant-time, allocation-free, or +/// harmless. +/// +/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) for +/// implementors. +// FIXME(share_trait): The public location of `Share` is still unresolved; if libs-api confirms a +// different home, move this trait and update tests. +#[unstable(feature = "share_trait", issue = "156756")] +pub trait Share: Clone { + /// Creates another alias to the same underlying value. + /// + /// This is equivalent to calling [`Clone::clone`]. + #[unstable(feature = "share_trait", issue = "156756")] + // FIXME(share_trait): The default method body form is unresolved; keep the explicit + // `Clone::clone(self)` spelling until the tracking issue decides it. + fn share(&self) -> Self { + Clone::clone(self) + } +} + /// Trait for objects whose [`Clone`] impl is lightweight (e.g. reference-counted) /// /// Cloning an object implementing this trait should in general: diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index 6122ab12ec351..d763640bc9307 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -30,6 +30,8 @@ pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::clone::Clone; +// FIXME(share_trait): Whether `Share` belongs in the prelude is unresolved; do not re-export it +// here until that API question is decided. #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; diff --git a/tests/ui/README.md b/tests/ui/README.md index 39402f78bb5ec..386e77ae2bd3d 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -1244,6 +1244,10 @@ In this directory, multiple crates are compiled, but some of them have `inline` Tests on name shadowing. +## `tests/ui/share-trait` + +Tests for the unstable `Share` trait. + ## `tests/ui/shell-argfiles/`: `-Z shell-argfiles` command line flag The `-Zshell-argfiles` compiler flag allows argfiles to be parsed using POSIX "shell-style" quoting. When enabled, the compiler will use shlex to parse the arguments from argfiles specified with `@shell:`. diff --git a/tests/ui/share-trait/share-trait-no-feature.rs b/tests/ui/share-trait/share-trait-no-feature.rs new file mode 100644 index 0000000000000..2b4791d2eb034 --- /dev/null +++ b/tests/ui/share-trait/share-trait-no-feature.rs @@ -0,0 +1,20 @@ +use std::clone::Share; +//~^ ERROR use of unstable library feature `share_trait` + +#[derive(Clone)] +struct Alias; + +impl Share for Alias {} +//~^ ERROR use of unstable library feature `share_trait` + +fn share_generic(value: &T) -> T { + //~^ ERROR use of unstable library feature `share_trait` + value.share() + //~^ ERROR use of unstable library feature `share_trait` +} + +fn main() { + let value = Alias; + let _ = Share::share(&value); + //~^ ERROR use of unstable library feature `share_trait` +} diff --git a/tests/ui/share-trait/share-trait-no-feature.stderr b/tests/ui/share-trait/share-trait-no-feature.stderr new file mode 100644 index 0000000000000..344f5d8670f63 --- /dev/null +++ b/tests/ui/share-trait/share-trait-no-feature.stderr @@ -0,0 +1,53 @@ +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:1:5 + | +LL | use std::clone::Share; + | ^^^^^^^^^^^^^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:7:6 + | +LL | impl Share for Alias {} + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:10:21 + | +LL | fn share_generic(value: &T) -> T { + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:18:13 + | +LL | let _ = Share::share(&value); + | ^^^^^^^^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: use of unstable library feature `share_trait` + --> $DIR/share-trait-no-feature.rs:12:11 + | +LL | value.share() + | ^^^^^ + | + = note: see issue #156756 for more information + = help: add `#![feature(share_trait)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/share-trait/share-trait-not-in-prelude.rs b/tests/ui/share-trait/share-trait-not-in-prelude.rs new file mode 100644 index 0000000000000..4876fac2b8c1d --- /dev/null +++ b/tests/ui/share-trait/share-trait-not-in-prelude.rs @@ -0,0 +1,9 @@ +#![feature(share_trait)] + +#[derive(Clone)] +struct Alias; + +impl Share for Alias {} +//~^ ERROR cannot find trait `Share` in this scope + +fn main() {} diff --git a/tests/ui/share-trait/share-trait-not-in-prelude.stderr b/tests/ui/share-trait/share-trait-not-in-prelude.stderr new file mode 100644 index 0000000000000..733c8f2d2b215 --- /dev/null +++ b/tests/ui/share-trait/share-trait-not-in-prelude.stderr @@ -0,0 +1,14 @@ +error[E0405]: cannot find trait `Share` in this scope + --> $DIR/share-trait-not-in-prelude.rs:6:6 + | +LL | impl Share for Alias {} + | ^^^^^ not found in this scope + | +help: consider importing this trait + | +LL + use std::clone::Share; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0405`. diff --git a/tests/ui/share-trait/share-trait-requires-clone.rs b/tests/ui/share-trait/share-trait-requires-clone.rs new file mode 100644 index 0000000000000..a70ae3ade37d0 --- /dev/null +++ b/tests/ui/share-trait/share-trait-requires-clone.rs @@ -0,0 +1,10 @@ +#![feature(share_trait)] + +use std::clone::Share; + +struct NotClone; + +impl Share for NotClone {} +//~^ ERROR the trait bound `NotClone: Clone` is not satisfied + +fn main() {} diff --git a/tests/ui/share-trait/share-trait-requires-clone.stderr b/tests/ui/share-trait/share-trait-requires-clone.stderr new file mode 100644 index 0000000000000..ac775db690a40 --- /dev/null +++ b/tests/ui/share-trait/share-trait-requires-clone.stderr @@ -0,0 +1,17 @@ +error[E0277]: the trait bound `NotClone: Clone` is not satisfied + --> $DIR/share-trait-requires-clone.rs:7:16 + | +LL | impl Share for NotClone {} + | ^^^^^^^^ the trait `Clone` is not implemented for `NotClone` + | +note: required by a bound in `Share` + --> $SRC_DIR/core/src/clone.rs:LL:COL +help: consider annotating `NotClone` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct NotClone; + | + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/share-trait/share-trait.rs b/tests/ui/share-trait/share-trait.rs new file mode 100644 index 0000000000000..4292222d97512 --- /dev/null +++ b/tests/ui/share-trait/share-trait.rs @@ -0,0 +1,28 @@ +//@ check-pass + +#![feature(share_trait)] + +use std::clone::Share; + +#[derive(Debug, PartialEq)] +struct Alias(u8); + +impl Clone for Alias { + fn clone(&self) -> Self { + Alias(self.0 + 1) + } +} + +impl Share for Alias {} + +fn share_generic(value: &T) -> T { + value.share() +} + +fn main() { + let value = Alias(1); + + assert_eq!(Share::share(&value), Alias(2)); + assert_eq!(value.share(), Alias(2)); + assert_eq!(share_generic(&value), Alias(2)); +} From 02109ee82217c3417f5ca9ee16322eea3fb9d5a6 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 21 May 2026 22:34:04 +0200 Subject: [PATCH 102/174] Implement Share for shared references --- library/core/src/clone.rs | 7 ++- .../share-trait-non-implementors.rs | 19 ++++++ .../share-trait-non-implementors.stderr | 60 +++++++++++++++++++ tests/ui/share-trait/share-trait.rs | 12 ++++ 4 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 tests/ui/share-trait/share-trait-non-implementors.rs create mode 100644 tests/ui/share-trait/share-trait-non-implementors.stderr diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index ab7e85a6aabaf..1cc68def00cb9 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -625,7 +625,7 @@ unsafe impl CloneToUninit for crate::bstr::ByteStr { /// are implemented in `traits::SelectionContext::copy_clone_conditions()` /// in `rustc_trait_selection`. mod impls { - use super::TrivialClone; + use super::{Share, TrivialClone}; use crate::marker::PointeeSized; macro_rules! impl_clone { @@ -713,6 +713,11 @@ mod impls { #[rustc_const_unstable(feature = "const_clone", issue = "142757")] unsafe impl const TrivialClone for &T {} + #[unstable(feature = "share_trait", issue = "156756")] + impl Share for &T {} + + // FIXME(share_trait): Whether mutable references are non-candidates is unresolved; keep + // `Share` unimplemented for `&mut T` until that API question is decided. /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl !Clone for &mut T {} diff --git a/tests/ui/share-trait/share-trait-non-implementors.rs b/tests/ui/share-trait/share-trait-non-implementors.rs new file mode 100644 index 0000000000000..a53f10fb9e6b3 --- /dev/null +++ b/tests/ui/share-trait/share-trait-non-implementors.rs @@ -0,0 +1,19 @@ +#![feature(share_trait)] + +use std::clone::Share; + +fn require_share() {} + +fn main() { + require_share::<&mut i32>(); + //~^ ERROR the trait bound `&mut i32: Share` is not satisfied + + require_share::(); + //~^ ERROR the trait bound `String: Share` is not satisfied + + require_share::>(); + //~^ ERROR the trait bound `Vec: Share` is not satisfied + + require_share::>(); + //~^ ERROR the trait bound `Box: Share` is not satisfied +} diff --git a/tests/ui/share-trait/share-trait-non-implementors.stderr b/tests/ui/share-trait/share-trait-non-implementors.stderr new file mode 100644 index 0000000000000..88655d64595db --- /dev/null +++ b/tests/ui/share-trait/share-trait-non-implementors.stderr @@ -0,0 +1,60 @@ +error[E0277]: the trait bound `&mut i32: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:8:21 + | +LL | require_share::<&mut i32>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `&mut i32` + | +help: the trait `Share` is implemented for `&T` + --> $SRC_DIR/core/src/clone.rs:LL:COL + = note: `Share` is implemented for `&i32`, but not for `&mut i32` +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `String: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:11:21 + | +LL | require_share::(); + | ^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `String` + | +help: the trait `Share` is implemented for `&T` + --> $SRC_DIR/core/src/clone.rs:LL:COL +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `Vec: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:14:21 + | +LL | require_share::>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Vec` + | +help: the trait `Share` is implemented for `&T` + --> $SRC_DIR/core/src/clone.rs:LL:COL +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error[E0277]: the trait bound `Box: Share` is not satisfied + --> $DIR/share-trait-non-implementors.rs:17:21 + | +LL | require_share::>(); + | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Box` + | +help: the trait `Share` is implemented for `&T` + --> $SRC_DIR/core/src/clone.rs:LL:COL +note: required by a bound in `require_share` + --> $DIR/share-trait-non-implementors.rs:5:21 + | +LL | fn require_share() {} + | ^^^^^ required by this bound in `require_share` + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/share-trait/share-trait.rs b/tests/ui/share-trait/share-trait.rs index 4292222d97512..d909139116e00 100644 --- a/tests/ui/share-trait/share-trait.rs +++ b/tests/ui/share-trait/share-trait.rs @@ -25,4 +25,16 @@ fn main() { assert_eq!(Share::share(&value), Alias(2)); assert_eq!(value.share(), Alias(2)); assert_eq!(share_generic(&value), Alias(2)); + + let number = 3; + let shared = &number; + let shared_again = shared.share(); + let shared_fqs: &i32 = Share::share(&shared); + let shared_generic: &i32 = share_generic(&shared); + assert!(std::ptr::eq(shared, shared_again)); + assert!(std::ptr::eq(shared_fqs, shared)); + assert!(std::ptr::eq(shared_generic, shared)); + + let text: &str = "text"; + assert!(std::ptr::eq(text, text.share())); } From d082ac96d50bfdbf7017c251f00b10137bd3a2db Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Thu, 21 May 2026 23:50:46 +0200 Subject: [PATCH 103/174] Tighten initial Share trait implementation --- tests/ui/share-trait/share-trait.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/ui/share-trait/share-trait.rs b/tests/ui/share-trait/share-trait.rs index d909139116e00..7a099cd96cb85 100644 --- a/tests/ui/share-trait/share-trait.rs +++ b/tests/ui/share-trait/share-trait.rs @@ -2,7 +2,9 @@ #![feature(share_trait)] -use std::clone::Share; +extern crate core; + +use core::clone::Share; #[derive(Debug, PartialEq)] struct Alias(u8); @@ -23,6 +25,7 @@ fn main() { let value = Alias(1); assert_eq!(Share::share(&value), Alias(2)); + assert_eq!(std::clone::Share::share(&value), Alias(2)); assert_eq!(value.share(), Alias(2)); assert_eq!(share_generic(&value), Alias(2)); @@ -31,10 +34,11 @@ fn main() { let shared_again = shared.share(); let shared_fqs: &i32 = Share::share(&shared); let shared_generic: &i32 = share_generic(&shared); + assert!(std::ptr::eq(shared, shared_again)); assert!(std::ptr::eq(shared_fqs, shared)); assert!(std::ptr::eq(shared_generic, shared)); - let text: &str = "text"; - assert!(std::ptr::eq(text, text.share())); + let slice: &[i32] = &[1, 2, 3]; + assert!(std::ptr::eq(slice, slice.share())); } From fc33b6d4f46d477c608d3488e14c8ff60ccceb71 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 00:09:24 +0200 Subject: [PATCH 104/174] Add Share impl for Rc --- library/alloc/src/lib.rs | 1 + library/alloc/src/rc.rs | 8 ++++++- tests/ui/share-trait/share-trait-rc.rs | 32 ++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 1 deletion(-) create mode 100644 tests/ui/share-trait/share-trait-rc.rs diff --git a/library/alloc/src/lib.rs b/library/alloc/src/lib.rs index 7b41023ff31bf..5fe5464ab2cdd 100644 --- a/library/alloc/src/lib.rs +++ b/library/alloc/src/lib.rs @@ -144,6 +144,7 @@ #![feature(ptr_metadata)] #![feature(rev_into_inner)] #![feature(set_ptr_value)] +#![feature(share_trait)] #![feature(sized_type_properties)] #![feature(slice_from_ptr_range)] #![feature(slice_index_methods)] diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2905170d22a76..67fa09d22f77f 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -245,7 +245,7 @@ use core::any::Any; use core::cell::{Cell, CloneFromCell}; #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::{CloneToUninit, UseCloned}; +use core::clone::{CloneToUninit, Share, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -2525,6 +2525,12 @@ impl Clone for Rc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Rc {} +// FIXME(share_trait): The initial `Share` impl set is still being confirmed in +// rust-lang/rust#156756. This assumes cloning `Rc` creates a clone-as-alias +// value because the new handle points to the same allocation. +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Rc {} + #[cfg(not(no_global_oom_handling))] #[stable(feature = "rust1", since = "1.0.0")] impl Default for Rc { diff --git a/tests/ui/share-trait/share-trait-rc.rs b/tests/ui/share-trait/share-trait-rc.rs new file mode 100644 index 0000000000000..7de6936bcc9cb --- /dev/null +++ b/tests/ui/share-trait/share-trait-rc.rs @@ -0,0 +1,32 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::cell::Cell; +use std::clone::Share; +use std::rc::Rc; + +trait Value { + fn get(&self) -> i32; +} + +impl Value for Cell { + fn get(&self) -> i32 { + Cell::get(self) + } +} + +fn main() { + let value = Rc::new(Cell::new(1)); + let shared = value.share(); + + assert!(Rc::ptr_eq(&value, &shared)); + shared.set(2); + assert_eq!(value.get(), 2); + + let dyn_value: Rc = Rc::new(Cell::new(3)); + let shared_dyn_value = dyn_value.share(); + + assert!(Rc::ptr_eq(&dyn_value, &shared_dyn_value)); + assert_eq!(shared_dyn_value.get(), 3); +} From 78b0f9db850bdcfb3528df66eb51a2872d60e077 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 00:11:38 +0200 Subject: [PATCH 105/174] Add Share impl for Arc --- library/alloc/src/sync.rs | 8 ++++++- tests/ui/share-trait/share-trait-arc.rs | 31 +++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 tests/ui/share-trait/share-trait-arc.rs diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 229fcd2b429cf..75ee1787e557b 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -12,7 +12,7 @@ use core::any::Any; use core::cell::CloneFromCell; #[cfg(not(no_global_oom_handling))] use core::clone::TrivialClone; -use core::clone::{CloneToUninit, UseCloned}; +use core::clone::{CloneToUninit, Share, UseCloned}; use core::cmp::Ordering; use core::hash::{Hash, Hasher}; use core::intrinsics::abort; @@ -2436,6 +2436,12 @@ impl Clone for Arc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Arc {} +// FIXME(share_trait): The initial `Share` impl set is still being confirmed in +// rust-lang/rust#156756. This assumes cloning `Arc` creates a clone-as-alias +// value because the new handle points to the same allocation. +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Arc {} + #[stable(feature = "rust1", since = "1.0.0")] impl Deref for Arc { type Target = T; diff --git a/tests/ui/share-trait/share-trait-arc.rs b/tests/ui/share-trait/share-trait-arc.rs new file mode 100644 index 0000000000000..1a4f421caeeb9 --- /dev/null +++ b/tests/ui/share-trait/share-trait-arc.rs @@ -0,0 +1,31 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::{Arc, Mutex}; + +trait Value { + fn get(&self) -> i32; +} + +impl Value for Mutex { + fn get(&self) -> i32 { + *self.lock().unwrap() + } +} + +fn main() { + let value = Arc::new(Mutex::new(1)); + let shared = value.share(); + + assert!(Arc::ptr_eq(&value, &shared)); + *shared.lock().unwrap() = 2; + assert_eq!(*value.lock().unwrap(), 2); + + let dyn_value: Arc = Arc::new(Mutex::new(3)); + let shared_dyn_value = dyn_value.share(); + + assert!(Arc::ptr_eq(&dyn_value, &shared_dyn_value)); + assert_eq!(shared_dyn_value.get(), 3); +} From 4b32cb2b005ade0d20114e3289a50266d7040cfa Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 00:14:24 +0200 Subject: [PATCH 106/174] Add Share impl for mpsc Sender --- library/std/src/lib.rs | 1 + library/std/src/sync/mpsc.rs | 8 ++++++++ .../ui/share-trait/share-trait-mpsc-sender.rs | 19 +++++++++++++++++++ 3 files changed, 28 insertions(+) create mode 100644 tests/ui/share-trait/share-trait-mpsc-sender.rs diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index 8b863b07ce5d9..cb0f8edb7b852 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -369,6 +369,7 @@ #![feature(random)] #![feature(raw_os_error_ty)] #![feature(seek_io_take_position)] +#![feature(share_trait)] #![feature(slice_internals)] #![feature(slice_ptr_get)] #![feature(slice_range)] diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 8c40f07f0d17d..c2a3ee47fe100 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -142,6 +142,8 @@ // not exposed publicly, but if you are curious about the implementation, // that's where everything is. +use core::clone::Share; + use crate::sync::mpmc; use crate::time::{Duration, Instant}; use crate::{error, fmt}; @@ -645,6 +647,12 @@ impl Clone for Sender { } } +// FIXME(share_trait): The sender-like `Share` impl set is still being confirmed in +// rust-lang/rust#156756. This assumes cloning `Sender` creates a clone-as-alias +// value because both handles send to the same receiving endpoint. +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for Sender {} + #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for Sender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tests/ui/share-trait/share-trait-mpsc-sender.rs b/tests/ui/share-trait/share-trait-mpsc-sender.rs new file mode 100644 index 0000000000000..1939b10fbf43a --- /dev/null +++ b/tests/ui/share-trait/share-trait-mpsc-sender.rs @@ -0,0 +1,19 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::mpsc::channel; + +fn main() { + let (sender, receiver) = channel(); + let shared_sender = sender.share(); + + sender.send(1).unwrap(); + shared_sender.send(2).unwrap(); + + let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; + received.sort(); + + assert_eq!(received, [1, 2]); +} From 4a32c28276a5ebefbe48efdf4deb57eb5e3d50e3 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 00:16:29 +0200 Subject: [PATCH 107/174] Add Share impl for mpsc SyncSender --- library/std/src/sync/mpsc.rs | 6 ++++++ .../share-trait-mpsc-sync-sender.rs | 19 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 tests/ui/share-trait/share-trait-mpsc-sync-sender.rs diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index c2a3ee47fe100..95cea6df880d5 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -782,6 +782,12 @@ impl Clone for SyncSender { } } +// FIXME(share_trait): The sender-like `Share` impl set is still being confirmed in +// rust-lang/rust#156756. This assumes cloning `SyncSender` creates a clone-as-alias +// value because both handles send to the same receiving endpoint. +#[unstable(feature = "share_trait", issue = "156756")] +impl Share for SyncSender {} + #[stable(feature = "mpsc_debug", since = "1.8.0")] impl fmt::Debug for SyncSender { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { diff --git a/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs b/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs new file mode 100644 index 0000000000000..6b10d05324283 --- /dev/null +++ b/tests/ui/share-trait/share-trait-mpsc-sync-sender.rs @@ -0,0 +1,19 @@ +//@ run-pass + +#![feature(share_trait)] + +use std::clone::Share; +use std::sync::mpsc::sync_channel; + +fn main() { + let (sender, receiver) = sync_channel(2); + let shared_sender = sender.share(); + + sender.send(1).unwrap(); + shared_sender.send(2).unwrap(); + + let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; + received.sort(); + + assert_eq!(received, [1, 2]); +} From 71bcc49e5ffcd34b9bc7e8410bd5ffcf4d27607c Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 00:19:26 +0200 Subject: [PATCH 108/174] Update Share non-implementor diagnostics --- .../share-trait-non-implementors.stderr | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/ui/share-trait/share-trait-non-implementors.stderr b/tests/ui/share-trait/share-trait-non-implementors.stderr index 88655d64595db..8056e34beea55 100644 --- a/tests/ui/share-trait/share-trait-non-implementors.stderr +++ b/tests/ui/share-trait/share-trait-non-implementors.stderr @@ -4,8 +4,12 @@ error[E0277]: the trait bound `&mut i32: Share` is not satisfied LL | require_share::<&mut i32>(); | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `&mut i32` | -help: the trait `Share` is implemented for `&T` - --> $SRC_DIR/core/src/clone.rs:LL:COL + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender = note: `Share` is implemented for `&i32`, but not for `&mut i32` note: required by a bound in `require_share` --> $DIR/share-trait-non-implementors.rs:5:21 @@ -19,8 +23,12 @@ error[E0277]: the trait bound `String: Share` is not satisfied LL | require_share::(); | ^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `String` | -help: the trait `Share` is implemented for `&T` - --> $SRC_DIR/core/src/clone.rs:LL:COL + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender note: required by a bound in `require_share` --> $DIR/share-trait-non-implementors.rs:5:21 | @@ -33,8 +41,12 @@ error[E0277]: the trait bound `Vec: Share` is not satisfied LL | require_share::>(); | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Vec` | -help: the trait `Share` is implemented for `&T` - --> $SRC_DIR/core/src/clone.rs:LL:COL + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender note: required by a bound in `require_share` --> $DIR/share-trait-non-implementors.rs:5:21 | @@ -47,8 +59,12 @@ error[E0277]: the trait bound `Box: Share` is not satisfied LL | require_share::>(); | ^^^^^^^^ the nightly-only, unstable trait `Share` is not implemented for `Box` | -help: the trait `Share` is implemented for `&T` - --> $SRC_DIR/core/src/clone.rs:LL:COL + = help: the following other types implement trait `Share`: + &T + Arc + Rc + SyncSender + std::sync::mpsc::Sender note: required by a bound in `require_share` --> $DIR/share-trait-non-implementors.rs:5:21 | From 65f41518550839f9d6e37826dabc17c884b5118b Mon Sep 17 00:00:00 2001 From: Qi Deng Date: Thu, 21 May 2026 16:32:47 -0700 Subject: [PATCH 109/174] Fix reborrow_info early return skipping field validation `reborrow_info` validates that all data fields of a struct implementing `Reborrow` are either `Copy` or themselves `Reborrow`. When a field implementing `Reborrow` was found, the loop returned `Ok(())` immediately, so any remaining fields were never validated. This let a struct whose first data field is `Reborrow` and whose second field is neither `Copy` nor `Reborrow` incorrectly implement `Reborrow`. Change the early `return Ok(())` to `continue` so every field is checked. --- .../rustc_hir_analysis/src/coherence/builtin.rs | 4 ++-- .../reborrow/reborrow_multi_field_validation.rs | 15 +++++++++++++++ .../reborrow_multi_field_validation.stderr | 9 +++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 tests/ui/reborrow/reborrow_multi_field_validation.rs create mode 100644 tests/ui/reborrow/reborrow_multi_field_validation.stderr diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs index f83433c2bd503..2ba7e026461f3 100644 --- a/compiler/rustc_hir_analysis/src/coherence/builtin.rs +++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs @@ -563,8 +563,8 @@ pub(crate) fn reborrow_info<'tcx>( ) .is_ok() { - // Field implements Reborrow. - return Ok(()); + // Field implements Reborrow, check remaining fields. + continue; } // Field does not implement Reborrow: it must be Copy. diff --git a/tests/ui/reborrow/reborrow_multi_field_validation.rs b/tests/ui/reborrow/reborrow_multi_field_validation.rs new file mode 100644 index 0000000000000..9be4dd89f16c0 --- /dev/null +++ b/tests/ui/reborrow/reborrow_multi_field_validation.rs @@ -0,0 +1,15 @@ +#![feature(reborrow)] + +use std::marker::Reborrow; + +// Regression test: `reborrow_info` must validate ALL data fields, +// not just stop at the first Reborrow field. + +struct Bad<'a> { + first: &'a mut i32, + second: String, //~ ERROR the trait bound `String: Copy` is not satisfied +} + +impl<'a> Reborrow for Bad<'a> {} + +fn main() {} diff --git a/tests/ui/reborrow/reborrow_multi_field_validation.stderr b/tests/ui/reborrow/reborrow_multi_field_validation.stderr new file mode 100644 index 0000000000000..26d52818beeff --- /dev/null +++ b/tests/ui/reborrow/reborrow_multi_field_validation.stderr @@ -0,0 +1,9 @@ +error[E0277]: the trait bound `String: Copy` is not satisfied + --> $DIR/reborrow_multi_field_validation.rs:10:5 + | +LL | second: String, + | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. From 9b3cd578ab676ccab38ef22821a31b687a9245e3 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 07:13:06 +0200 Subject: [PATCH 110/174] Remove provisional Share trait FIXME notes --- library/alloc/src/rc.rs | 3 --- library/alloc/src/sync.rs | 3 --- library/core/src/clone.rs | 6 ------ library/core/src/prelude/v1.rs | 2 -- library/std/src/sync/mpsc.rs | 6 ------ 5 files changed, 20 deletions(-) diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 67fa09d22f77f..8150767339e18 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -2525,9 +2525,6 @@ impl Clone for Rc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Rc {} -// FIXME(share_trait): The initial `Share` impl set is still being confirmed in -// rust-lang/rust#156756. This assumes cloning `Rc` creates a clone-as-alias -// value because the new handle points to the same allocation. #[unstable(feature = "share_trait", issue = "156756")] impl Share for Rc {} diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 75ee1787e557b..17476a4e2b1e7 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -2436,9 +2436,6 @@ impl Clone for Arc { #[unstable(feature = "ergonomic_clones", issue = "132290")] impl UseCloned for Arc {} -// FIXME(share_trait): The initial `Share` impl set is still being confirmed in -// rust-lang/rust#156756. This assumes cloning `Arc` creates a clone-as-alias -// value because the new handle points to the same allocation. #[unstable(feature = "share_trait", issue = "156756")] impl Share for Arc {} diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 1cc68def00cb9..84d0ab6413ad3 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -299,16 +299,12 @@ pub macro Clone($item:item) { /// /// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) for /// implementors. -// FIXME(share_trait): The public location of `Share` is still unresolved; if libs-api confirms a -// different home, move this trait and update tests. #[unstable(feature = "share_trait", issue = "156756")] pub trait Share: Clone { /// Creates another alias to the same underlying value. /// /// This is equivalent to calling [`Clone::clone`]. #[unstable(feature = "share_trait", issue = "156756")] - // FIXME(share_trait): The default method body form is unresolved; keep the explicit - // `Clone::clone(self)` spelling until the tracking issue decides it. fn share(&self) -> Self { Clone::clone(self) } @@ -716,8 +712,6 @@ mod impls { #[unstable(feature = "share_trait", issue = "156756")] impl Share for &T {} - // FIXME(share_trait): Whether mutable references are non-candidates is unresolved; keep - // `Share` unimplemented for `&mut T` until that API question is decided. /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] impl !Clone for &mut T {} diff --git a/library/core/src/prelude/v1.rs b/library/core/src/prelude/v1.rs index d763640bc9307..6122ab12ec351 100644 --- a/library/core/src/prelude/v1.rs +++ b/library/core/src/prelude/v1.rs @@ -30,8 +30,6 @@ pub use crate::mem::{align_of, align_of_val, size_of, size_of_val}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::clone::Clone; -// FIXME(share_trait): Whether `Share` belongs in the prelude is unresolved; do not re-export it -// here until that API question is decided. #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use crate::cmp::{Eq, Ord, PartialEq, PartialOrd}; diff --git a/library/std/src/sync/mpsc.rs b/library/std/src/sync/mpsc.rs index 95cea6df880d5..b74f84ff465c0 100644 --- a/library/std/src/sync/mpsc.rs +++ b/library/std/src/sync/mpsc.rs @@ -647,9 +647,6 @@ impl Clone for Sender { } } -// FIXME(share_trait): The sender-like `Share` impl set is still being confirmed in -// rust-lang/rust#156756. This assumes cloning `Sender` creates a clone-as-alias -// value because both handles send to the same receiving endpoint. #[unstable(feature = "share_trait", issue = "156756")] impl Share for Sender {} @@ -782,9 +779,6 @@ impl Clone for SyncSender { } } -// FIXME(share_trait): The sender-like `Share` impl set is still being confirmed in -// rust-lang/rust#156756. This assumes cloning `SyncSender` creates a clone-as-alias -// value because both handles send to the same receiving endpoint. #[unstable(feature = "share_trait", issue = "156756")] impl Share for SyncSender {} From 60889bd9c2715745251af352b0d53f76d697d31e Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 2 May 2026 23:44:03 -0700 Subject: [PATCH 111/174] Add some basic tests for `{size,align}_of_val` --- .../intrinsics/size_and_align_of_val.rs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 tests/codegen-llvm/intrinsics/size_and_align_of_val.rs diff --git a/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs new file mode 100644 index 0000000000000..813a4ae1479ce --- /dev/null +++ b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs @@ -0,0 +1,53 @@ +//@ compile-flags: -Copt-level=3 -C no-prepopulate-passes -Z mir-opt-level=0 +//@ only-64bit (so I don't need to worry about usize) + +#![crate_type = "lib"] +#![feature(core_intrinsics)] + +// Here to have unit tests of what they actually emit and to track things like +// + +use std::intrinsics::{align_of_val, size_of_val}; + +// CHECK-LABEL: @align_of_array( +#[no_mangle] +pub unsafe fn align_of_array(x: &[u16; 7]) -> usize { + // CHECK: start: + // CHECK: %0 = alloca [8 x i8] + // CHECK: store i64 2, ptr %0 + // CHECK: [[R:%.+]] = load i64, ptr %0 + // CHECK: ret i64 [[R]] + align_of_val(x) +} + +// CHECK-LABEL: @size_of_array( +#[no_mangle] +pub unsafe fn size_of_array(x: &[u16; 7]) -> usize { + // CHECK: %0 = alloca [8 x i8] + // CHECK: store i64 14, ptr %0 + // CHECK: [[R:%.+]] = load i64, ptr %0 + // CHECK: ret i64 [[R]] + size_of_val(x) +} + +// CHECK-LABEL: @align_of_slice( +#[no_mangle] +pub unsafe fn align_of_slice(x: &[u16]) -> usize { + // CHECK: %0 = alloca [8 x i8] + // CHECK: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK: store i64 2, ptr %0 + // CHECK: [[R:%.+]] = load i64, ptr %0 + // CHECK: ret i64 [[R]] + align_of_val(x) +} + +// CHECK-LABEL: @size_of_slice( +#[no_mangle] +pub unsafe fn size_of_slice(x: &[u16]) -> usize { + // CHECK: %0 = alloca [8 x i8] + // CHECK: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK: store i64 [[SIZE]], ptr %0 + // CHECK: [[R:%.+]] = load i64, ptr %0 + // CHECK: ret i64 [[R]] + size_of_val(x) +} From c01e32073c1fe9e93c03d56f7cd635eb21ea9219 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 07:21:45 +0200 Subject: [PATCH 112/174] Document unstable Share trait semantics --- library/core/src/clone.rs | 72 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 67 insertions(+), 5 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 84d0ab6413ad3..6c2b4fa151b86 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -292,13 +292,75 @@ pub macro Clone($item:item) { /// A trait for types whose [`Clone`] operation creates another alias to the same value. /// -/// `Share` marks types where cloning preserves the identity of the underlying value instead of -/// creating an independent owned copy. The distinction is semantic, not cost-based: implementing -/// `Share` does not merely mean that cloning is cheap, constant-time, allocation-free, or -/// harmless. +/// `Share` marks types where cloning creates another handle or reference to the same logical +/// resource or shared state, rather than an independent owned value. The distinction is semantic, +/// not cost-based: implementing `Share` does not merely mean that cloning is cheap, constant-time, +/// allocation-free, or convenient. /// /// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) for -/// implementors. +/// implementors, but communicates that the resulting value aliases the same underlying resource. +/// +/// Shared references, [`Rc`](std::rc::Rc), [`Arc`](std::sync::Arc), +/// [`Sender`](std::sync::mpsc::Sender), and [`SyncSender`](std::sync::mpsc::SyncSender) are +/// examples of types that can be shared this way. Types such as [`Vec`](std::vec::Vec), +/// [`String`](std::string::String), and [`Box`](std::boxed::Box) are not `Share` even though they +/// implement `Clone`, because cloning them creates another owned value rather than another handle +/// to the same logical resource. +/// +/// # Examples +/// +/// ``` +/// #![feature(share_trait)] +/// +/// use std::cell::Cell; +/// use std::clone::Share; +/// use std::rc::Rc; +/// use std::sync::{ +/// Arc, +/// atomic::{AtomicUsize, Ordering}, +/// }; +/// +/// let value = 1; +/// let reference = &value; +/// assert!(std::ptr::eq(reference, reference.share())); +/// +/// let rc = Rc::new(Cell::new(2)); +/// let shared_rc = rc.share(); +/// assert!(Rc::ptr_eq(&rc, &shared_rc)); +/// shared_rc.set(3); +/// assert_eq!(rc.get(), 3); +/// +/// let arc = Arc::new(AtomicUsize::new(4)); +/// let shared_arc = arc.share(); +/// assert!(Arc::ptr_eq(&arc, &shared_arc)); +/// shared_arc.store(5, Ordering::Relaxed); +/// assert_eq!(arc.load(Ordering::Relaxed), 5); +/// ``` +/// +/// ``` +/// #![feature(share_trait)] +/// +/// use std::clone::Share; +/// use std::sync::mpsc::{channel, sync_channel}; +/// +/// let (sender, receiver) = channel(); +/// let shared_sender = sender.share(); +/// sender.send(1).unwrap(); +/// shared_sender.send(2).unwrap(); +/// +/// let mut received = [receiver.recv().unwrap(), receiver.recv().unwrap()]; +/// received.sort(); +/// assert_eq!(received, [1, 2]); +/// +/// let (sync_sender, sync_receiver) = sync_channel(2); +/// let shared_sync_sender = sync_sender.share(); +/// sync_sender.send(3).unwrap(); +/// shared_sync_sender.send(4).unwrap(); +/// +/// let mut received = [sync_receiver.recv().unwrap(), sync_receiver.recv().unwrap()]; +/// received.sort(); +/// assert_eq!(received, [3, 4]); +/// ``` #[unstable(feature = "share_trait", issue = "156756")] pub trait Share: Clone { /// Creates another alias to the same underlying value. From dd63bda55c67f9d55343963a508227c2f3578cf8 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Fri, 22 May 2026 00:33:25 -0700 Subject: [PATCH 113/174] Bump the version number to 1.98.0 --- src/version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version b/src/version index acbb747ac540f..783fda86436c2 100644 --- a/src/version +++ b/src/version @@ -1 +1 @@ -1.97.0 +1.98.0 From a29df2a259b7183f994b419e6db228bea7d7d12f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 22 May 2026 08:01:16 +0000 Subject: [PATCH 114/174] Rustfmt: format const trait impls to `const impl` for syntax transition --- src/tools/rustfmt/src/items.rs | 2 +- src/tools/rustfmt/src/utils.rs | 8 -------- src/tools/rustfmt/tests/source/const_trait.rs | 14 -------------- src/tools/rustfmt/tests/target/const_trait.rs | 5 +---- src/tools/rustfmt/tests/target/impls.rs | 2 +- src/tools/rustfmt/tests/target/type.rs | 2 +- 6 files changed, 4 insertions(+), 29 deletions(-) delete mode 100644 src/tools/rustfmt/tests/source/const_trait.rs diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 32f71703e019f..95b13eb4aac99 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -960,6 +960,7 @@ fn format_impl_ref_and_type( if let Some(of_trait) = of_trait.as_deref() { result.push_str(format_defaultness(of_trait.defaultness)); + result.push_str(format_constness(*constness)); result.push_str(format_safety(of_trait.safety)); } else { result.push_str(format_constness(*constness)); @@ -980,7 +981,6 @@ fn format_impl_ref_and_type( let trait_ref_overhead; if let Some(of_trait) = of_trait.as_deref() { - result.push_str(format_constness_right(*constness)); let polarity_str = match of_trait.polarity { ast::ImplPolarity::Negative(_) => "!", ast::ImplPolarity::Positive => "", diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index de72c9ce14bc3..6eb76c935a920 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -125,14 +125,6 @@ pub(crate) fn format_constness(constness: ast::Const) -> &'static str { } } -#[inline] -pub(crate) fn format_constness_right(constness: ast::Const) -> &'static str { - match constness { - ast::Const::Yes(..) => " const", - ast::Const::No => "", - } -} - #[inline] pub(crate) fn format_defaultness(defaultness: ast::Defaultness) -> &'static str { match defaultness { diff --git a/src/tools/rustfmt/tests/source/const_trait.rs b/src/tools/rustfmt/tests/source/const_trait.rs deleted file mode 100644 index 99414a74f250c..0000000000000 --- a/src/tools/rustfmt/tests/source/const_trait.rs +++ /dev/null @@ -1,14 +0,0 @@ -#![feature(trait_alias, const_trait_impl)] - -const trait Bar {} - -const trait Foo = Bar; - -impl const Bar for () {} - -// const impl gets reformatted to impl const.. for now -const impl Bar for u8 {} - -struct X; - -const impl X {} diff --git a/src/tools/rustfmt/tests/target/const_trait.rs b/src/tools/rustfmt/tests/target/const_trait.rs index 337a4cf4140e8..bb6ba1cd85b1d 100644 --- a/src/tools/rustfmt/tests/target/const_trait.rs +++ b/src/tools/rustfmt/tests/target/const_trait.rs @@ -4,10 +4,7 @@ const trait Bar {} const trait Foo = Bar; -impl const Bar for () {} - -// const impl gets reformatted to impl const.. for now -impl const Bar for u8 {} +const impl Bar for () {} struct X; diff --git a/src/tools/rustfmt/tests/target/impls.rs b/src/tools/rustfmt/tests/target/impls.rs index 99e02990e4177..71dfb5591e7ce 100644 --- a/src/tools/rustfmt/tests/target/impls.rs +++ b/src/tools/rustfmt/tests/target/impls.rs @@ -244,7 +244,7 @@ where } // #4084 -impl const std::default::Default for Struct { +const impl std::default::Default for Struct { #[inline] fn default() -> Self { Self { f: 12.5 } diff --git a/src/tools/rustfmt/tests/target/type.rs b/src/tools/rustfmt/tests/target/type.rs index 623192b72b8b7..4855f726ea3f9 100644 --- a/src/tools/rustfmt/tests/target/type.rs +++ b/src/tools/rustfmt/tests/target/type.rs @@ -153,7 +153,7 @@ const fn not_quite_const() -> i32 { ::CONST } -impl const T for U {} +const impl T for U {} fn apit(_: impl [const] T) {} From 5d394148bef3bb317038f8ffec020136656a39b1 Mon Sep 17 00:00:00 2001 From: Bryanskiy Date: Fri, 22 May 2026 12:12:44 +0300 Subject: [PATCH 115/174] Privacy: enqueue type alias --- compiler/rustc_privacy/src/lib.rs | 3 +-- .../privacy/reach-type-alias-issue-156778.rs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 tests/ui/privacy/reach-type-alias-issue-156778.rs diff --git a/compiler/rustc_privacy/src/lib.rs b/compiler/rustc_privacy/src/lib.rs index 23a8ddaa3d6da..de5c00306a736 100644 --- a/compiler/rustc_privacy/src/lib.rs +++ b/compiler/rustc_privacy/src/lib.rs @@ -783,7 +783,7 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> { } } - DefKind::TraitAlias | DefKind::Fn => { + DefKind::TraitAlias | DefKind::Fn | DefKind::TyAlias => { self.ev.queue.insert(def_id); } @@ -808,7 +808,6 @@ impl ReachEverythingInTheInterfaceVisitor<'_, '_> { // Can't be reached DefKind::Impl { .. } - | DefKind::TyAlias | DefKind::Field | DefKind::Variant | DefKind::Static { .. } diff --git a/tests/ui/privacy/reach-type-alias-issue-156778.rs b/tests/ui/privacy/reach-type-alias-issue-156778.rs new file mode 100644 index 0000000000000..4297a44038cfc --- /dev/null +++ b/tests/ui/privacy/reach-type-alias-issue-156778.rs @@ -0,0 +1,20 @@ +//@ check-pass +#![feature(lazy_type_alias)] + +use src::hidden_core; +mod src { + mod aliases { + use hidden_core::InternalStruct; + pub type ExposedType = InternalStruct; + } + pub mod hidden_core { + use super::aliases::ExposedType; + pub struct InternalStruct { + _x: T, + } + pub fn new() -> ExposedType { + InternalStruct { _x: 1.0 } + } + } +} +fn main() {} From a83eba40861930b56fd73c83d4e228f7debe73f0 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Fri, 22 May 2026 12:50:49 +0300 Subject: [PATCH 116/174] Visit delegation body under elided-infer lifetime rib --- compiler/rustc_resolve/src/late.rs | 4 +- .../ice-line-bounds-issue-148732.rs | 2 +- .../ice-line-bounds-issue-148732.stderr | 21 ++++++---- tests/ui/delegation/wrong-lifetime-rib.rs | 10 +++++ tests/ui/delegation/wrong-lifetime-rib.stderr | 40 ++++++++++++++++++- 5 files changed, 65 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 21b36f1934c3a..f4bd6354972b5 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -3937,7 +3937,9 @@ impl<'a, 'ast, 'ra, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> { //As we lower target_expr_template body to a body of a function we need a label rib (#148889) this.with_label_rib(RibKind::FnOrCoroutine, |this| { - this.visit_block(body); + this.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| { + this.visit_block(body); + }); }); }); } diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.rs b/tests/ui/delegation/ice-line-bounds-issue-148732.rs index 699e7d86f2581..0123f0c8705b0 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.rs +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.rs @@ -2,7 +2,7 @@ reuse a as b { //~^ ERROR cannot find function `a` in this scope //~| ERROR functions delegation is not yet fully implemented dbg!(b); - //~^ ERROR missing lifetime specifier + //~^ ERROR: `fn() {b}` doesn't implement `Debug` } fn main() {} diff --git a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr index c4f261181b109..eb93655beb60d 100644 --- a/tests/ui/delegation/ice-line-bounds-issue-148732.stderr +++ b/tests/ui/delegation/ice-line-bounds-issue-148732.stderr @@ -1,9 +1,3 @@ -error[E0106]: missing lifetime specifier - --> $DIR/ice-line-bounds-issue-148732.rs:4:5 - | -LL | dbg!(b); - | ^^^^^^^ expected named lifetime parameter - error[E0425]: cannot find function `a` in this scope --> $DIR/ice-line-bounds-issue-148732.rs:1:7 | @@ -25,7 +19,18 @@ LL | | } = help: add `#![feature(fn_delegation)]` to the crate attributes to enable = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date +error[E0277]: `fn() {b}` doesn't implement `Debug` + --> $DIR/ice-line-bounds-issue-148732.rs:4:5 + | +LL | reuse a as b { + | - consider calling this function +... +LL | dbg!(b); + | ^^^^^^^ the trait `Debug` is not implemented for fn item `fn() {b}` + | + = help: use parentheses to call this function: `b()` + error: aborting due to 3 previous errors -Some errors have detailed explanations: E0106, E0425, E0658. -For more information about an error, try `rustc --explain E0106`. +Some errors have detailed explanations: E0277, E0425, E0658. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/wrong-lifetime-rib.rs b/tests/ui/delegation/wrong-lifetime-rib.rs index 14d62cbaa41fa..825ac6a64008a 100644 --- a/tests/ui/delegation/wrong-lifetime-rib.rs +++ b/tests/ui/delegation/wrong-lifetime-rib.rs @@ -29,4 +29,14 @@ mod ice_156758 { } } +mod ice_156806 { + trait X {} + + impl X { //~ ERROR: expected a type, found a trait + reuse Iterator::fold { //~ ERROR: `()` is not an iterator + let _: &X; //~ ERROR: expected a type, found a trait + } + } +} + fn main() {} diff --git a/tests/ui/delegation/wrong-lifetime-rib.stderr b/tests/ui/delegation/wrong-lifetime-rib.stderr index 2c9594af581ce..07ddc94f7a4ae 100644 --- a/tests/ui/delegation/wrong-lifetime-rib.stderr +++ b/tests/ui/delegation/wrong-lifetime-rib.stderr @@ -19,6 +19,21 @@ help: you might have intended to implement this trait for a given type LL | impl X for /* Type */ { | ++++++++++++++ +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:35:10 + | +LL | impl X { + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | impl dyn X { + | +++ +help: you might have intended to implement this trait for a given type + | +LL | impl X for /* Type */ { + | ++++++++++++++ + error[E0116]: cannot define inherent `impl` for a type outside of the crate where the type is defined --> $DIR/wrong-lifetime-rib.rs:9:5 | @@ -40,7 +55,28 @@ LL - reuse<<<&Project> :: Ty> :: Ty as Iterator>::next; LL + reuse<<<&() as Example>::Ty> :: Ty as Iterator>::next; | -error: aborting due to 4 previous errors +error[E0782]: expected a type, found a trait + --> $DIR/wrong-lifetime-rib.rs:37:21 + | +LL | let _: &X; + | ^ + | +help: you can add the `dyn` keyword if you want a trait object + | +LL | let _: &dyn X; + | +++ + +error[E0599]: `()` is not an iterator + --> $DIR/wrong-lifetime-rib.rs:36:25 + | +LL | reuse Iterator::fold { + | ^^^^ `()` is not an iterator + | + = note: the following trait bounds were not satisfied: + `(): Iterator` + which is required by `&mut (): Iterator` + +error: aborting due to 7 previous errors -Some errors have detailed explanations: E0116, E0223, E0423, E0782. +Some errors have detailed explanations: E0116, E0223, E0423, E0599, E0782. For more information about an error, try `rustc --explain E0116`. From ee30e613f3b406baa8674c06a91509c88d439b91 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman <127837395+P8L1@users.noreply.github.com> Date: Fri, 22 May 2026 11:54:09 +0200 Subject: [PATCH 117/174] Move comments to top of test file --- tests/ui/reborrow/generic-reborrow-rvalue-contract.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs index a33091fc9632d..465be1b52fb2c 100644 --- a/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs +++ b/tests/ui/reborrow/generic-reborrow-rvalue-contract.rs @@ -1,12 +1,12 @@ //@ check-pass +// Regression test for rust-lang/rust#156482. +// This used to ICE in MIR building when a THIR `ExprKind::Reborrow` +// was categorized as a place but `expr_as_place` treated it as unreachable. #![feature(reborrow)] use std::marker::{CoerceShared, Reborrow}; -// Regression test for rust-lang/rust#156482. -// This used to ICE in MIR building when a THIR `ExprKind::Reborrow` -// was categorized as a place but `expr_as_place` treated it as unreachable. struct Thing<'a>(&'a ()); impl<'a> Reborrow for Thing<'a> {} From 3c3027d59a4302240595bfa506f3133219c27bb1 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 7 May 2026 18:57:42 +0900 Subject: [PATCH 118/174] Add `mut_restriction` feature --- compiler/rustc_ast_passes/src/feature_gate.rs | 1 + compiler/rustc_feature/src/unstable.rs | 2 ++ compiler/rustc_span/src/symbol.rs | 1 + 3 files changed, 4 insertions(+) diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index b43dbad611a0e..1b3ce2427c3c7 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -505,6 +505,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(more_qualified_paths, "usage of qualified paths in this context is experimental"); gate_all!(move_expr, "`move(expr)` syntax is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); + gate_all!(mut_restriction, "`mut` restrictions are experimental"); gate_all!(pin_ergonomics, "pinned reference syntax is experimental"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(return_type_notation, "return type notation is experimental"); diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index 8202f4c31c910..da45596d0b74e 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -648,6 +648,8 @@ declare_features! ( (unstable, must_not_suspend, "1.57.0", Some(83310)), /// Allows `mut ref` and `mut ref mut` identifier patterns. (incomplete, mut_ref, "1.79.0", Some(123076)), + /// Allows `mut(crate) field: Type` restrictions. + (incomplete, mut_restriction, "CURRENT_RUSTC_VERSION", Some(105077)), /// Allows using `#[naked]` on `extern "Rust"` functions. (unstable, naked_functions_rustic_abi, "1.88.0", Some(138997)), /// Allows using `#[target_feature(enable = "...")]` on `#[naked]` on functions. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 72339efd0a132..7869731ef82f7 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1338,6 +1338,7 @@ symbols! { must_use, mut_preserve_binding_mode_2024, mut_ref, + mut_restriction, mutable, naked, naked_asm, From 41c61112a7e5853c7181a00b5d68ad7c2d79c1f7 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 7 May 2026 19:11:53 +0900 Subject: [PATCH 119/174] Introduce `mut` restrictions to AST --- compiler/rustc_ast/src/ast.rs | 8 ++++++++ compiler/rustc_ast/src/ast_traits.rs | 19 +++++++++++++++---- compiler/rustc_ast/src/visit.rs | 6 ++++-- 3 files changed, 27 insertions(+), 6 deletions(-) diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index fa29fb76c51a8..af8ad425507c4 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -3582,6 +3582,13 @@ pub struct ImplRestriction { pub tokens: Option, } +#[derive(Clone, Encodable, Decodable, Debug, Walkable)] +pub struct MutRestriction { + pub kind: RestrictionKind, + pub span: Span, + pub tokens: Option, +} + #[derive(Clone, Encodable, Decodable, Debug, Walkable)] pub enum RestrictionKind { Unrestricted, @@ -3597,6 +3604,7 @@ pub struct FieldDef { pub id: NodeId, pub span: Span, pub vis: Visibility, + pub mut_restriction: MutRestriction, pub safety: Safety, pub ident: Option, diff --git a/compiler/rustc_ast/src/ast_traits.rs b/compiler/rustc_ast/src/ast_traits.rs index 73bfa0ba7ab6d..74d8ae469b3d1 100644 --- a/compiler/rustc_ast/src/ast_traits.rs +++ b/compiler/rustc_ast/src/ast_traits.rs @@ -8,8 +8,8 @@ use std::marker::PhantomData; use crate::tokenstream::LazyAttrTokenStream; use crate::{ Arm, AssocItem, AttrItem, AttrKind, AttrVec, Attribute, Block, Crate, Expr, ExprField, - FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, NodeId, Param, Pat, PatField, Path, - Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, + FieldDef, ForeignItem, GenericParam, ImplRestriction, Item, MutRestriction, NodeId, Param, Pat, + PatField, Path, Stmt, StmtKind, Ty, Variant, Visibility, WherePredicate, }; /// A trait for AST nodes having an ID. @@ -108,7 +108,8 @@ impl_has_tokens!( Path, Ty, Visibility, - ImplRestriction + ImplRestriction, + MutRestriction, ); impl_has_tokens_none!( Arm, @@ -254,7 +255,17 @@ impl_has_attrs!( Variant, WherePredicate, ); -impl_has_attrs_none!(Attribute, AttrItem, Block, Pat, Path, Ty, Visibility, ImplRestriction); +impl_has_attrs_none!( + Attribute, + AttrItem, + Block, + Pat, + Path, + Ty, + Visibility, + ImplRestriction, + MutRestriction +); impl HasAttrs for Box { const SUPPORTS_CUSTOM_INNER_ATTRS: bool = T::SUPPORTS_CUSTOM_INNER_ATTRS; diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index ed8c3787bfec4..1e96d1d52f7eb 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -582,12 +582,14 @@ macro_rules! common_visitor_and_walkers { fn visit_generics(Generics); fn visit_inline_asm(InlineAsm); fn visit_inline_asm_sym(InlineAsmSym); + fn visit_impl_restriction(ImplRestriction); //fn visit_item(Item); fn visit_label(Label); fn visit_lifetime(Lifetime, _ctxt: LifetimeCtxt); fn visit_local(Local); fn visit_mac_call(MacCall); fn visit_macro_def(MacroDef); + fn visit_mut_restriction(MutRestriction); fn visit_param_bound(GenericBound, _ctxt: BoundKind); fn visit_param(Param); fn visit_pat_field(PatField); @@ -597,7 +599,6 @@ macro_rules! common_visitor_and_walkers { fn visit_poly_trait_ref(PolyTraitRef); fn visit_precise_capturing_arg(PreciseCapturingArg); fn visit_qself(QSelf); - fn visit_impl_restriction(ImplRestriction); fn visit_trait_ref(TraitRef); fn visit_ty_pat(TyPat); fn visit_ty(Ty); @@ -1106,12 +1107,14 @@ macro_rules! common_visitor_and_walkers { pub fn walk_generics(Generics); pub fn walk_inline_asm(InlineAsm); pub fn walk_inline_asm_sym(InlineAsmSym); + pub fn walk_impl_restriction(ImplRestriction); //pub fn walk_item(Item); pub fn walk_label(Label); pub fn walk_lifetime(Lifetime); pub fn walk_local(Local); pub fn walk_mac(MacCall); pub fn walk_macro_def(MacroDef); + pub fn walk_mut_restriction(MutRestriction); pub fn walk_param_bound(GenericBound); pub fn walk_param(Param); pub fn walk_pat_field(PatField); @@ -1121,7 +1124,6 @@ macro_rules! common_visitor_and_walkers { pub fn walk_poly_trait_ref(PolyTraitRef); pub fn walk_precise_capturing_arg(PreciseCapturingArg); pub fn walk_qself(QSelf); - pub fn walk_impl_restriction(ImplRestriction); pub fn walk_trait_ref(TraitRef); pub fn walk_ty_pat(TyPat); pub fn walk_ty(Ty); From 3a6cb0519d5c6595f6815992142d66964612b8ac Mon Sep 17 00:00:00 2001 From: okaneco <47607823+okaneco@users.noreply.github.com> Date: Fri, 22 May 2026 09:15:36 -0400 Subject: [PATCH 120/174] Add `#[doc(alias = "phi")]` for float GOLDEN_RATIO constants --- library/core/src/num/f128.rs | 1 + library/core/src/num/f16.rs | 1 + library/core/src/num/f32.rs | 1 + library/core/src/num/f64.rs | 1 + 4 files changed, 4 insertions(+) diff --git a/library/core/src/num/f128.rs b/library/core/src/num/f128.rs index d381402b469f4..cd1987aa959dd 100644 --- a/library/core/src/num/f128.rs +++ b/library/core/src/num/f128.rs @@ -33,6 +33,7 @@ pub mod consts { pub const TAU: f128 = 6.28318530717958647692528676655900576839433879875021164194989_f128; /// The golden ratio (φ) + #[doc(alias = "phi")] #[unstable(feature = "f128", issue = "116909")] pub const GOLDEN_RATIO: f128 = 1.61803398874989484820458683436563811772030917980576286213545_f128; diff --git a/library/core/src/num/f16.rs b/library/core/src/num/f16.rs index c26ae17d870cc..ef8eb3c0b57a0 100644 --- a/library/core/src/num/f16.rs +++ b/library/core/src/num/f16.rs @@ -35,6 +35,7 @@ pub mod consts { pub const TAU: f16 = 6.28318530717958647692528676655900577_f16; /// The golden ratio (φ) + #[doc(alias = "phi")] #[unstable(feature = "f16", issue = "116909")] pub const GOLDEN_RATIO: f16 = 1.618033988749894848204586834365638118_f16; diff --git a/library/core/src/num/f32.rs b/library/core/src/num/f32.rs index 24c97a6491c11..2a7506f198589 100644 --- a/library/core/src/num/f32.rs +++ b/library/core/src/num/f32.rs @@ -292,6 +292,7 @@ pub mod consts { pub const TAU: f32 = 6.28318530717958647692528676655900577_f32; /// The golden ratio (φ) + #[doc(alias = "phi")] #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] pub const GOLDEN_RATIO: f32 = 1.618033988749894848204586834365638118_f32; diff --git a/library/core/src/num/f64.rs b/library/core/src/num/f64.rs index be045033a3553..8b77a3b7b0bde 100644 --- a/library/core/src/num/f64.rs +++ b/library/core/src/num/f64.rs @@ -292,6 +292,7 @@ pub mod consts { pub const TAU: f64 = 6.28318530717958647692528676655900577_f64; /// The golden ratio (φ) + #[doc(alias = "phi")] #[stable(feature = "euler_gamma_golden_ratio", since = "1.94.0")] pub const GOLDEN_RATIO: f64 = 1.618033988749894848204586834365638118_f64; From 4f12864fabd73bf01b1e175fc14a365ae76379ff Mon Sep 17 00:00:00 2001 From: Edvin Bryntesson Date: Fri, 22 May 2026 15:13:08 +0200 Subject: [PATCH 121/174] make `lint_index` field not Optional in LintExpectationId --- compiler/rustc_lint/src/expect.rs | 2 +- compiler/rustc_lint/src/levels.rs | 12 ++++-------- compiler/rustc_lint_defs/src/lib.rs | 6 +++--- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/compiler/rustc_lint/src/expect.rs b/compiler/rustc_lint/src/expect.rs index 5364a4141805b..2f257bb092ae8 100644 --- a/compiler/rustc_lint/src/expect.rs +++ b/compiler/rustc_lint/src/expect.rs @@ -38,7 +38,7 @@ fn check_expectations(tcx: TyCtxt<'_>, tool_filter: Option) { (tcx.hir_attrs(id.hir_id)[id.attr_index as usize].id(), id.lint_index) } }; - (attr_id, lint_index.expect("fulfilled expectations must have a lint index")) + (attr_id, lint_index) }; let fulfilled_expectations: FxHashSet<_> = diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index b8da46594cfc2..00ae385c57672 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -233,7 +233,7 @@ pub trait LintLevelsProvider { &self, attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId; } @@ -258,7 +258,7 @@ impl LintLevelsProvider for TopDown { &self, attr_id: AttrId, _attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { UnstableLintExpectationId { attr_id, lint_index } } @@ -296,7 +296,7 @@ impl LintLevelsProvider for LintLevelQueryMap<'_> { &self, _attr_id: AttrId, attr_index: usize, - lint_index: Option, + lint_index: u16, ) -> Self::LintExpectationId { let attr_index = attr_index.try_into().unwrap(); StableLintExpectationId { hir_id: self.cur, attr_index, lint_index } @@ -740,11 +740,7 @@ where // `Expect` is the only lint level with a `LintExpectationId` that can be created // from an attribute. let lint_id = (level == Level::Expect).then(|| { - self.provider.mk_lint_expectation_id( - attr.id(), - attr_index, - Some(lint_index as u16), - ) + self.provider.mk_lint_expectation_id(attr.id(), attr_index, lint_index as u16) }); let sp = li.span(); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index fc8b2b65a8d48..1edc12e1ca7eb 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -109,7 +109,7 @@ pub enum LintExpectationId { #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash, Encodable, Decodable)] pub struct UnstableLintExpectationId { pub attr_id: AttrId, - pub lint_index: Option, + pub lint_index: u16, } impl From for LintExpectationId { @@ -125,7 +125,7 @@ impl From for LintExpectationId { pub struct StableLintExpectationId { pub hir_id: HirId, pub attr_index: u16, - pub lint_index: Option, + pub lint_index: u16, } impl StableHash for StableLintExpectationId { @@ -135,7 +135,7 @@ impl StableHash for StableLintExpectationId { hir_id.stable_hash(hcx, hasher); attr_index.stable_hash(hcx, hasher); - lint_index.expect("must be filled to call `stable_hash`").stable_hash(hcx, hasher); + lint_index.stable_hash(hcx, hasher); } } From 501ab4e8c59d865b428b63498577a08ab4bf3cf0 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Fri, 22 May 2026 16:21:32 +0200 Subject: [PATCH 122/174] Clarify Share trait docs --- library/core/src/clone.rs | 34 ++++++++++++++++++---------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 6c2b4fa151b86..0c3e90b78103a 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -290,21 +290,23 @@ pub macro Clone($item:item) { /* compiler built-in */ } -/// A trait for types whose [`Clone`] operation creates another alias to the same value. -/// -/// `Share` marks types where cloning creates another handle or reference to the same logical -/// resource or shared state, rather than an independent owned value. The distinction is semantic, -/// not cost-based: implementing `Share` does not merely mean that cloning is cheap, constant-time, -/// allocation-free, or convenient. -/// -/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) for -/// implementors, but communicates that the resulting value aliases the same underlying resource. -/// -/// Shared references, [`Rc`](std::rc::Rc), [`Arc`](std::sync::Arc), -/// [`Sender`](std::sync::mpsc::Sender), and [`SyncSender`](std::sync::mpsc::SyncSender) are -/// examples of types that can be shared this way. Types such as [`Vec`](std::vec::Vec), -/// [`String`](std::string::String), and [`Box`](std::boxed::Box) are not `Share` even though they -/// implement `Clone`, because cloning them creates another owned value rather than another handle +/// A trait for types whose [`Clone`] operation creates another alias to the same +/// logical resource or shared state. +/// +/// `Share` marks types where cloning creates another handle, reference, or alias +/// to the same logical resource or shared state, rather than an independent owned +/// value. The distinction is semantic, not cost-based: implementing `Share` does +/// not merely mean that cloning is cheap, constant-time, allocation-free, or +/// convenient. +/// +/// Calling [`share`](Share::share) is equivalent to calling [`clone`](Clone::clone) +/// for implementors, but communicates that the resulting value aliases the same +/// underlying resource. +/// +/// Shared references, `Rc`, `Arc`, `Sender`, and `SyncSender` are +/// examples of types that can be shared this way. Types such as `Vec`, +/// `String`, and `Box` are not `Share` even though they implement `Clone`, +/// because cloning them creates another owned value rather than another handle /// to the same logical resource. /// /// # Examples @@ -363,7 +365,7 @@ pub macro Clone($item:item) { /// ``` #[unstable(feature = "share_trait", issue = "156756")] pub trait Share: Clone { - /// Creates another alias to the same underlying value. + /// Creates another alias to the same underlying resource or shared state. /// /// This is equivalent to calling [`Clone::clone`]. #[unstable(feature = "share_trait", issue = "156756")] From 877d57c2c28ab7a5c5ea972e8898d0440689beb4 Mon Sep 17 00:00:00 2001 From: Yotam Ofek Date: Fri, 22 May 2026 18:08:11 +0300 Subject: [PATCH 123/174] Also emit "Dyn Compatibility" section for traits that *are* dyn compatible --- src/librustdoc/html/render/print_item.rs | 32 +++++++++++------------- tests/rustdoc-html/dyn-compatibility.rs | 4 +-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index 45e38d67c4020..336513804e1ff 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -946,24 +946,22 @@ fn item_trait(cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) -> impl fmt: let mut extern_crates = FxIndexSet::default(); - if !t.is_dyn_compatible(cx.tcx()) { - write!( - w, - "{}", - write_section_heading( - "Dyn Compatibility", - "dyn-compatibility", - None, - format!( - "

This trait is not \ - dyn compatible.

\ -

In older versions of Rust, dyn compatibility was called \"object safety\", \ - so this trait is not object safe.

", - base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION - ), + write!( + w, + "{}", + write_section_heading( + "Dyn Compatibility", + "dyn-compatibility", + None, + format!( + "

This trait {} \ + dyn compatible.

\ +

In older versions of Rust, dyn compatibility was called \"object safety\".

", + if t.is_dyn_compatible(cx.tcx()) { "is" } else { "is not" }, + base = crate::clean::utils::DOC_RUST_LANG_ORG_VERSION ), - )?; - } + ), + )?; if let Some(implementors) = cx.shared.cache.implementors.get(&it.item_id.expect_def_id()) { // The DefId is for the first Type found with that name. The bool is diff --git a/tests/rustdoc-html/dyn-compatibility.rs b/tests/rustdoc-html/dyn-compatibility.rs index 9115f93bc3bac..2d13c8a88bfdb 100644 --- a/tests/rustdoc-html/dyn-compatibility.rs +++ b/tests/rustdoc-html/dyn-compatibility.rs @@ -15,8 +15,8 @@ pub trait DynIncompatible2 { } //@ has 'foo/trait.DynCompatible.html' -//@ !has - '//*[@class="dyn-compatibility-info"]' '' -//@ !has - '//*[@id="dyn-compatibility"]' '' +//@ has - '//*[@class="dyn-compatibility-info"]' 'This trait is dyn compatible.' +//@ has - '//*[@id="dyn-compatibility"]' 'Dyn Compatibility' pub trait DynCompatible { fn foo(&self); } From fb2751b2e6143d3af8f497c24dc1389436c94a03 Mon Sep 17 00:00:00 2001 From: Mira Morgana Date: Fri, 22 May 2026 14:37:47 +0200 Subject: [PATCH 124/174] float_literal_f32_fallback: Don't suggest invalid code When a float literal ended with a dot, `float_literal_f32_fallback` should not include it in its suggestion. --- compiler/rustc_hir_typeck/src/fallback.rs | 8 +++++++- tests/ui/float/f32-into-f32.next-solver.fixed | 3 +++ tests/ui/float/f32-into-f32.next-solver.stderr | 13 +++++++++++-- tests/ui/float/f32-into-f32.old-solver.fixed | 3 +++ tests/ui/float/f32-into-f32.old-solver.stderr | 13 +++++++++++-- tests/ui/float/f32-into-f32.rs | 3 +++ 6 files changed, 38 insertions(+), 5 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fallback.rs b/compiler/rustc_hir_typeck/src/fallback.rs index ed8081ea887f7..886bcc8c21823 100644 --- a/compiler/rustc_hir_typeck/src/fallback.rs +++ b/compiler/rustc_hir_typeck/src/fallback.rs @@ -171,7 +171,13 @@ impl<'tcx> FnCtxt<'_, 'tcx> { .inspect(|vid| { let origin = self.float_var_origin(*vid); // Show the entire literal in the suggestion to make it clearer. - let literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); + let mut literal = self.tcx.sess.source_map().span_to_snippet(origin.span).ok(); + // A `.` at the end of the literal is no longer necessary if `f32` is explicitly specified + if let Some(ref mut literal) = literal + && literal.ends_with('.') + { + literal.pop(); + } self.tcx.emit_node_span_lint( FLOAT_LITERAL_F32_FALLBACK, origin.lint_id.unwrap_or(CRATE_HIR_ID), diff --git a/tests/ui/float/f32-into-f32.next-solver.fixed b/tests/ui/float/f32-into-f32.next-solver.fixed index a8d56f4428b91..6bc04ff757eef 100644 --- a/tests/ui/float/f32-into-f32.next-solver.fixed +++ b/tests/ui/float/f32-into-f32.next-solver.fixed @@ -15,6 +15,9 @@ fn main() { foo(1e5_f32); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0_f32); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0_f32; //~^ WARN falling back to `f32` diff --git a/tests/ui/float/f32-into-f32.next-solver.stderr b/tests/ui/float/f32-into-f32.next-solver.stderr index fc88f9d2c7f3c..1384f73f09689 100644 --- a/tests/ui/float/f32-into-f32.next-solver.stderr +++ b/tests/ui/float/f32-into-f32.next-solver.stderr @@ -27,7 +27,16 @@ LL | foo(1e5); = note: for more information, see issue #154024 warning: falling back to `f32` as the trait bound `f32: From` is not satisfied - --> $DIR/f32-into-f32.rs:19:14 + --> $DIR/f32-into-f32.rs:18:9 + | +LL | foo(0.); + | ^^ help: explicitly specify the type as `f32`: `0_f32` + | + = 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 #154024 + +warning: falling back to `f32` as the trait bound `f32: From` is not satisfied + --> $DIR/f32-into-f32.rs:22:14 | LL | let x = -4.0; | ^^^ help: explicitly specify the type as `f32`: `4.0_f32` @@ -35,5 +44,5 @@ LL | let x = -4.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 #154024 -warning: 4 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/float/f32-into-f32.old-solver.fixed b/tests/ui/float/f32-into-f32.old-solver.fixed index a8d56f4428b91..6bc04ff757eef 100644 --- a/tests/ui/float/f32-into-f32.old-solver.fixed +++ b/tests/ui/float/f32-into-f32.old-solver.fixed @@ -15,6 +15,9 @@ fn main() { foo(1e5_f32); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0_f32); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0_f32; //~^ WARN falling back to `f32` diff --git a/tests/ui/float/f32-into-f32.old-solver.stderr b/tests/ui/float/f32-into-f32.old-solver.stderr index fc88f9d2c7f3c..1384f73f09689 100644 --- a/tests/ui/float/f32-into-f32.old-solver.stderr +++ b/tests/ui/float/f32-into-f32.old-solver.stderr @@ -27,7 +27,16 @@ LL | foo(1e5); = note: for more information, see issue #154024 warning: falling back to `f32` as the trait bound `f32: From` is not satisfied - --> $DIR/f32-into-f32.rs:19:14 + --> $DIR/f32-into-f32.rs:18:9 + | +LL | foo(0.); + | ^^ help: explicitly specify the type as `f32`: `0_f32` + | + = 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 #154024 + +warning: falling back to `f32` as the trait bound `f32: From` is not satisfied + --> $DIR/f32-into-f32.rs:22:14 | LL | let x = -4.0; | ^^^ help: explicitly specify the type as `f32`: `4.0_f32` @@ -35,5 +44,5 @@ LL | let x = -4.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 #154024 -warning: 4 warnings emitted +warning: 5 warnings emitted diff --git a/tests/ui/float/f32-into-f32.rs b/tests/ui/float/f32-into-f32.rs index b55023453b7e0..3dbdd20bd3098 100644 --- a/tests/ui/float/f32-into-f32.rs +++ b/tests/ui/float/f32-into-f32.rs @@ -15,6 +15,9 @@ fn main() { foo(1e5); //~^ WARN falling back to `f32` //~| WARN this was previously accepted + foo(0.); + //~^ WARN falling back to `f32` + //~| WARN this was previously accepted foo(4f32); // no warning let x = -4.0; //~^ WARN falling back to `f32` From 1ac7072415582da6ff86d5fda758388ad5afbf1f Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Fri, 22 May 2026 13:49:03 -0400 Subject: [PATCH 125/174] add regression test --- .../single-use-lifetime/two-uses-in-trait-impl.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs index 1f0da77baee8b..983157614a24d 100644 --- a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs +++ b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs @@ -19,4 +19,18 @@ impl<'f> Iterator for Foo<'f> { } } +trait BoundTrait<'a> { + fn foo(self, handler: &Handler<'a>); +} + +struct Handler<'a>(fn(&'a u32)); +struct Bar<'b>(&'b u32); + +// https://github.com/rust-lang/rust/issues/153836 +impl<'a, 'b: 'a> BoundTrait<'a> for Bar<'b> { + fn foo(self, handler: &Handler<'a>) { + (handler.0)(self.0); + } +} + fn main() {} From d3d3485dd4953e0ed27ec2239ef7baef4e9dc45d Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sun, 3 May 2026 12:01:22 -0700 Subject: [PATCH 126/174] Let intrinsics use the SSA operand path --- compiler/rustc_codegen_gcc/src/context.rs | 6 +- .../rustc_codegen_gcc/src/intrinsic/mod.rs | 34 +-- .../rustc_codegen_gcc/src/intrinsic/simd.rs | 14 +- compiler/rustc_codegen_llvm/src/context.rs | 13 +- compiler/rustc_codegen_llvm/src/intrinsic.rs | 135 ++++++---- compiler/rustc_codegen_ssa/src/mir/analyze.rs | 20 +- compiler/rustc_codegen_ssa/src/mir/block.rs | 101 +++++--- .../rustc_codegen_ssa/src/mir/intrinsic.rs | 237 +++++++++--------- compiler/rustc_codegen_ssa/src/mir/mod.rs | 24 ++ compiler/rustc_codegen_ssa/src/mir/operand.rs | 1 + .../rustc_codegen_ssa/src/traits/intrinsic.rs | 17 +- compiler/rustc_codegen_ssa/src/traits/misc.rs | 7 + tests/codegen-llvm/array-equality.rs | 4 +- tests/codegen-llvm/atomicptr.rs | 9 +- .../codegen-llvm/dst-vtable-align-nonzero.rs | 4 +- tests/codegen-llvm/dst-vtable-size-range.rs | 4 +- .../codegen-llvm/intrinsics/disjoint_bitor.rs | 8 +- .../intrinsics/size_and_align_of_val.rs | 27 +- .../simd-intrinsic-generic-bitmask.rs | 12 +- .../simd-intrinsic-mask-reduce.rs | 24 +- tests/codegen-llvm/simd/aggregate-simd.rs | 4 +- 21 files changed, 420 insertions(+), 285 deletions(-) diff --git a/compiler/rustc_codegen_gcc/src/context.rs b/compiler/rustc_codegen_gcc/src/context.rs index e0810a35b040b..ed313859aeafa 100644 --- a/compiler/rustc_codegen_gcc/src/context.rs +++ b/compiler/rustc_codegen_gcc/src/context.rs @@ -19,7 +19,7 @@ use rustc_middle::ty::{self, ExistentialTraitRef, Instance, Ty, TyCtxt}; use rustc_session::Session; #[cfg(feature = "master")] use rustc_session::config::DebugInfo; -use rustc_span::{DUMMY_SP, Span, respan}; +use rustc_span::{DUMMY_SP, Span, Symbol, respan}; use rustc_target::spec::{HasTargetSpec, HasX86AbiOpt, Target, TlsModel, X86Abi}; #[cfg(feature = "master")] @@ -497,6 +497,10 @@ impl<'gcc, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'gcc, 'tcx> { None } } + + fn intrinsic_call_expects_place_always(&self, _name: Symbol) -> bool { + true + } } impl<'gcc, 'tcx> HasTyCtxt<'tcx> for CodegenCx<'gcc, 'tcx> { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 6fb25174bf1e0..f56cec6ce227e 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -9,6 +9,7 @@ use rustc_abi::{BackendRepr, HasDataLayout, WrappingRange}; use rustc_codegen_ssa::base::wants_msvc_seh; use rustc_codegen_ssa::common::IntPredicate; use rustc_codegen_ssa::errors::InvalidMonomorphization; +use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; #[cfg(feature = "master")] @@ -194,11 +195,14 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc &mut self, instance: Instance<'tcx>, args: &[OperandRef<'tcx, RValue<'gcc>>], - result: PlaceRef<'tcx, RValue<'gcc>>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>>, span: Span, - ) -> Result<(), Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, RValue<'gcc>> { let tcx = self.tcx; + let result = PlaceRef { val: result_place.unwrap(), layout: result_layout }; + let name = tcx.item_name(instance.def_id()); let name_str = name.as_str(); let fn_args = instance.args; @@ -353,7 +357,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc args[2].immediate(), result, ); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::breakpoint => { unimplemented!(); @@ -375,12 +379,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc sym::volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::unaligned_volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.unaligned_volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::prefetch_read_data | sym::prefetch_write_data @@ -448,12 +452,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc _ => bug!(), }, None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty: args[0].layout.ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } } } @@ -544,7 +548,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc extended_asm.set_volatile_flag(true); // We have copied the value to `result` already. - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::ptr_mask => { @@ -569,12 +573,15 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc span, ) { Ok(value) => value, - Err(()) => return Ok(()), + Err(err) => return IntrinsicResult::Err(err), } } // Fall back to default body - _ => return Err(Instance::new_raw(instance.def_id(), instance.args)), + _ => { + let fallback = Instance::new_raw(instance.def_id(), instance.args); + return IntrinsicResult::Fallback(fallback); + } }; if result.layout.ty.is_bool() { @@ -583,7 +590,7 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc } else if !result.layout.ty.is_unit() { self.store_to_place(value, result.val); } - Ok(()) + IntrinsicResult::WroteIntoPlace } fn codegen_llvm_intrinsic_call( @@ -694,13 +701,12 @@ impl<'a, 'gcc, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'a, 'gcc, 'tc self.context.new_rvalue_from_int(self.int_type, 0) } - fn va_start(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + fn va_start(&mut self, _va_list: RValue<'gcc>) { unimplemented!(); } - fn va_end(&mut self, _va_list: RValue<'gcc>) -> RValue<'gcc> { + fn va_end(&mut self, _va_list: RValue<'gcc>) { // FIXME(antoyo): implement. - self.context.new_rvalue_from_int(self.int_type, 0) } fn retag_reg(&mut self, _ptr: Self::Value, _info: &RetagInfo) -> Self::Value { diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs index ff5155f9f7776..82ef99703b253 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/simd.rs @@ -17,7 +17,7 @@ use rustc_hir as hir; use rustc_middle::mir::BinOp; use rustc_middle::ty::layout::HasTyCtxt; use rustc_middle::ty::{self, Ty}; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use crate::builder::Builder; #[cfg(not(feature = "master"))] @@ -32,12 +32,12 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( ret_ty: Ty<'tcx>, llret_ty: Type<'gcc>, span: Span, -) -> Result, ()> { +) -> Result, ErrorGuaranteed> { // macros for error handling: macro_rules! return_error { ($err:expr) => {{ - bx.tcx.dcx().emit_err($err); - return Err(()); + let err = bx.tcx.dcx().emit_err($err); + return Err(err); }}; } macro_rules! require { @@ -803,11 +803,11 @@ pub fn generic_simd_intrinsic<'a, 'gcc, 'tcx>( bx: &mut Builder<'_, 'gcc, 'tcx>, span: Span, args: &[OperandRef<'tcx, RValue<'gcc>>], - ) -> Result, ()> { + ) -> Result, ErrorGuaranteed> { macro_rules! return_error { ($err:expr) => {{ - bx.tcx.dcx().emit_err($err); - return Err(()); + let err = bx.tcx.dcx().emit_err($err); + return Err(err); }}; } let ty::Float(ref f) = *in_elem.kind() else { diff --git a/compiler/rustc_codegen_llvm/src/context.rs b/compiler/rustc_codegen_llvm/src/context.rs index 3e575f969afab..a7b5c71b51285 100644 --- a/compiler/rustc_codegen_llvm/src/context.rs +++ b/compiler/rustc_codegen_llvm/src/context.rs @@ -25,7 +25,7 @@ use rustc_session::Session; use rustc_session::config::{ BranchProtection, CFGuard, CFProtection, CrateType, DebugInfo, FunctionReturn, PAuthKey, PacRet, }; -use rustc_span::{DUMMY_SP, Span, Spanned, Symbol}; +use rustc_span::{DUMMY_SP, Span, Spanned, Symbol, sym}; use rustc_symbol_mangling::mangle_internal_symbol; use rustc_target::spec::{ Arch, CfgAbi, Env, HasTargetSpec, Os, RelocModel, SmallDataThresholdSupport, Target, TlsModel, @@ -937,6 +937,17 @@ impl<'ll, 'tcx> MiscCodegenMethods<'tcx> for CodegenCx<'ll, 'tcx> { None } } + + fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool { + matches!( + name, + sym::autodiff + | sym::catch_unwind + | sym::volatile_load + | sym::unaligned_volatile_load + | sym::black_box + ) + } } impl<'ll> CodegenCx<'ll, '_> { diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index ec6f068f46580..957ea23b9167c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -10,6 +10,7 @@ use rustc_codegen_ssa::RetagInfo; use rustc_codegen_ssa::base::{compare_simd_types, wants_msvc_seh, wants_wasm_eh}; use rustc_codegen_ssa::common::{IntPredicate, TypeKind}; use rustc_codegen_ssa::errors::{ExpectedPointerMutability, InvalidMonomorphization}; +use rustc_codegen_ssa::mir::IntrinsicResult; use rustc_codegen_ssa::mir::operand::{OperandRef, OperandValue}; use rustc_codegen_ssa::mir::place::{PlaceRef, PlaceValue}; use rustc_codegen_ssa::traits::*; @@ -24,7 +25,7 @@ use rustc_middle::{bug, span_bug}; use rustc_session::config::CrateType; use rustc_session::errors::feature_err; use rustc_session::lint::builtin::DEPRECATED_LLVM_INTRINSIC; -use rustc_span::{Span, Symbol, sym}; +use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; use rustc_symbol_mangling::{mangle_internal_symbol, symbol_name_for_instance_in_crate}; use rustc_target::callconv::PassMode; use rustc_target::spec::{Arch, Os}; @@ -174,9 +175,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, &'ll Value>], - result: PlaceRef<'tcx, &'ll Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, span: Span, - ) -> Result<(), ty::Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, &'ll Value> { let tcx = self.tcx; let llvm_version = crate::llvm_util::get_version(); @@ -221,8 +223,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ) } sym::autodiff => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; codegen_autodiff(self, tcx, instance, args, result); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::offload => { if tcx.sess.opts.unstable_opts.offload.is_empty() { @@ -234,7 +240,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } codegen_offload(self, tcx, instance, args); - return Ok(()); + // offload *has* a return type, but somehow works without mentioning the place + return IntrinsicResult::WroteIntoPlace; } sym::is_val_statically_known => { if let OperandValue::Immediate(imm) = args[0].val { @@ -263,8 +270,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let ptr = select(self, true_val.llval, false_val.llval); let selected = OperandValue::Ref(PlaceValue::new_sized(ptr, true_val.align)); + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; selected.store(self, result); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } (OperandValue::Immediate(_), OperandValue::Immediate(_)) | (OperandValue::Pair(_, _), OperandValue::Pair(_, _)) => { @@ -272,11 +283,15 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { let false_val = args[2].immediate_or_packed_pair(self); select(self, true_val, false_val) } - (OperandValue::ZeroSized, OperandValue::ZeroSized) => return Ok(()), + (OperandValue::ZeroSized, OperandValue::ZeroSized) => return IntrinsicResult::Operand(OperandValue::ZeroSized), _ => span_bug!(span, "Incompatible OperandValue for select_unpredictable"), } } sym::catch_unwind => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; catch_unwind_intrinsic( self, args[0].immediate(), @@ -284,7 +299,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { args[2].immediate(), result, ); - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::breakpoint => self.call_intrinsic("llvm.debugtrap", &[], &[]), sym::va_arg => { @@ -298,7 +313,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { feature_err(&*self.sess(), feature, span, msg).emit(); } - let BackendRepr::Scalar(scalar) = result.layout.backend_repr else { + let BackendRepr::Scalar(scalar) = result_layout.backend_repr else { bug!("the va_arg intrinsic does not support non-scalar types") }; @@ -315,7 +330,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { bug!("the va_arg intrinsic does not support `i128`/`u128`") } Primitive::Int(..) => { - let int_width = self.cx().size_of(result.layout.ty).bits(); + let int_width = self.cx().size_of(result_layout.ty).bits(); let target_c_int_width = self.cx().sess().target.options.c_int_width; if int_width < u64::from(target_c_int_width) { // Smaller integer types are automatically promototed and `va_arg` @@ -345,34 +360,39 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } } - emit_va_arg(self, args[0], result.layout.ty) + emit_va_arg(self, args[0], result_layout.ty) } sym::volatile_load | sym::unaligned_volatile_load => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; + let ptr = args[0].immediate(); - let load = self.volatile_load(result.layout.llvm_type(self), ptr); + let load = self.volatile_load(result_layout.llvm_type(self), ptr); let align = if name == sym::unaligned_volatile_load { 1 } else { - result.layout.align.bytes() as u32 + result_layout.align.bytes() as u32 }; unsafe { llvm::LLVMSetAlignment(load, align); } - if !result.layout.is_zst() { + if !result_layout.is_zst() { self.store_to_place(load, result.val); } - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::unaligned_volatile_store => { let dst = args[0].deref(self.cx()); args[1].val.unaligned_volatile_store(self, dst); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::prefetch_read_data | sym::prefetch_write_data @@ -396,7 +416,8 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.const_i32(locality), self.const_i32(cache_type), ], - ) + ); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } sym::carrying_mul_add => { let (size, signed) = fn_args.type_at(0).int_size_and_signed(self.tcx); @@ -434,12 +455,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { sym::carryless_mul if llvm_version >= (22, 0, 0) => { let ty = args[0].layout.ty; if !ty.is_integral() { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } let (size, _) = ty.int_size_and_signed(self.tcx); let width = size.bits(); @@ -463,12 +484,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::unchecked_funnel_shr => { let ty = args[0].layout.ty; if !ty.is_integral() { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } let (size, signed) = ty.int_size_and_signed(self.tcx); let width = size.bits(); @@ -484,12 +505,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; let ret = self.call_intrinsic(llvm_name, &[llty], &[args[0].immediate(), y]); - self.intcast(ret, result.layout.llvm_type(self), false) + self.intcast(ret, result_layout.llvm_type(self), false) } sym::ctpop => { let ret = self.call_intrinsic("llvm.ctpop", &[llty], &[args[0].immediate()]); - self.intcast(ret, result.layout.llvm_type(self), false) + self.intcast(ret, result_layout.llvm_type(self), false) } sym::bswap => { if width == 8 { @@ -551,12 +572,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { Scalar(_) | ScalarPair(_, _) => true, SimdVector { .. } => false, SimdScalableVector { .. } => { - tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { + let err = tcx.dcx().emit_err(InvalidMonomorphization::NonScalableType { span, name: sym::raw_eq, ty: tp_ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } Memory { .. } => { // For rusty ABIs, small aggregates are actually passed @@ -594,6 +615,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { } sym::black_box => { + let result = PlaceRef { + val: result_place.unwrap(), + layout: result_layout, + }; args[0].val.store(self, result); let result_val_span = [result.val.llval]; // We need to "use" the argument in some way LLVM can't introspect, and on @@ -628,7 +653,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { .unwrap_or_else(|| bug!("failed to generate inline asm call for `black_box`")); // We have copied the value to `result` already. - return Ok(()); + return IntrinsicResult::WroteIntoPlace; } sym::gpu_launch_sized_workgroup_mem => { @@ -652,7 +677,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.type_array(self.type_i8(), 0), AddressSpace::GPU_WORKGROUP, ); - let ty::RawPtr(inner_ty, _) = result.layout.ty.kind() else { unreachable!() }; + let ty::RawPtr(inner_ty, _) = result_layout.ty.kind() else { unreachable!() }; // The alignment of the global is used to specify the *minimum* alignment that // must be obeyed by the GPU runtime. // When multiple of these global variables are used by a kernel, the maximum alignment is taken. @@ -816,10 +841,10 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { ); } - let llret_ty = if result.layout.ty.is_simd() - && let BackendRepr::Memory { .. } = result.layout.backend_repr + let llret_ty = if result_layout.ty.is_simd() + && let BackendRepr::Memory { .. } = result_layout.backend_repr { - let (size, elem_ty) = result.layout.ty.simd_size_and_type(self.tcx()); + let (size, elem_ty) = result_layout.ty.simd_size_and_type(self.tcx()); let elem_ll_ty = match elem_ty.kind() { ty::Float(f) => self.type_float_from_ty(*f), ty::Int(i) => self.type_int_from_ty(*i), @@ -829,7 +854,7 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { }; self.type_vector(elem_ll_ty, size) } else { - result.layout.llvm_type(self) + result_layout.llvm_type(self) }; match generic_simd_intrinsic( @@ -837,14 +862,14 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { name, fn_args, &loaded_args, - result.layout.ty, + result_layout.ty, llret_ty, span, ) { Ok(llval) => llval, // If there was an error, just skip this invocation... we'll abort compilation // anyway, but we can keep codegen'ing to find more errors. - Err(()) => return Ok(()), + Err(err) => return IntrinsicResult::Err(err), } } @@ -874,17 +899,23 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { _ => { debug!("unknown intrinsic '{}' -- falling back to default body", name); // Call the fallback body instead of generating the intrinsic code - return Err(ty::Instance::new_raw(instance.def_id(), instance.args)); + let fallback = ty::Instance::new_raw(instance.def_id(), instance.args); + return IntrinsicResult::Fallback(fallback); } }; - if result.layout.ty.is_bool() { - let val = self.from_immediate(llval); - self.store_to_place(val, result.val); - } else if !result.layout.ty.is_unit() { - self.store_to_place(llval, result.val); + if let BackendRepr::Memory { .. } = result_layout.backend_repr { + // We have an llvm immediate, but that's not what cg_ssa expects, + // so write it into the place (that always exists for memory) + if !result_layout.is_zst() { + self.store_to_place(llval, result_place.unwrap()); + } + IntrinsicResult::WroteIntoPlace + } else { + IntrinsicResult::Operand( + OperandRef::from_immediate_or_packed_pair(self, llval, result_layout).val, + ) } - Ok(()) } fn codegen_llvm_intrinsic_call( @@ -1041,12 +1072,12 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { self.extract_value(type_checked_load, 0) } - fn va_start(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]) + fn va_start(&mut self, va_list: &'ll Value) { + self.call_intrinsic("llvm.va_start", &[self.val_ty(va_list)], &[va_list]); } - fn va_end(&mut self, va_list: &'ll Value) -> &'ll Value { - self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]) + fn va_end(&mut self, va_list: &'ll Value) { + self.call_intrinsic("llvm.va_end", &[self.val_ty(va_list)], &[va_list]); } fn retag_reg(&mut self, ptr: Self::Value, info: &RetagInfo) -> Self::Value { @@ -1999,11 +2030,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( ret_ty: Ty<'tcx>, llret_ty: &'ll Type, span: Span, -) -> Result<&'ll Value, ()> { +) -> Result<&'ll Value, ErrorGuaranteed> { macro_rules! return_error { ($diag: expr) => {{ - bx.sess().dcx().emit_err($diag); - return Err(()); + let err = bx.sess().dcx().emit_err($diag); + return Err(err); }}; } @@ -2450,11 +2481,11 @@ fn generic_simd_intrinsic<'ll, 'tcx>( bx: &mut Builder<'_, 'll, 'tcx>, span: Span, args: &[OperandRef<'tcx, &'ll Value>], - ) -> Result<&'ll Value, ()> { + ) -> Result<&'ll Value, ErrorGuaranteed> { macro_rules! return_error { ($diag: expr) => {{ - bx.sess().dcx().emit_err($diag); - return Err(()); + let err = bx.sess().dcx().emit_err($diag); + return Err(err); }}; } @@ -3040,7 +3071,7 @@ fn generic_simd_intrinsic<'ll, 'tcx>( return match in_elem.kind() { ty::Int(_) | ty::Uint(_) => { let r = bx.$red(input); - Ok(if !$boolean { r } else { bx.zext(r, bx.type_bool()) }) + Ok(r) } _ => return_error!(InvalidMonomorphization::UnsupportedSymbol { span, diff --git a/compiler/rustc_codegen_ssa/src/mir/analyze.rs b/compiler/rustc_codegen_ssa/src/mir/analyze.rs index de755d5617801..fb5734ba087c6 100644 --- a/compiler/rustc_codegen_ssa/src/mir/analyze.rs +++ b/compiler/rustc_codegen_ssa/src/mir/analyze.rs @@ -7,8 +7,8 @@ use rustc_index::bit_set::DenseBitSet; use rustc_index::{IndexSlice, IndexVec}; use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor}; use rustc_middle::mir::{self, DefLocation, Location, TerminatorKind, traversal}; -use rustc_middle::ty::layout::LayoutOf; -use rustc_middle::{bug, span_bug}; +use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf}; +use rustc_middle::{bug, span_bug, ty}; use tracing::debug; use super::FunctionCx; @@ -55,7 +55,7 @@ pub(crate) fn non_ssa_locals<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( non_ssa_locals } -#[derive(Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq)] enum LocalKind { ZST, /// A local that requires an alloca. @@ -195,12 +195,20 @@ impl<'a, 'b, 'tcx, Bx: BuilderMethods<'b, 'tcx>> Visitor<'tcx> for LocalAnalyzer match context { PlaceContext::MutatingUse(MutatingUseContext::Call) => { let call = location.block; - let TerminatorKind::Call { target, .. } = - self.fx.mir.basic_blocks[call].terminator().kind + let TerminatorKind::Call { target, func, .. } = + &self.fx.mir.basic_blocks[call].terminator().kind else { bug!() }; - self.define(local, DefLocation::CallReturn { call, target }); + let tcx = self.fx.cx.tcx(); + let func_ty = func.ty(&self.fx.mir.local_decls, tcx); + if let ty::FnDef(def_id, _args) = *func_ty.kind() + && let Some(intrinsic) = tcx.intrinsic(def_id) + && self.fx.cx.intrinsic_call_expects_place_always(intrinsic.name) + { + self.locals[local] = LocalKind::Memory; + } + self.define(local, DefLocation::CallReturn { call, target: *target }); } PlaceContext::NonUse(_) diff --git a/compiler/rustc_codegen_ssa/src/mir/block.rs b/compiler/rustc_codegen_ssa/src/mir/block.rs index f4e08e08ef8db..b6b95c5f12aae 100644 --- a/compiler/rustc_codegen_ssa/src/mir/block.rs +++ b/compiler/rustc_codegen_ssa/src/mir/block.rs @@ -17,12 +17,13 @@ use rustc_target::callconv::{ArgAbi, ArgAttributes, CastTarget, FnAbi, PassMode} use tracing::{debug, info}; use super::operand::OperandRef; -use super::operand::OperandValue::{Immediate, Pair, Ref, ZeroSized}; +use super::operand::OperandValue::{self, Immediate, Pair, Ref, ZeroSized}; use super::place::{PlaceRef, PlaceValue}; use super::{CachedLlbb, FunctionCx, LocalRef}; use crate::base::{self, is_call_from_compiler_builtins_to_upstream_monomorphization}; use crate::common::{self, IntPredicate}; use crate::errors::CompilerBuiltinsCannotCall; +use crate::mir::IntrinsicResult; use crate::traits::*; use crate::{MemFlags, meth}; @@ -944,32 +945,31 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let result_layout = self.cx.layout_of(self.monomorphized_place_ty(destination.as_ref())); - let (result, store_in_local) = if result_layout.is_zst() { - ( - PlaceRef::new_sized(bx.const_undef(bx.type_ptr()), result_layout), - None, - ) - } else if let Some(local) = destination.as_local() { - match self.locals[local] { - LocalRef::Place(dest) => (dest, None), - LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), - LocalRef::PendingOperand => { - // Currently, intrinsics always need a location to store - // the result, so we create a temporary `alloca` for the - // result. - let tmp = PlaceRef::alloca(bx, result_layout); - tmp.storage_live(bx); - (tmp, Some(local)) + let (result_place, store_in_local) = + if let Some(local) = destination.as_local() { + match self.locals[local] { + LocalRef::Place(dest) => (Some(dest.val), None), + LocalRef::UnsizedPlace(_) => bug!("return type must be sized"), + LocalRef::PendingOperand => (None, Some(local)), + LocalRef::Operand(_) => { + if result_layout.is_zst() { + let place = PlaceRef::new_sized( + bx.const_undef(bx.type_ptr()), + result_layout, + ); + (Some(place.val), None) + } else { + bug!("place local already assigned to"); + } + } } - LocalRef::Operand(_) => { - bug!("place local already assigned to"); - } - } - } else { - (self.codegen_place(bx, destination.as_ref()), None) - }; + } else { + (Some(self.codegen_place(bx, destination.as_ref()).val), None) + }; - if result.val.align < result.layout.align.abi { + if let Some(place) = result_place + && place.align < result_layout.align.abi + { // Currently, MIR code generation does not create calls // that store directly to fields of packed structs (in // fact, the calls it creates write only to temps). @@ -982,16 +982,36 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let args: Vec<_> = args.iter().map(|arg| self.codegen_operand(bx, &arg.node)).collect(); - match self.codegen_intrinsic_call(bx, instance, &args, result, source_info) - { - Ok(()) => { - if let Some(local) = store_in_local { - let op = bx.load_operand(result); - result.storage_dead(bx); + let intrinsic_result = self.codegen_intrinsic_call( + bx, + instance, + &args, + result_layout, + result_place, + source_info, + ); + + if let IntrinsicResult::Operand(op_val) = intrinsic_result { + match (result_place, store_in_local) { + (None, Some(local)) => { + let op = OperandRef { + val: op_val, + layout: result_layout, + move_annotation: None, + }; self.overwrite_local(local, LocalRef::Operand(op)); self.debug_introduce_local(bx, local); } + (Some(place_val), None) => { + let dest = PlaceRef { val: place_val, layout: result_layout }; + op_val.store(bx, dest); + } + _ => bug!(), + } + } + match intrinsic_result { + IntrinsicResult::Operand(_) | IntrinsicResult::WroteIntoPlace => { return if let Some(target) = target { helper.funclet_br(self, bx, target, mergeable_succ) } else { @@ -999,7 +1019,24 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { MergingSucc::False }; } - Err(instance) => { + IntrinsicResult::Err(_) => { + // Even though we're definitely going to error, we need it initialize + // the local or `maybe_codegen_consume_direct` might ICE later + // when it goes to use the result from this intrinsic. + if let Some(local) = store_in_local { + let op = OperandRef { + val: OperandValue::poison(bx, result_layout), + layout: result_layout, + move_annotation: None, + }; + self.overwrite_local(local, LocalRef::Operand(op)); + } + // Also we need to terminate the block to avoid an LLVM assertion, + // even though we're not going to actually use the IR. + bx.abort(); + return MergingSucc::False; + } + IntrinsicResult::Fallback(instance) => { if intrinsic.must_be_overridden { span_bug!( fn_span, diff --git a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs index a11e584207e2f..ac6c6a0a52efa 100644 --- a/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/mir/intrinsic.rs @@ -1,16 +1,17 @@ -use rustc_abi::{Align, WrappingRange}; +use rustc_abi::{Align, FieldIdx, WrappingRange}; use rustc_middle::mir::SourceInfo; use rustc_middle::ty::{self, Ty, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::config::OptLevel; -use rustc_span::sym; +use rustc_span::{ErrorGuaranteed, sym}; use rustc_target::spec::Arch; -use super::FunctionCx; -use super::operand::OperandRef; -use super::place::PlaceRef; +use super::operand::{OperandRef, OperandValue}; +use super::place::PlaceValue; +use super::{FunctionCx, IntrinsicResult}; use crate::common::{AtomicRmwBinOp, SynchronizationScope}; use crate::errors::InvalidMonomorphization; +use crate::mir::operand::OperandRefBuilder; use crate::traits::*; use crate::{MemFlags, meth, size_of_val}; @@ -52,15 +53,16 @@ fn memset_intrinsic<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>( } impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { - /// In the `Err` case, returns the instance that should be called instead. + /// In the `Fallback` case, returns the instance that should be called instead. pub fn codegen_intrinsic_call( &mut self, bx: &mut Bx, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Bx::Value>], - result: PlaceRef<'tcx, Bx::Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, source_info: SourceInfo, - ) -> Result<(), ty::Instance<'tcx>> { + ) -> IntrinsicResult<'tcx, Bx::Value> { let span = source_info.span; let name = bx.tcx().item_name(instance.def_id()); @@ -86,19 +88,19 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let x_place = args[0].val.deref(align); let y_place = args[1].val.deref(align); bx.typed_place_swap(x_place, y_place, pointee_layout); - return Ok(()); + return IntrinsicResult::Operand(OperandValue::ZeroSized); } } - let invalid_monomorphization_int_type = |ty| { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); + let invalid_monomorphization_int_type = |ty| -> ErrorGuaranteed { + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }) }; - let invalid_monomorphization_int_or_ptr_type = |ty| { + let invalid_monomorphization_int_or_ptr_type = |ty| -> ErrorGuaranteed { bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerOrPtrType { span, name, ty, - }); + }) }; let parse_atomic_ordering = |ord: ty::Value<'tcx>| { @@ -137,32 +139,34 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } } - let llval = match name { + let op_val: OperandValue<_> = match name { sym::abort => { bx.abort(); - return Ok(()); + OperandValue::ZeroSized } sym::caller_location => { let location = self.get_caller_location(bx, source_info); - location.val.store(bx, result); - return Ok(()); + location.val } // va_end uses the fallback body (a no-op). - sym::va_start => bx.va_start(args[0].immediate()), + sym::va_start => { + bx.va_start(args[0].immediate()); + OperandValue::ZeroSized + } sym::size_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); let (llsize, _) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); - llsize + OperandValue::Immediate(llsize) } sym::align_of_val => { let tp_ty = fn_args.type_at(0); let (_, meta) = args[0].val.pointer_parts(); let (_, llalign) = size_of_val::size_and_align_of_dst(bx, tp_ty, meta); - llalign + OperandValue::Immediate(llalign) } sym::vtable_size | sym::vtable_align => { let vtable = args[0].immediate(); @@ -190,14 +194,14 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { } _ => {} } - value + OperandValue::Immediate(value) } sym::arith_offset => { let ty = fn_args.type_at(0); let layout = bx.layout_of(ty); let ptr = args[0].immediate(); let offset = args[1].immediate(); - bx.gep(bx.backend_type(layout), ptr, &[offset]) + OperandValue::Immediate(bx.gep(bx.backend_type(layout), ptr, &[offset])) } sym::copy => { copy_intrinsic( @@ -209,7 +213,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[0].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::write_bytes => { memset_intrinsic( @@ -220,7 +224,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_copy_nonoverlapping_memory => { @@ -233,7 +237,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_copy_memory => { copy_intrinsic( @@ -245,7 +249,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_set_memory => { memset_intrinsic( @@ -256,60 +260,58 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { args[1].immediate(), args[2].immediate(), ); - return Ok(()); + OperandValue::ZeroSized } sym::volatile_store => { let dst = args[0].deref(bx.cx()); args[1].val.volatile_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::unaligned_volatile_store => { let dst = args[0].deref(bx.cx()); args[1].val.unaligned_volatile_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::disjoint_bitor => { let a = args[0].immediate(); let b = args[1].immediate(); - bx.or_disjoint(a, b) + OperandValue::Immediate(bx.or_disjoint(a, b)) } sym::exact_div => { let ty = args[0].layout.ty; match int_type_width_signed(ty, bx.tcx()) { - Some((_width, signed)) => { - if signed { - bx.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.exactudiv(args[0].immediate(), args[1].immediate()) - } - } + Some((_width, signed)) => OperandValue::Immediate(if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); + let err = bx + .tcx() + .dcx() + .emit_err(InvalidMonomorphization::BasicIntegerType { span, name, ty }); + return IntrinsicResult::Err(err); } } } sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { match float_type_width(args[0].layout.ty) { - Some(_width) => match name { + Some(_width) => OperandValue::Immediate(match name { sym::fadd_fast => bx.fadd_fast(args[0].immediate(), args[1].immediate()), sym::fsub_fast => bx.fsub_fast(args[0].immediate(), args[1].immediate()), sym::fmul_fast => bx.fmul_fast(args[0].immediate(), args[1].immediate()), sym::fdiv_fast => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), sym::frem_fast => bx.frem_fast(args[0].immediate(), args[1].immediate()), _ => bug!(), - }, + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { - span, - name, - ty: args[0].layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { + span, + name, + ty: args[0].layout.ty, + }); + return IntrinsicResult::Err(err); } } } @@ -318,7 +320,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { | sym::fmul_algebraic | sym::fdiv_algebraic | sym::frem_algebraic => match float_type_width(args[0].layout.ty) { - Some(_width) => match name { + Some(_width) => OperandValue::Immediate(match name { sym::fadd_algebraic => { bx.fadd_algebraic(args[0].immediate(), args[1].immediate()) } @@ -335,75 +337,77 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { bx.frem_algebraic(args[0].immediate(), args[1].immediate()) } _ => bug!(), - }, + }), None => { - bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { + let err = bx.tcx().dcx().emit_err(InvalidMonomorphization::BasicFloatType { span, name, ty: args[0].layout.ty, }); - return Ok(()); + return IntrinsicResult::Err(err); } }, sym::float_to_int_unchecked => { if float_type_width(args[0].layout.ty).is_none() { - bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { - span, - ty: args[0].layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { + span, + ty: args[0].layout.ty, + }); + return IntrinsicResult::Err(err); } - let Some((_width, signed)) = int_type_width_signed(result.layout.ty, bx.tcx()) + let Some((_width, signed)) = int_type_width_signed(result_layout.ty, bx.tcx()) else { - bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { - span, - ty: result.layout.ty, - }); - return Ok(()); + let err = + bx.tcx().dcx().emit_err(InvalidMonomorphization::FloatToIntUnchecked { + span, + ty: result_layout.ty, + }); + return IntrinsicResult::Err(err); }; - if signed { - bx.fptosi(args[0].immediate(), bx.backend_type(result.layout)) + OperandValue::Immediate(if signed { + bx.fptosi(args[0].immediate(), bx.backend_type(result_layout)) } else { - bx.fptoui(args[0].immediate(), bx.backend_type(result.layout)) - } + bx.fptoui(args[0].immediate(), bx.backend_type(result_layout)) + }) } sym::atomic_load => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let ordering = fn_args.const_at(1).to_value(); let layout = bx.layout_of(ty); let source = args[0].immediate(); - bx.atomic_load( + OperandValue::Immediate(bx.atomic_load( bx.backend_type(layout), source, parse_atomic_ordering(ordering), layout.size, - ) + )) } sym::atomic_store => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let ordering = fn_args.const_at(1).to_value(); let size = bx.layout_of(ty).size; let val = args[1].immediate(); let ptr = args[0].immediate(); bx.atomic_store(val, ptr, parse_atomic_ordering(ordering), size); - return Ok(()); + OperandValue::ZeroSized } // These are all AtomicRMW ops sym::atomic_cxchg | sym::atomic_cxchgweak => { let ty = fn_args.type_at(0); if !(int_type_width_signed(ty, bx.tcx()).is_some() || ty.is_raw_ptr()) { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } let succ_ordering = fn_args.const_at(1).to_value(); let fail_ordering = fn_args.const_at(2).to_value(); @@ -422,12 +426,10 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let val = bx.from_immediate(val); let success = bx.from_immediate(success); - let dest = result.project_field(bx, 0); - bx.store_to_place(val, dest.val); - let dest = result.project_field(bx, 1); - bx.store_to_place(success, dest.val); - - return Ok(()); + let mut builder = OperandRefBuilder::new(result_layout); + builder.insert_imm(FieldIdx::from_u32(0), val); + builder.insert_imm(FieldIdx::from_u32(1), success); + builder.build(bx.cx()).val } sym::atomic_max | sym::atomic_min => { let atom_op = if name == sym::atomic_max { @@ -441,16 +443,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ordering = fn_args.const_at(1).to_value(); let ptr = args[0].immediate(); let val = args[1].immediate(); - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ false, - ) + )) } else { - invalid_monomorphization_int_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_umax | sym::atomic_umin => { @@ -465,16 +467,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ordering = fn_args.const_at(1).to_value(); let ptr = args[0].immediate(); let val = args[1].immediate(); - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ false, - ) + )) } else { - invalid_monomorphization_int_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_xchg => { @@ -484,16 +486,16 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let ptr = args[0].immediate(); let val = args[1].immediate(); let atomic_op = AtomicRmwBinOp::AtomicXchg; - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atomic_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ ty.is_raw_ptr(), - ) + )) } else { - invalid_monomorphization_int_or_ptr_type(ty); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty); + return IntrinsicResult::Err(err); } } sym::atomic_xadd @@ -525,22 +527,22 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { { let ptr = args[0].immediate(); // of type "pointer to `ty_mem`" let val = args[1].immediate(); // of type `ty_op` - bx.atomic_rmw( + OperandValue::Immediate(bx.atomic_rmw( atom_op, ptr, val, parse_atomic_ordering(ordering), /* ret_ptr */ ty_mem.is_raw_ptr(), - ) + )) } else { - invalid_monomorphization_int_or_ptr_type(ty_mem); - return Ok(()); + let err = invalid_monomorphization_int_or_ptr_type(ty_mem); + return IntrinsicResult::Err(err); } } sym::atomic_fence => { let ordering = fn_args.const_at(0).to_value(); bx.atomic_fence(parse_atomic_ordering(ordering), SynchronizationScope::CrossThread); - return Ok(()); + OperandValue::ZeroSized } sym::atomic_singlethreadfence => { @@ -549,13 +551,13 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { parse_atomic_ordering(ordering), SynchronizationScope::SingleThread, ); - return Ok(()); + OperandValue::ZeroSized } sym::nontemporal_store => { let dst = args[0].deref(bx.cx()); args[1].val.nontemporal_store(bx, dst); - return Ok(()); + OperandValue::ZeroSized } sym::ptr_offset_from | sym::ptr_offset_from_unsigned => { @@ -567,7 +569,7 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { let a = bx.ptrtoint(a, bx.type_isize()); let b = bx.ptrtoint(b, bx.type_isize()); let pointee_size = bx.const_usize(pointee_size.bytes()); - if name == sym::ptr_offset_from { + OperandValue::Immediate(if name == sym::ptr_offset_from { // This is the same sequence that Clang emits for pointer subtraction. // It can be neither `nsw` nor `nuw` because the input is treated as // unsigned but then the output is treated as signed, so neither works. @@ -579,27 +581,32 @@ impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> { // so can use `sub nuw` and `udiv exact` instead of dealing in signed. let d = bx.unchecked_usub(a, b); bx.exactudiv(d, pointee_size) - } + }) } sym::cold_path => { // This is a no-op. The intrinsic is just a hint to the optimizer. - return Ok(()); + OperandValue::ZeroSized } _ => { // Need to use backend-specific things in the implementation. - return bx.codegen_intrinsic_call(instance, args, result, span); + let result = + bx.codegen_intrinsic_call(instance, args, result_layout, result_place, span); + if let IntrinsicResult::Operand(op) = result { + op + } else { + return result; + } } }; - if result.layout.ty.is_bool() { - let val = bx.from_immediate(llval); - bx.store_to_place(val, result.val); - } else if !result.layout.ty.is_unit() { - bx.store_to_place(llval, result.val); - } - Ok(()) + debug_assert!( + op_val.is_expected_variant_for_type(bx.cx(), result_layout), + "[{name:?}] Value {op_val:?} is wrong for type {result_layout:?}", + ); + + IntrinsicResult::Operand(op_val) } } diff --git a/compiler/rustc_codegen_ssa/src/mir/mod.rs b/compiler/rustc_codegen_ssa/src/mir/mod.rs index 84013a00d79df..4bcf037ecce07 100644 --- a/compiler/rustc_codegen_ssa/src/mir/mod.rs +++ b/compiler/rustc_codegen_ssa/src/mir/mod.rs @@ -7,6 +7,7 @@ use rustc_middle::mir::{Body, Local, UnwindTerminateReason, traversal}; use rustc_middle::ty::layout::{FnAbiOf, HasTyCtxt, HasTypingEnv, TyAndLayout}; use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable, TypeVisitableExt}; use rustc_middle::{bug, mir, span_bug}; +use rustc_span::ErrorGuaranteed; use rustc_target::callconv::{FnAbi, PassMode}; use tracing::{debug, instrument}; @@ -157,6 +158,29 @@ enum LocalRef<'tcx, V> { PendingOperand, } +pub enum IntrinsicResult<'tcx, V> { + /// This intrinsic created an operand without using the `result_place` argument. + /// + /// `codegen_call_terminator` will handle writing the result into the place, + /// if doing so is needed. + /// + /// The vast majority of intrinsics can do this, see MCP#970 + Operand(OperandValue), + + /// The intrinsic wrote its result into the `result_place` argument. + /// + /// Most things don't need to do this, but there are some: `volatile_load` + /// of a non-scalar type, for example, has to. + WroteIntoPlace, + + /// Another instance should be called instead. This is used to invoke intrinsic + /// default bodies in case an intrinsic is not implemented by the backend. + Fallback(ty::Instance<'tcx>), + + /// Arguably this shouldn't exist, per MCP#620, but a bunch do it. + Err(ErrorGuaranteed), +} + impl<'tcx, V: CodegenObject> LocalRef<'tcx, V> { fn new_operand(layout: TyAndLayout<'tcx>) -> LocalRef<'tcx, V> { if layout.is_zst() { diff --git a/compiler/rustc_codegen_ssa/src/mir/operand.rs b/compiler/rustc_codegen_ssa/src/mir/operand.rs index e1d1ef858c017..83fce5a5c8deb 100644 --- a/compiler/rustc_codegen_ssa/src/mir/operand.rs +++ b/compiler/rustc_codegen_ssa/src/mir/operand.rs @@ -103,6 +103,7 @@ impl OperandValue { PlaceValue { llval, llextra, align } } + #[must_use] pub(crate) fn is_expected_variant_for_type<'tcx, Cx: LayoutTypeCodegenMethods<'tcx>>( &self, cx: &Cx, diff --git a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs index fffd9b75f2bf7..dcd4e722a27a8 100644 --- a/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs +++ b/compiler/rustc_codegen_ssa/src/traits/intrinsic.rs @@ -3,8 +3,9 @@ use rustc_span::Span; use super::BackendTypes; use crate::RetagInfo; +use crate::mir::IntrinsicResult; use crate::mir::operand::OperandRef; -use crate::mir::place::PlaceRef; +use crate::mir::place::PlaceValue; pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Higher-level interface to emitting calls to intrinsics @@ -12,9 +13,12 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { /// Remember to add all intrinsics here, in `compiler/rustc_hir_analysis/src/check/mod.rs`, /// and in `library/core/src/intrinsics.rs`; if you need access to any LLVM intrinsics, /// add them to `compiler/rustc_codegen_llvm/src/context.rs`. - /// Returns `Err` if another instance should be called instead. This is used to invoke + /// Returns `Fallback` if another instance should be called instead. This is used to invoke /// intrinsic default bodies in case an intrinsic is not implemented by the backend. /// + /// The `result_place` will be provided for things that weren't `LocalKind::SSA`. + /// If you need it for more things, see `intrinsic_call_expects_place_always`. + /// /// NOTE: allowed to call [`BuilderMethods::call`] /// /// [`BuilderMethods::call`]: super::builder::BuilderMethods::call @@ -22,9 +26,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { &mut self, instance: ty::Instance<'tcx>, args: &[OperandRef<'tcx, Self::Value>], - result_dest: PlaceRef<'tcx, Self::Value>, + result_layout: ty::layout::TyAndLayout<'tcx>, + result_place: Option>, span: Span, - ) -> Result<(), ty::Instance<'tcx>>; + ) -> IntrinsicResult<'tcx, Self::Value>; fn codegen_llvm_intrinsic_call( &mut self, @@ -46,10 +51,10 @@ pub trait IntrinsicCallBuilderMethods<'tcx>: BackendTypes { ) -> Self::Value; /// Trait method used to inject `va_start` on the "spoofed" `VaList` in /// Rust defined C-variadic functions. - fn va_start(&mut self, val: Self::Value) -> Self::Value; + fn va_start(&mut self, val: Self::Value); /// Trait method used to inject `va_end` on the "spoofed" `VaList` before /// Rust defined C-variadic functions return. - fn va_end(&mut self, val: Self::Value) -> Self::Value; + fn va_end(&mut self, val: Self::Value); /// Trait method used to retag a pointer stored within a place. fn retag_mem(&mut self, place: Self::Value, info: &RetagInfo); /// Trait method used to retag a pointer that has been loaded into a register. diff --git a/compiler/rustc_codegen_ssa/src/traits/misc.rs b/compiler/rustc_codegen_ssa/src/traits/misc.rs index 6a0f889833492..92ddc1f347994 100644 --- a/compiler/rustc_codegen_ssa/src/traits/misc.rs +++ b/compiler/rustc_codegen_ssa/src/traits/misc.rs @@ -3,6 +3,7 @@ use std::cell::RefCell; use rustc_data_structures::fx::FxHashMap; use rustc_middle::ty::{self, Instance, Ty}; use rustc_session::Session; +use rustc_span::Symbol; use super::BackendTypes; @@ -26,4 +27,10 @@ pub trait MiscCodegenMethods<'tcx>: BackendTypes { /// Declares the extern "C" main function for the entry point. Returns None if the symbol /// already exists. fn declare_c_main(&self, fn_type: Self::FunctionSignature) -> Option; + + /// Whether `codegen_intrinsic_call` expects to always have a `place_value` + /// when emitting code for the intrinsic `name`. + /// + /// This is discouraged, but here for now to simplify migration to using OperandValues + fn intrinsic_call_expects_place_always(&self, name: Symbol) -> bool; } diff --git a/tests/codegen-llvm/array-equality.rs b/tests/codegen-llvm/array-equality.rs index 8e4c170e4e674..385b7d7803594 100644 --- a/tests/codegen-llvm/array-equality.rs +++ b/tests/codegen-llvm/array-equality.rs @@ -9,8 +9,8 @@ #[no_mangle] pub fn array_eq_value(a: [u16; 3], b: [u16; 3]) -> bool { // CHECK-NEXT: start: - // CHECK-NEXT: %2 = icmp eq i48 %0, %1 - // CHECK-NEXT: ret i1 %2 + // CHECK-NEXT: %_0 = icmp eq i48 %0, %1 + // CHECK-NEXT: ret i1 %_0 a == b } diff --git a/tests/codegen-llvm/atomicptr.rs b/tests/codegen-llvm/atomicptr.rs index 9d5e618fe76f2..9042f71e9442c 100644 --- a/tests/codegen-llvm/atomicptr.rs +++ b/tests/codegen-llvm/atomicptr.rs @@ -19,17 +19,20 @@ pub fn helper(_: usize) {} // CHECK-LABEL: @atomicptr_fetch_byte_add #[no_mangle] pub fn atomicptr_fetch_byte_add(a: &AtomicPtr, v: usize) -> *mut u8 { - // CHECK: llvm.lifetime.start + // CHECK: start // CHECK-NEXT: %[[RET:.*]] = atomicrmw add ptr %{{.*}}, [[USIZE]] %v - // CHECK-NEXT: inttoptr [[USIZE]] %[[RET]] to ptr + // CHECK-NEXT: %[[RETPTR:.*]] = inttoptr [[USIZE]] %[[RET]] to ptr + // CHECK-NEXT: ret ptr %[[RETPTR]] a.fetch_byte_add(v, Relaxed) } // CHECK-LABEL: @atomicptr_swap #[no_mangle] pub fn atomicptr_swap(a: &AtomicPtr, ptr: *mut u8) -> *mut u8 { + // CHECK: start // CHECK-NOT: ptrtoint - // CHECK: atomicrmw xchg ptr %{{.*}}, ptr %{{.*}} monotonic + // CHECK-NEXT: %[[RET:.*]] = atomicrmw xchg ptr %{{.*}}, ptr %{{.*}} monotonic // CHECK-NOT: inttoptr + // CHECK-NEXT: ret ptr %[[RET]] a.swap(ptr, Relaxed) } diff --git a/tests/codegen-llvm/dst-vtable-align-nonzero.rs b/tests/codegen-llvm/dst-vtable-align-nonzero.rs index 2eee91876683c..80bb55c705dd1 100644 --- a/tests/codegen-llvm/dst-vtable-align-nonzero.rs +++ b/tests/codegen-llvm/dst-vtable-align-nonzero.rs @@ -52,7 +52,7 @@ pub fn does_not_eliminate_runtime_check_when_align_2( // CHECK-LABEL: @align_load_from_align_of_val #[no_mangle] pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize { - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::mem::align_of_val(x) } @@ -60,7 +60,7 @@ pub fn align_load_from_align_of_val(x: &dyn Trait) -> usize { #[no_mangle] pub unsafe fn align_load_from_vtable_align_intrinsic(x: &dyn Trait) -> usize { let (data, vtable): (*const (), *const ()) = core::mem::transmute(x); - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::intrinsics::vtable_align(vtable) } diff --git a/tests/codegen-llvm/dst-vtable-size-range.rs b/tests/codegen-llvm/dst-vtable-size-range.rs index 670f5e8d553fa..92fd68ece6a08 100644 --- a/tests/codegen-llvm/dst-vtable-size-range.rs +++ b/tests/codegen-llvm/dst-vtable-size-range.rs @@ -20,7 +20,7 @@ pub fn generate_exclusive_bound() -> usize { // CHECK-LABEL: @size_load_from_size_of_val #[no_mangle] pub fn size_load_from_size_of_val(x: &dyn Trait) -> usize { - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META:![0-9]+]] core::mem::size_of_val(x) } @@ -28,7 +28,7 @@ pub fn size_load_from_size_of_val(x: &dyn Trait) -> usize { #[no_mangle] pub unsafe fn size_load_from_vtable_size_intrinsic(x: &dyn Trait) -> usize { let (data, vtable): (*const (), *const ()) = core::mem::transmute(x); - // CHECK: {{%[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] + // CHECK: {{%_?[0-9]+}} = load [[USIZE]], {{.+}} !range [[RANGE_META]] core::intrinsics::vtable_size(vtable) } diff --git a/tests/codegen-llvm/intrinsics/disjoint_bitor.rs b/tests/codegen-llvm/intrinsics/disjoint_bitor.rs index fc45439ee0b95..b9d17bc9281d0 100644 --- a/tests/codegen-llvm/intrinsics/disjoint_bitor.rs +++ b/tests/codegen-llvm/intrinsics/disjoint_bitor.rs @@ -8,14 +8,16 @@ use std::intrinsics::disjoint_bitor; // CHECK-LABEL: @disjoint_bitor_signed #[no_mangle] pub unsafe fn disjoint_bitor_signed(x: i32, y: i32) -> i32 { - // CHECK: or disjoint i32 %x, %y + // CHECK: [[TEMP:%.+]] = or disjoint i32 %x, %y + // CHECK: ret i32 [[TEMP]] disjoint_bitor(x, y) } // CHECK-LABEL: @disjoint_bitor_unsigned #[no_mangle] pub unsafe fn disjoint_bitor_unsigned(x: u64, y: u64) -> u64 { - // CHECK: or disjoint i64 %x, %y + // CHECK: [[TEMP:%.+]] = or disjoint i64 %x, %y + // CHECK: ret i64 [[TEMP]] disjoint_bitor(x, y) } @@ -25,6 +27,6 @@ pub unsafe fn disjoint_bitor_literal() -> u8 { // This is a separate check because even without any passes, // LLVM will fold so it's not an instruction, which can assert in LLVM. - // CHECK: store i8 3 + // CHECK: ret i8 3 disjoint_bitor(1, 2) } diff --git a/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs index 813a4ae1479ce..2ae45aec5a623 100644 --- a/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs +++ b/tests/codegen-llvm/intrinsics/size_and_align_of_val.rs @@ -13,41 +13,32 @@ use std::intrinsics::{align_of_val, size_of_val}; #[no_mangle] pub unsafe fn align_of_array(x: &[u16; 7]) -> usize { // CHECK: start: - // CHECK: %0 = alloca [8 x i8] - // CHECK: store i64 2, ptr %0 - // CHECK: [[R:%.+]] = load i64, ptr %0 - // CHECK: ret i64 [[R]] + // CHECK-NEXT: ret i64 2 align_of_val(x) } // CHECK-LABEL: @size_of_array( #[no_mangle] pub unsafe fn size_of_array(x: &[u16; 7]) -> usize { - // CHECK: %0 = alloca [8 x i8] - // CHECK: store i64 14, ptr %0 - // CHECK: [[R:%.+]] = load i64, ptr %0 - // CHECK: ret i64 [[R]] + // CHECK: start: + // CHECK-NEXT: ret i64 14 size_of_val(x) } // CHECK-LABEL: @align_of_slice( #[no_mangle] pub unsafe fn align_of_slice(x: &[u16]) -> usize { - // CHECK: %0 = alloca [8 x i8] - // CHECK: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 - // CHECK: store i64 2, ptr %0 - // CHECK: [[R:%.+]] = load i64, ptr %0 - // CHECK: ret i64 [[R]] + // CHECK: start: + // CHECK-NEXT: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK-NEXT: ret i64 2 align_of_val(x) } // CHECK-LABEL: @size_of_slice( #[no_mangle] pub unsafe fn size_of_slice(x: &[u16]) -> usize { - // CHECK: %0 = alloca [8 x i8] - // CHECK: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 - // CHECK: store i64 [[SIZE]], ptr %0 - // CHECK: [[R:%.+]] = load i64, ptr %0 - // CHECK: ret i64 [[R]] + // CHECK: start: + // CHECK-NEXT: [[SIZE:%.+]] = mul nuw nsw i64 %x.1, 2 + // CHECK-NEXT: ret i64 [[SIZE]] size_of_val(x) } diff --git a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs index 294262d81526f..4af0287fbc120 100644 --- a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs +++ b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-generic-bitmask.rs @@ -20,29 +20,29 @@ use std::intrinsics::simd::simd_bitmask; // CHECK-LABEL: @bitmask_int #[no_mangle] pub unsafe fn bitmask_int(x: i32x2) -> u8 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2 - // CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8 + // CHECK: %{{_?[0-9]+}} = zext i2 [[C]] to i8 simd_bitmask(x) } // CHECK-LABEL: @bitmask_uint #[no_mangle] pub unsafe fn bitmask_uint(x: u32x2) -> u8 { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> // CHECK: [[C:%[0-9]+]] = bitcast <2 x i1> [[B]] to i2 - // CHECK: %{{[0-9]+}} = zext i2 [[C]] to i8 + // CHECK: %{{_?[0-9]+}} = zext i2 [[C]] to i8 simd_bitmask(x) } // CHECK-LABEL: @bitmask_int16 #[no_mangle] pub unsafe fn bitmask_int16(x: i8x16) -> u16 { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1|2}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0|1}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: %{{[0-9]+}} = bitcast <16 x i1> [[B]] to i16 + // CHECK: %{{_?[0-9]+}} = bitcast <16 x i1> [[B]] to i16 // CHECK-NOT: zext simd_bitmask(x) } diff --git a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs index 79f00a6ed6032..7521ba1fcb573 100644 --- a/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs +++ b/tests/codegen-llvm/simd-intrinsic/simd-intrinsic-mask-reduce.rs @@ -22,39 +22,39 @@ pub type mask8x16 = Simd; // CHECK-LABEL: @reduce_any_32x2 #[no_mangle] pub unsafe fn reduce_any_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.or.v2i1(<2 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_any(x) } // CHECK-LABEL: @reduce_all_32x2 #[no_mangle] pub unsafe fn reduce_all_32x2(x: mask32x2) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|1}}, {{|splat \(i32 31\)}} + // CHECK: [[A:%[0-9]+]] = lshr <2 x i32> %{{x|0}}, {{|splat \(i32 31\)}} // CHECK: [[B:%[0-9]+]] = trunc <2 x i32> [[A]] to <2 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.and.v2i1(<2 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_all(x) } // CHECK-LABEL: @reduce_any_8x16 #[no_mangle] pub unsafe fn reduce_any_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.or.v16i1(<16 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_any(x) } // CHECK-LABEL: @reduce_all_8x16 #[no_mangle] pub unsafe fn reduce_all_8x16(x: mask8x16) -> bool { - // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|1}}, {{|splat \(i8 7\)}} + // CHECK: [[A:%[0-9]+]] = lshr <16 x i8> %{{x|0}}, {{|splat \(i8 7\)}} // CHECK: [[B:%[0-9]+]] = trunc <16 x i8> [[A]] to <16 x i1> - // CHECK: [[C:%[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) - // CHECK: %{{[0-9]+}} = zext i1 [[C]] to i8 + // CHECK: [[C:%_?[0-9]+]] = call i1 @llvm.vector.reduce.and.v16i1(<16 x i1> [[B]]) + // CHECK: ret i1 [[C]] simd_reduce_all(x) } diff --git a/tests/codegen-llvm/simd/aggregate-simd.rs b/tests/codegen-llvm/simd/aggregate-simd.rs index 57a301d634c81..2a31287fd9aea 100644 --- a/tests/codegen-llvm/simd/aggregate-simd.rs +++ b/tests/codegen-llvm/simd/aggregate-simd.rs @@ -88,11 +88,9 @@ pub fn transparent_simd_aggregate(x: [u32; 4]) -> u32 { // CHECK-LABEL: transparent_simd_aggregate // CHECK-NOT: alloca - // CHECK: %[[RET:.+]] = alloca [4 x i8] - // CHECK-NOT: alloca // CHECK: %a = load <4 x i32>, ptr %x, align 4 // CHECK: %[[TEMP:.+]] = extractelement <4 x i32> %a, i32 1 - // CHECK: store i32 %[[TEMP]], ptr %[[RET]] + // CHECK: ret i32 %[[TEMP]] unsafe { let a = Simd(x); From 8684b3a37a52d101037e234da345e77b03b77693 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 23 May 2026 14:55:20 +0900 Subject: [PATCH 127/174] stdarch-gen-hexagon-scalar: Do not emit inline(always) if target_feature is used --- library/stdarch/.github/workflows/main.yml | 6 +- .../crates/core_arch/src/hexagon/scalar.rs | 112 +++++++++--------- .../stdarch-gen-hexagon-scalar/src/main.rs | 4 +- 3 files changed, 64 insertions(+), 58 deletions(-) diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index aabe44a1c5721..42e0b573ea219 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -309,7 +309,7 @@ jobs: # Check that the generated files agree with the checked-in versions. check-stdarch-gen: needs: [style] - name: Check stdarch-gen-{arm, loongarch, hexagon} output + name: Check stdarch-gen-{arm, loongarch, hexagon, hexagon-scalar} output runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 @@ -331,6 +331,10 @@ jobs: run: | cargo run -p stdarch-gen-hexagon --release git diff --exit-code + - name: Check hexagon scalar + run: | + cargo run -p stdarch-gen-hexagon-scalar --release + git diff --exit-code # Run some tests with Miri. Most stdarch functions use platform-specific intrinsics # that Miri does not support. Also Miri is reltively slow. diff --git a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs index c906ec5166a19..477414de74b85 100644 --- a/library/stdarch/crates/core_arch/src/hexagon/scalar.rs +++ b/library/stdarch/crates/core_arch/src/hexagon/scalar.rs @@ -11425,7 +11425,7 @@ pub unsafe fn Q6_l2fetch_AP(rs: i32, rtt: i64) { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11440,7 +11440,7 @@ pub unsafe fn Q6_P_rol_PI(rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11455,7 +11455,7 @@ pub unsafe fn Q6_P_rolacc_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11470,7 +11470,7 @@ pub unsafe fn Q6_P_roland_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11485,7 +11485,7 @@ pub unsafe fn Q6_P_rolnac_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11500,7 +11500,7 @@ pub unsafe fn Q6_P_rolor_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU6 = 0))] @@ -11515,7 +11515,7 @@ pub unsafe fn Q6_P_rolxacc_PI(rxx: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11530,7 +11530,7 @@ pub unsafe fn Q6_R_rol_RI(rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11545,7 +11545,7 @@ pub unsafe fn Q6_R_rolacc_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11560,7 +11560,7 @@ pub unsafe fn Q6_R_roland_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11575,7 +11575,7 @@ pub unsafe fn Q6_R_rolnac_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11590,7 +11590,7 @@ pub unsafe fn Q6_R_rolor_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V60 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v60"))] #[rustc_legacy_const_generics(2)] #[cfg_attr(test, assert_instr(rol, IU5 = 0))] @@ -11605,7 +11605,7 @@ pub unsafe fn Q6_R_rolxacc_RI(rx: i32, rs: i32) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vabsdiffb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11618,7 +11618,7 @@ pub unsafe fn Q6_P_vabsdiffb_PP(rtt: i64, rss: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vabsdiffub))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11631,7 +11631,7 @@ pub unsafe fn Q6_P_vabsdiffub_PP(rtt: i64, rss: i64) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vsplatb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11644,7 +11644,7 @@ pub unsafe fn Q6_P_vsplatb_R(rs: i32) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vtrunehb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11657,7 +11657,7 @@ pub unsafe fn Q6_P_vtrunehb_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V62 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v62"))] #[cfg_attr(test, assert_instr(vtrunohb))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11670,7 +11670,7 @@ pub unsafe fn Q6_P_vtrunohb_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: ALU64 /// Execution Slots: SLOT23 /// Requires: V65 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v65"))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] pub unsafe fn Q6_p_not_any8_vcmpb_eq_PP(rss: i64, rtt: i64) -> i32 { @@ -11682,7 +11682,7 @@ pub unsafe fn Q6_p_not_any8_vcmpb_eq_PP(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(dfadd))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11695,7 +11695,7 @@ pub unsafe fn Q6_P_dfadd_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(dfsub))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11708,7 +11708,7 @@ pub unsafe fn Q6_P_dfsub_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[cfg_attr(test, assert_instr(mpyi))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11721,7 +11721,7 @@ pub unsafe fn Q6_R_mpyinac_RR(rx: i32, rs: i32, rt: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V66 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v66"))] #[rustc_legacy_const_generics(0, 1)] #[cfg_attr(test, assert_instr(mask, IU5 = 0, IU5_2 = 0))] @@ -11737,7 +11737,7 @@ pub unsafe fn Q6_R_mask_II() -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(clip, IU5 = 0))] @@ -11752,7 +11752,7 @@ pub unsafe fn Q6_R_clip_RI(rs: i32) -> i32 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(cround, IU6 = 0))] @@ -11767,7 +11767,7 @@ pub unsafe fn Q6_P_cround_PI(rss: i64) -> i64 { /// Instruction Type: S_3op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cround))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11780,7 +11780,7 @@ pub unsafe fn Q6_P_cround_PR(rss: i64, rt: i32) -> i64 { /// Instruction Type: S_2op /// Execution Slots: SLOT23 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[rustc_legacy_const_generics(1)] #[cfg_attr(test, assert_instr(vclip, IU5 = 0))] @@ -11795,7 +11795,7 @@ pub unsafe fn Q6_P_vclip_PI(rss: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmax))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11808,7 +11808,7 @@ pub unsafe fn Q6_P_dfmax_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmin))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11821,7 +11821,7 @@ pub unsafe fn Q6_P_dfmin_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyfix))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11834,7 +11834,7 @@ pub unsafe fn Q6_P_dfmpyfix_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyhh))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11847,7 +11847,7 @@ pub unsafe fn Q6_P_dfmpyhhacc_PP(rxx: f64, rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpylh))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11860,7 +11860,7 @@ pub unsafe fn Q6_P_dfmpylhacc_PP(rxx: f64, rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT23 /// Requires: V67 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67"))] #[cfg_attr(test, assert_instr(dfmpyll))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11873,7 +11873,7 @@ pub unsafe fn Q6_P_dfmpyll_PP(rss: f64, rtt: f64) -> f64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11886,7 +11886,7 @@ pub unsafe fn Q6_P_cmpyiw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11899,7 +11899,7 @@ pub unsafe fn Q6_P_cmpyiwacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11912,7 +11912,7 @@ pub unsafe fn Q6_P_cmpyiw_PP_conj(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11925,7 +11925,7 @@ pub unsafe fn Q6_P_cmpyiwacc_PP_conj(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11938,7 +11938,7 @@ pub unsafe fn Q6_P_cmpyrw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11951,7 +11951,7 @@ pub unsafe fn Q6_P_cmpyrwacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11964,7 +11964,7 @@ pub unsafe fn Q6_P_cmpyrw_PP_conj(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11977,7 +11977,7 @@ pub unsafe fn Q6_P_cmpyrwacc_PP_conj(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(vdmpyw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -11990,7 +11990,7 @@ pub unsafe fn Q6_P_vdmpyw_PP(rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(vdmpyw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12003,7 +12003,7 @@ pub unsafe fn Q6_P_vdmpywacc_PP(rxx: i64, rss: i64, rtt: i64) -> i64 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12016,7 +12016,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12029,7 +12029,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12042,7 +12042,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_conj_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyiw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12055,7 +12055,7 @@ pub unsafe fn Q6_R_cmpyiw_PP_conj_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12068,7 +12068,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12081,7 +12081,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12094,7 +12094,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_conj_s1_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: M /// Execution Slots: SLOT3 /// Requires: V67, Audio -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v67,audio"))] #[cfg_attr(test, assert_instr(cmpyrw))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12107,7 +12107,7 @@ pub unsafe fn Q6_R_cmpyrw_PP_conj_s1_rnd_sat(rss: i64, rtt: i64) -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmlink))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12120,7 +12120,7 @@ pub unsafe fn Q6_dmlink_AA(rs: i32, rt: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmpause))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12133,7 +12133,7 @@ pub unsafe fn Q6_R_dmpause() -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmpoll))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12146,7 +12146,7 @@ pub unsafe fn Q6_R_dmpoll() -> i32 { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmresume))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12159,7 +12159,7 @@ pub unsafe fn Q6_dmresume_A(rs: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmstart))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] @@ -12172,7 +12172,7 @@ pub unsafe fn Q6_dmstart_A(rs: i32) { /// Instruction Type: ST /// Execution Slots: SLOT0 /// Requires: V68 -#[inline(always)] +#[inline] #[cfg_attr(target_arch = "hexagon", target_feature(enable = "v68"))] #[cfg_attr(test, assert_instr(dmwait))] #[unstable(feature = "stdarch_hexagon", issue = "151523")] diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs index bbe28174ffa05..3e3f00bb7fb02 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs @@ -513,9 +513,11 @@ fn generate_functions(intrinsics: &[ScalarIntrinsic]) -> String { } // Attributes - output.push_str("#[inline(always)]\n"); if let Some(tf_attr) = info.arch_guard.target_feature_attr() { + output.push_str("#[inline]\n"); // https://github.com/rust-lang/rust/issues/145574 output.push_str(&format!("{}\n", tf_attr)); + } else { + output.push_str("#[inline(always)]\n"); } // Immediate parameters become const generics but are passed as positional From 8d74557a0c03f631ca056ebce5842cd7e5d3a696 Mon Sep 17 00:00:00 2001 From: Taiki Endo Date: Sat, 23 May 2026 15:36:16 +0900 Subject: [PATCH 128/174] Workaround rustfmt issue around impl const --- library/stdarch/crates/core_arch/src/loongarch64/simd.rs | 1 + library/stdarch/crates/core_arch/src/simd.rs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs index b4ec6881c36ab..b102e137f1133 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/simd.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/simd.rs @@ -14,6 +14,7 @@ pub(super) const trait SimdExt: Sized { unsafe fn splat(v: i64) -> Self; } +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 macro_rules! impl_simd_ext { ($v:ident, $e:ty) => { #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] diff --git a/library/stdarch/crates/core_arch/src/simd.rs b/library/stdarch/crates/core_arch/src/simd.rs index 2c6829b465c42..9a756eee446d2 100644 --- a/library/stdarch/crates/core_arch/src/simd.rs +++ b/library/stdarch/crates/core_arch/src/simd.rs @@ -87,6 +87,7 @@ impl Clone for Simd { } #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 impl const crate::cmp::PartialEq for Simd { #[inline] fn eq(&self, other: &Self) -> bool { @@ -299,6 +300,7 @@ impl Clone for SimdM { } #[rustc_const_unstable(feature = "stdarch_const_helpers", issue = "none")] +#[rustfmt::skip] // FIXME: https://github.com/rust-lang/stdarch/pull/2133#issuecomment-4524350350 impl const crate::cmp::PartialEq for SimdM { #[inline] fn eq(&self, other: &Self) -> bool { From 8604f001a96a05f58cadfb4695b39e9458ac3f1e Mon Sep 17 00:00:00 2001 From: Sappho de Nooij Date: Fri, 22 May 2026 10:40:14 +0200 Subject: [PATCH 129/174] Fix suggestion of unused variables with raw identifier in struct pattern This commit fixes a broken suggestion that occurs when a struct pattern contains an unused variable written with a raw identifier. --- compiler/rustc_mir_transform/src/errors.rs | 2 +- compiler/rustc_mir_transform/src/liveness.rs | 2 +- ...ith-raw-identifier-in-struct-pattern.fixed | 19 +++++++++++++++++++ ...e-with-raw-identifier-in-struct-pattern.rs | 19 +++++++++++++++++++ ...th-raw-identifier-in-struct-pattern.stderr | 15 +++++++++++++++ 5 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs create mode 100644 tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr diff --git a/compiler/rustc_mir_transform/src/errors.rs b/compiler/rustc_mir_transform/src/errors.rs index 39c85489f939a..9e605479bd0e8 100644 --- a/compiler/rustc_mir_transform/src/errors.rs +++ b/compiler/rustc_mir_transform/src/errors.rs @@ -290,7 +290,7 @@ pub(crate) enum UnusedVariableSugg { shorthands: Vec, #[suggestion_part(code = "_")] non_shorthands: Vec, - name: Symbol, + name: String, }, #[multipart_suggestion( diff --git a/compiler/rustc_mir_transform/src/liveness.rs b/compiler/rustc_mir_transform/src/liveness.rs index a1b2a2853c0bd..c449cf6867395 100644 --- a/compiler/rustc_mir_transform/src/liveness.rs +++ b/compiler/rustc_mir_transform/src/liveness.rs @@ -1068,7 +1068,7 @@ impl<'a, 'tcx> AssignmentResult<'a, 'tcx> { let sugg = if any_shorthand { errors::UnusedVariableSugg::TryIgnore { - name, + name: name.to_ident_string(), shorthands: introductions .iter() .filter_map( diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed new file mode 100644 index 0000000000000..9e188cfdcc0e3 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.fixed @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move: _ } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs new file mode 100644 index 0000000000000..514ba4f7df418 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.rs @@ -0,0 +1,19 @@ +//@ check-pass +//@ run-rustfix + +#![warn(unused)] +#![allow(dead_code)] + +struct Foo { + r#move: u32 +} + +fn main() { + let y = Foo { r#move: 3 }; + + let _ = match y { + Foo { r#move } => 0 //~ WARNING unused variable: `r#move` + //~| HELP try ignoring the field + //~| SUGGESTION r#move: _ + }; +} diff --git a/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr new file mode 100644 index 0000000000000..0f96605651616 --- /dev/null +++ b/tests/ui/lint/unused/unused-variable-with-raw-identifier-in-struct-pattern.stderr @@ -0,0 +1,15 @@ +warning: unused variable: `r#move` + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:15:16 + | +LL | Foo { r#move } => 0 + | ^^^^^^ help: try ignoring the field: `r#move: _` + | +note: the lint level is defined here + --> $DIR/unused-variable-with-raw-identifier-in-struct-pattern.rs:4:9 + | +LL | #![warn(unused)] + | ^^^^^^ + = note: `#[warn(unused_variables)]` implied by `#[warn(unused)]` + +warning: 1 warning emitted + From 47bcee0a83b282c3f783d03437ed0236c10fb4af Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 May 2026 12:22:43 +0200 Subject: [PATCH 130/174] Fix invalid cg_gcc `panic` function cast --- compiler/rustc_codegen_gcc/src/intrinsic/mod.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs index 6fb25174bf1e0..a244a9c493ad8 100644 --- a/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs +++ b/compiler/rustc_codegen_gcc/src/intrinsic/mod.rs @@ -1352,7 +1352,10 @@ fn try_intrinsic<'a, 'b, 'gcc, 'tcx>( dest: PlaceRef<'tcx, RValue<'gcc>>, ) { if !bx.sess().panic_strategy().unwinds() { - bx.call(bx.type_void(), None, None, try_func, &[data], None, None); + let param_type = bx.u8_type.make_pointer(); + let fn_type = + bx.context.new_function_pointer_type(None, bx.type_void(), &[param_type], false); + bx.call(fn_type, None, None, try_func, &[data], None, None); // Return 0 unconditionally from the intrinsic call; // we can never unwind. OperandValue::Immediate(bx.const_i32(0)).store(bx, dest); From b6eb2659025a94c3eced1fdf27b5482476cc3d15 Mon Sep 17 00:00:00 2001 From: Haoran Wang Date: Sat, 23 May 2026 08:42:56 +0800 Subject: [PATCH 131/174] Keep redundant-capture suggestions syntactically valid The lint suggestion now removes the `+` joiner adjacent to a redundant `use<...>` clause, so applying the suggestion does not leave a dangling `+` that fails to parse. The existing `tests/ui/impl-trait/precise-capturing/redundant.stderr` baseline emits the same diagnostic with the expanded suggestion span and is blessed along with the new `run-rustfix` test. Tested: - ./x test tests/ui/impl-trait/precise-capturing/redundant.rs --force-rerun - ./x test tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs --force-rerun - ./x test tidy --- .../rustc_lint/src/impl_trait_overcaptures.rs | 22 ++++++++++--- .../redundant-machine-applicable.fixed | 28 ++++++++++++++++ .../redundant-machine-applicable.rs | 28 ++++++++++++++++ .../redundant-machine-applicable.stderr | 32 +++++++++++++++++++ .../precise-capturing/redundant.stderr | 24 +++++++------- 5 files changed, 118 insertions(+), 16 deletions(-) create mode 100644 tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed create mode 100644 tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs create mode 100644 tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr diff --git a/compiler/rustc_lint/src/impl_trait_overcaptures.rs b/compiler/rustc_lint/src/impl_trait_overcaptures.rs index 65dfa8b93de78..76d55030b1312 100644 --- a/compiler/rustc_lint/src/impl_trait_overcaptures.rs +++ b/compiler/rustc_lint/src/impl_trait_overcaptures.rs @@ -361,9 +361,9 @@ where // have no uncaptured args, then we should warn to the user that // it's redundant to capture all args explicitly. if new_capture_rules - && let Some((captured_args, capturing_span)) = - opaque.bounds.iter().find_map(|bound| match *bound { - hir::GenericBound::Use(a, s) => Some((a, s)), + && let Some((use_idx, captured_args, capturing_span)) = + opaque.bounds.iter().enumerate().find_map(|(i, bound)| match *bound { + hir::GenericBound::Use(a, s) => Some((i, a, s)), _ => None, }) { @@ -400,11 +400,25 @@ where .iter() .all(|(def_id, _)| explicitly_captured.contains(def_id)) { + // Extend the removal span to include the `+` joiner adjacent + // to `use<...>`, so applying the suggestion does not leave + // behind a stray `+` that fails to parse. + let suggestion_span = if let Some(next) = opaque.bounds.get(use_idx + 1) { + capturing_span.with_hi(next.span().lo()) + } else if let Some(prev_idx) = use_idx.checked_sub(1) { + let prev = opaque.bounds[prev_idx]; + capturing_span.with_lo(prev.span().hi()) + } else { + // `impl use<...>` with no other bound is not valid + // syntax, so this branch is unreachable in practice. + capturing_span + }; + self.tcx.emit_node_span_lint( IMPL_TRAIT_REDUNDANT_CAPTURES, self.tcx.local_def_id_to_hir_id(opaque_def_id), opaque_span, - ImplTraitRedundantCapturesLint { capturing_span }, + ImplTraitRedundantCapturesLint { capturing_span: suggestion_span }, ); } } diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed new file mode 100644 index 0000000000000..d8a7858e7659b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.fixed @@ -0,0 +1,28 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ edition: 2024 + +// Verify that the suggestion produced by `impl_trait_redundant_captures` +// removes the adjacent `+` joiner along with `use<...>`, instead of leaving +// behind a stray `+` that fails to compile. Regression test for +// https://github.com/rust-lang/rust/issues/143216. + +#![allow(unused)] +#![deny(impl_trait_redundant_captures)] + +// `use<>` at the end of the bound list: the suggestion must remove the +// preceding `+`. +fn end_position() -> impl Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` at the start of the bound list: the suggestion must remove the +// following `+`. +fn start_position() -> impl Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` in the middle of the bound list: the suggestion must remove +// exactly one `+`, keeping the other to join the remaining bounds. +fn middle_position() -> impl Sized + Send {} +//~^ ERROR all possible in-scope parameters are already captured + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs new file mode 100644 index 0000000000000..c6b2d4cba1c25 --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.rs @@ -0,0 +1,28 @@ +//@ run-rustfix +//@ rustfix-only-machine-applicable +//@ edition: 2024 + +// Verify that the suggestion produced by `impl_trait_redundant_captures` +// removes the adjacent `+` joiner along with `use<...>`, instead of leaving +// behind a stray `+` that fails to compile. Regression test for +// https://github.com/rust-lang/rust/issues/143216. + +#![allow(unused)] +#![deny(impl_trait_redundant_captures)] + +// `use<>` at the end of the bound list: the suggestion must remove the +// preceding `+`. +fn end_position() -> impl Sized + use<> {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` at the start of the bound list: the suggestion must remove the +// following `+`. +fn start_position() -> impl use<> + Sized {} +//~^ ERROR all possible in-scope parameters are already captured + +// `use<>` in the middle of the bound list: the suggestion must remove +// exactly one `+`, keeping the other to join the remaining bounds. +fn middle_position() -> impl Sized + use<> + Send {} +//~^ ERROR all possible in-scope parameters are already captured + +fn main() {} diff --git a/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr new file mode 100644 index 0000000000000..f07fc4905680b --- /dev/null +++ b/tests/ui/impl-trait/precise-capturing/redundant-machine-applicable.stderr @@ -0,0 +1,32 @@ +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:15:22 + | +LL | fn end_position() -> impl Sized + use<> {} + | ^^^^^^^^^^-------- + | | + | help: remove the `use<...>` syntax + | +note: the lint level is defined here + --> $DIR/redundant-machine-applicable.rs:11:9 + | +LL | #![deny(impl_trait_redundant_captures)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:20:24 + | +LL | fn start_position() -> impl use<> + Sized {} + | ^^^^^--------^^^^^ + | | + | help: remove the `use<...>` syntax + +error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant + --> $DIR/redundant-machine-applicable.rs:25:25 + | +LL | fn middle_position() -> impl Sized + use<> + Send {} + | ^^^^^^^^^^^^^--------^^^^ + | | + | help: remove the `use<...>` syntax + +error: aborting due to 3 previous errors + diff --git a/tests/ui/impl-trait/precise-capturing/redundant.stderr b/tests/ui/impl-trait/precise-capturing/redundant.stderr index c9f84d360e3c6..62724fc01976a 100644 --- a/tests/ui/impl-trait/precise-capturing/redundant.stderr +++ b/tests/ui/impl-trait/precise-capturing/redundant.stderr @@ -2,9 +2,9 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt --> $DIR/redundant.rs:5:19 | LL | fn hello<'a>() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax | note: the lint level is defined here --> $DIR/redundant.rs:3:9 @@ -16,25 +16,25 @@ error: all possible in-scope parameters are already captured, so `use<...>` synt --> $DIR/redundant.rs:10:27 | LL | fn inherent(&self) -> impl Sized + use<'_> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:15:22 | LL | fn in_trait() -> impl Sized + use<'a, Self>; - | ^^^^^^^^^^^^^------------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------------- + | | + | help: remove the `use<...>` syntax error: all possible in-scope parameters are already captured, so `use<...>` syntax is redundant --> $DIR/redundant.rs:19:22 | LL | fn in_trait() -> impl Sized + use<'a> {} - | ^^^^^^^^^^^^^------- - | | - | help: remove the `use<...>` syntax + | ^^^^^^^^^^---------- + | | + | help: remove the `use<...>` syntax error: aborting due to 4 previous errors From 766428b312eff521e6f983828bc3a7177a32ff01 Mon Sep 17 00:00:00 2001 From: Makai Date: Thu, 21 May 2026 17:54:59 +0800 Subject: [PATCH 132/174] Avoid regression in `derive(PartialOrd)` for single fieldless variant enums --- .../src/deriving/cmp/partial_ord.rs | 7 ++++ ...mp.runtime-optimized.after.panic-abort.mir | 41 ++----------------- tests/ui/derives/deriving-all-codegen.stdout | 2 +- 3 files changed, 11 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs index 5a66c2871db2e..d66d14bb881a1 100644 --- a/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs +++ b/compiler/rustc_builtin_macros/src/deriving/cmp/partial_ord.rs @@ -59,9 +59,16 @@ pub(crate) fn expand_deriving_partial_ord( Annotatable::Item(annitem) => match &annitem.kind { // For unit structs/zero-variant enums, the default generated code is better. ItemKind::Struct(.., ast::VariantData::Unit(..)) => (false, default_substructure), + // Also for single fieldless variant enum ItemKind::Enum(.., enum_def) if enum_def.variants.is_empty() => { (false, default_substructure) } + ItemKind::Enum(.., enum_def) + if enum_def.variants.len() == 1 + && matches!(enum_def.variants[0].data, ast::VariantData::Unit(..)) => + { + (false, default_substructure) + } ItemKind::Struct(_, ast::Generics { params, .. }, _) | ItemKind::Enum(_, ast::Generics { params, .. }, _) if is_simple_candidate(params) => diff --git a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir index 85f7e76eea8c6..e018894cf3f95 100644 --- a/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir +++ b/tests/mir-opt/pre-codegen/derived_ord_debug.{impl#0}-partial_cmp.runtime-optimized.after.panic-abort.mir @@ -4,49 +4,14 @@ fn ::partial_cmp(_1: &MultiField, debug self => _1; debug other => _2; let mut _0: std::option::Option; - let _3: &char; - let _4: &char; - let mut _5: std::option::Option; - let mut _6: isize; - let mut _7: i8; - let _8: &i16; - let _9: &i16; - scope 1 { - debug cmp => _5; - } + let mut _3: std::cmp::Ordering; bb0: { - _3 = &((*_1).0: char); - _4 = &((*_2).0: char); - _5 = ::partial_cmp(copy _3, copy _4) -> [return: bb1, unwind unreachable]; + _3 = ::cmp(copy _1, copy _2) -> [return: bb1, unwind unreachable]; } bb1: { - _6 = discriminant(_5); - switchInt(move _6) -> [1: bb2, 0: bb4, otherwise: bb6]; - } - - bb2: { - _7 = discriminant(((_5 as Some).0: std::cmp::Ordering)); - switchInt(move _7) -> [0: bb3, otherwise: bb4]; - } - - bb3: { - _8 = &((*_1).1: i16); - _9 = &((*_2).1: i16); - _0 = ::partial_cmp(copy _8, copy _9) -> [return: bb5, unwind unreachable]; - } - - bb4: { - _0 = copy _5; - goto -> bb5; - } - - bb5: { + _0 = Option::::Some(move _3); return; } - - bb6: { - unreachable; - } } diff --git a/tests/ui/derives/deriving-all-codegen.stdout b/tests/ui/derives/deriving-all-codegen.stdout index 0fbcda77bba90..83a3bebceeb0c 100644 --- a/tests/ui/derives/deriving-all-codegen.stdout +++ b/tests/ui/derives/deriving-all-codegen.stdout @@ -1149,7 +1149,7 @@ impl ::core::cmp::PartialOrd for Fieldless1 { #[inline] fn partial_cmp(&self, other: &Fieldless1) -> ::core::option::Option<::core::cmp::Ordering> { - ::core::option::Option::Some(::core::cmp::Ord::cmp(self, other)) + ::core::option::Option::Some(::core::cmp::Ordering::Equal) } } #[automatically_derived] From 383850dddc0ef79d8508590bbe3e29b0d478e8bf Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 23 May 2026 12:50:05 +0200 Subject: [PATCH 133/174] Add CI check when building sysroot with GCC backend and running libcore tests with it --- .../x86_64-gnu-gcc-core-tests/Dockerfile | 45 +++++++++++++++++++ src/ci/github-actions/jobs.yml | 7 +++ 2 files changed, 52 insertions(+) create mode 100644 src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile new file mode 100644 index 0000000000000..e1ac8e5bb8176 --- /dev/null +++ b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile @@ -0,0 +1,45 @@ +FROM ubuntu:22.04 + +ARG DEBIAN_FRONTEND=noninteractive +RUN apt-get update && apt-get install -y --no-install-recommends \ + bzip2 \ + g++ \ + make \ + ninja-build \ + file \ + curl \ + ca-certificates \ + python3 \ + git \ + cmake \ + sudo \ + gdb \ + libssl-dev \ + pkg-config \ + xz-utils \ + mingw-w64 \ + zlib1g-dev \ + libzstd-dev \ + # libgccjit dependencies + flex \ + libmpfr-dev \ + libgmp-dev \ + libmpc3 \ + libmpc-dev \ + && rm -rf /var/lib/apt/lists/* + +COPY scripts/sccache.sh /scripts/ +RUN sh /scripts/sccache.sh + +ENV NO_DEBUG_ASSERTIONS="1" +ENV RUSTFLAGS="-Cpanic=abort -Zpanic-abort-tests" +ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ + --enable-sanitizers \ + --enable-profiler \ + --enable-compiler-docs \ + --set llvm.libzstd=true \ + --set rust.codegen-backends=[\\\"gcc\\\"]" +ENV SCRIPT="python3 ../x.py \ + --stage 1 \ + test library/coretests \ + --set rust.codegen-backends=[\\\"gcc\\\"]" diff --git a/src/ci/github-actions/jobs.yml b/src/ci/github-actions/jobs.yml index 448a2c927b394..6587a85ddcec1 100644 --- a/src/ci/github-actions/jobs.yml +++ b/src/ci/github-actions/jobs.yml @@ -145,6 +145,9 @@ pr: - name: x86_64-gnu-gcc doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html <<: *job-linux-4c + - name: x86_64-gnu-gcc-core-tests + doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html + <<: *job-linux-4c # Jobs that run when you perform a try build (@bors try) # These jobs automatically inherit envs.try, to avoid repeating @@ -362,6 +365,10 @@ auto: doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html <<: *job-linux-4c + - name: x86_64-gnu-gcc-core-tests + doc_url: https://rustc-dev-guide.rust-lang.org/tests/codegen-backend-tests/cg_gcc.html + <<: *job-linux-4c + # This job ensures commits landing on nightly still pass the full # test suite on the stable channel. There are some UI tests that # depend on the channel being built (for example if they include the From 2f953da599463b6dda06d4948fc23bc301e0a6ce Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Sat, 23 May 2026 17:33:22 +0200 Subject: [PATCH 134/174] Remove unnecessary Share feature gate plumbing --- compiler/rustc_feature/src/unstable.rs | 2 -- compiler/rustc_span/src/symbol.rs | 1 - tests/ui/feature-gates/feature-gate-share-trait.rs | 5 ----- 3 files changed, 8 deletions(-) delete mode 100644 tests/ui/feature-gates/feature-gate-share-trait.rs diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index afb535112e11e..8202f4c31c910 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -711,8 +711,6 @@ declare_features! ( (unstable, s390x_target_feature, "1.82.0", Some(150259)), /// Allows the use of the `sanitize` attribute. (unstable, sanitize, "1.91.0", Some(39699)), - /// Allows use of the `Share` trait. - (unstable, share_trait, "CURRENT_RUSTC_VERSION", Some(156756)), /// Allows the use of SIMD types in functions declared in `extern` blocks. (unstable, simd_ffi, "1.0.0", Some(27731)), /// Target features on sparc. diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3182574049388..72339efd0a132 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -1841,7 +1841,6 @@ symbols! { sha512_sm_x86, shadow_call_stack, shallow, - share_trait, shl, shl_assign, shorter_tail_lifetimes, diff --git a/tests/ui/feature-gates/feature-gate-share-trait.rs b/tests/ui/feature-gates/feature-gate-share-trait.rs deleted file mode 100644 index 664093948e6e0..0000000000000 --- a/tests/ui/feature-gates/feature-gate-share-trait.rs +++ /dev/null @@ -1,5 +0,0 @@ -//@ check-pass - -#![feature(share_trait)] - -fn main() {} From 0c7388c750d537b7670d58c1ab480829b7960d39 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 19 May 2026 04:03:55 +0200 Subject: [PATCH 135/174] Fix jump to def link generation on primitive type associated methods --- src/librustdoc/clean/types.rs | 36 ++++++++++++++++++- src/librustdoc/html/format.rs | 35 ++++++++++-------- tests/rustdoc-html/jump-to-def/prim-method.rs | 16 +++++++++ 3 files changed, 71 insertions(+), 16 deletions(-) create mode 100644 tests/rustdoc-html/jump-to-def/prim-method.rs diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 2fe0c8d8776dd..2f992c622c496 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -20,7 +20,7 @@ use rustc_index::IndexVec; use rustc_metadata::rendered_const; use rustc_middle::span_bug; use rustc_middle::ty::fast_reject::SimplifiedType; -use rustc_middle::ty::{self, TyCtxt, Visibility}; +use rustc_middle::ty::{self, Ty, TyCtxt, Visibility}; use rustc_resolve::rustdoc::{ DocFragment, add_doc_fragment, attrs_to_doc_fragments, inner_docs, span_of_fragments, }; @@ -1758,6 +1758,40 @@ impl PrimitiveType { } } + pub(crate) fn from_ty(ty: Ty<'_>) -> Option { + match ty.kind() { + ty::Array(..) => Some(Self::Array), + ty::Bool => Some(Self::Bool), + ty::Char => Some(Self::Char), + ty::FnDef(..) | ty::FnPtr(..) => Some(Self::Fn), + ty::Int(int) => Some(Self::from(*int)), + ty::Uint(uint) => Some(Self::from(*uint)), + ty::Float(float) => Some(Self::from(*float)), + ty::Never => Some(Self::Never), + ty::Pat(..) => Some(Self::Pat), + ty::RawPtr(..) => Some(Self::RawPointer), + ty::Ref(..) => Some(Self::Reference), + ty::Slice(..) => Some(Self::Slice), + ty::Str => Some(Self::Str), + ty::Tuple(elems) if elems.is_empty() => Some(Self::Unit), + ty::Tuple(_) => Some(Self::Tuple), + ty::Adt(..) + | ty::Alias(..) + | ty::Bound(..) + | ty::Closure(..) + | ty::Coroutine(..) + | ty::CoroutineClosure(..) + | ty::CoroutineWitness(..) + | ty::Dynamic(..) + | ty::Error(..) + | ty::Foreign(..) + | ty::Infer(..) + | ty::Param(..) + | ty::Placeholder(..) + | ty::UnsafeBinder(..) => None, + } + } + pub(crate) fn simplified_types() -> &'static SimplifiedTypes { use PrimitiveType::*; use ty::{FloatTy, IntTy, UintTy}; diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 35212d480cfdd..e6c64ef8b4220 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -9,8 +9,7 @@ use std::cmp::Ordering; use std::fmt::{self, Display, Write}; -use std::iter::{self, once}; -use std::slice; +use std::{iter, slice}; use itertools::{Either, Itertools}; use rustc_abi::ExternAbi; @@ -434,27 +433,33 @@ fn generate_item_def_id_path( let tcx = cx.tcx(); let crate_name = tcx.crate_name(def_id.krate); + let mut prim = None; // No need to try to infer the actual parent item if it's not an associated item from the `impl` // block. if def_id != original_def_id && matches!(tcx.def_kind(def_id), DefKind::Impl { .. }) { let infcx = tcx.infer_ctxt().build(TypingMode::non_body_analysis()); - def_id = infcx + let ty = tcx.type_of(def_id); + let ty = infcx .at(&ObligationCause::dummy(), tcx.param_env(def_id)) - .query_normalize(ty::Binder::dummy( - tcx.type_of(def_id).instantiate_identity().skip_norm_wip(), - )) - .map(|resolved| infcx.resolve_vars_if_possible(resolved.value)) - .ok() - .and_then(|normalized| normalized.skip_binder().ty_adt_def()) - .map(|adt| adt.did()) - .unwrap_or(def_id); + .query_normalize(ty::Binder::dummy(ty.instantiate_identity().skip_norm_wip())) + .map(|resolved| infcx.resolve_vars_if_possible(resolved.value).skip_binder()) + .unwrap_or(ty.skip_binder()); + if let Some(new_def_id) = ty.ty_adt_def().map(|adt| adt.did()) { + def_id = new_def_id; + } else { + prim = PrimitiveType::from_ty(ty); + } } - let relative = clean::inline::item_relative_path(tcx, def_id); - let fqp: Vec = once(crate_name).chain(relative).collect(); - - let shortty = ItemType::from_def_id(def_id, tcx); + let mut fqp = vec![crate_name]; + let shortty = if let Some(prim) = prim { + fqp.push(prim.as_sym()); + ItemType::Primitive + } else { + fqp.append(&mut clean::inline::item_relative_path(tcx, def_id)); + ItemType::from_def_id(def_id, tcx) + }; let module_fqp = to_module_fqp(shortty, &fqp); let (parts, is_absolute) = url_parts(cx.cache(), def_id, module_fqp, &cx.current)?; diff --git a/tests/rustdoc-html/jump-to-def/prim-method.rs b/tests/rustdoc-html/jump-to-def/prim-method.rs new file mode 100644 index 0000000000000..43f6592de5390 --- /dev/null +++ b/tests/rustdoc-html/jump-to-def/prim-method.rs @@ -0,0 +1,16 @@ +// Checks that links to primitive types methods work. +// Regression test for . + +// ignore-tidy-linelength +//@ compile-flags: -Zunstable-options --generate-link-to-definition + +#![crate_name = "foo"] + +//@ has 'src/foo/prim-method.rs.html' + +fn scope() { + //@ has - '//a[@href="{{channel}}/core/primitive.usize.html#method.saturating_add"]' 'saturating_add' + let _ = 0usize.saturating_add(1); + //@ has - '//a[@href="{{channel}}/core/primitive.bool.html#method.then_some"]' 'then_some' + let _ = false.then_some(()); +} From 6ed5bf2f529dc103a9cecfd237d445643c20ec30 Mon Sep 17 00:00:00 2001 From: fallofpheonix Date: Sun, 24 May 2026 00:43:32 +0530 Subject: [PATCH 136/174] Remove po checks from debuginfo path test --- tests/debuginfo/path.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/debuginfo/path.rs b/tests/debuginfo/path.rs index 547ab4d43b7cb..2fffadd8510ce 100644 --- a/tests/debuginfo/path.rs +++ b/tests/debuginfo/path.rs @@ -7,9 +7,9 @@ //@ lldb-command:run //@ lldb-command:print pathbuf -//@ lldb-check:[...] "/some/path" [...] +//@ lldb-check:[...] "/some/path" { inner = "/some/path" { inner = { inner = size=10 { [0] = '/' [1] = 's' [2] = 'o' [3] = 'm' [4] = 'e' [5] = '/' [6] = 'p' [7] = 'a' [8] = 't' [9] = 'h' } } } } //@ lldb-command:print path -//@ lldb-check:[...] "/some/path" [...] +//@ lldb-check:[...] "/some/path" { data_ptr = [...] length = 10 } use std::path::Path; From 0ff74c976a8e0014b1e394735696a8e6c1c594b8 Mon Sep 17 00:00:00 2001 From: Pieter-Louis Schoeman Date: Sat, 23 May 2026 21:24:21 +0200 Subject: [PATCH 137/174] Fix Pieter-Louis Schoeman mailmap entry --- .mailmap | 1 + 1 file changed, 1 insertion(+) diff --git a/.mailmap b/.mailmap index 4a3e39831e8a2..3fc567081c1ff 100644 --- a/.mailmap +++ b/.mailmap @@ -569,6 +569,7 @@ Philipp Matthias Schäfer phosphorus Pierre Krieger pierwill <19642016+pierwill@users.noreply.github.com> +Pieter-Louis Schoeman <127837395+P8L1@users.noreply.github.com> Pietro Albini Pietro Albini Pradyumna Rahul From adbf6f56dccce8e3c2162894aafde9d5e50bd163 Mon Sep 17 00:00:00 2001 From: Ben Kimock Date: Sat, 23 May 2026 06:13:29 -0400 Subject: [PATCH 138/174] Re-add call site inlining attributes --- compiler/rustc_codegen_llvm/src/attributes.rs | 30 +++++++------- compiler/rustc_codegen_llvm/src/builder.rs | 15 +++++++ .../call-site-inline-attributes.rs | 40 +++++++++++++++++++ 3 files changed, 71 insertions(+), 14 deletions(-) create mode 100644 tests/codegen-llvm/call-site-inline-attributes.rs diff --git a/compiler/rustc_codegen_llvm/src/attributes.rs b/compiler/rustc_codegen_llvm/src/attributes.rs index ae14a72191d0e..f4a15b7b40267 100644 --- a/compiler/rustc_codegen_llvm/src/attributes.rs +++ b/compiler/rustc_codegen_llvm/src/attributes.rs @@ -5,7 +5,7 @@ use rustc_hir::find_attr; use rustc_middle::middle::codegen_fn_attrs::{ CodegenFnAttrFlags, CodegenFnAttrs, PatchableFunctionEntry, SanitizerFnAttrs, TargetFeature, }; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::{self, Instance, TyCtxt}; use rustc_session::config::{BranchProtection, FunctionReturn, OptLevel, PAuthKey, PacRet}; use rustc_span::sym; use rustc_symbol_mangling::mangle_internal_symbol; @@ -42,22 +42,31 @@ pub(crate) fn remove_string_attr_from_llfn(llfn: &Value, name: &str) { /// Get LLVM attribute for the provided inline heuristic. #[inline] -fn inline_attr<'ll>( +pub(crate) fn inline_attr<'tcx, 'll>( cx: &SimpleCx<'ll>, - sess: &Session, - inline: InlineAttr, + tcx: TyCtxt<'tcx>, + instance: Instance<'tcx>, + codegen_fn_attrs: &CodegenFnAttrs, ) -> Option<&'ll Attribute> { - if !sess.opts.unstable_opts.inline_llvm { + if !tcx.sess.opts.unstable_opts.inline_llvm { // disable LLVM inlining return Some(AttributeKind::NoInline.create_attr(cx.llcx)); } + + // `optnone` requires `noinline` + let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { + (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, + (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, + (inline, _) => inline, + }; + match inline { InlineAttr::Hint => Some(AttributeKind::InlineHint.create_attr(cx.llcx)), InlineAttr::Always | InlineAttr::Force { .. } => { Some(AttributeKind::AlwaysInline.create_attr(cx.llcx)) } InlineAttr::Never => { - if sess.target.arch != Arch::AmdGpu { + if tcx.sess.target.arch != Arch::AmdGpu { Some(AttributeKind::NoInline.create_attr(cx.llcx)) } else { None @@ -412,14 +421,7 @@ pub(crate) fn llfn_attrs_from_instance<'ll, 'tcx>( } if let Some(instance) = instance { - // `optnone` requires `noinline` - let inline = match (codegen_fn_attrs.inline, &codegen_fn_attrs.optimize) { - (_, OptimizeAttr::DoNotOptimize) => InlineAttr::Never, - (InlineAttr::None, _) if instance.def.requires_inline(tcx) => InlineAttr::Hint, - (inline, _) => inline, - }; - - to_add.extend(inline_attr(cx, sess, inline)); + to_add.extend(inline_attr(cx, tcx, instance, codegen_fn_attrs)); } if sess.must_emit_unwind_tables() { diff --git a/compiler/rustc_codegen_llvm/src/builder.rs b/compiler/rustc_codegen_llvm/src/builder.rs index 044011c4ea982..134bc5006dd00 100644 --- a/compiler/rustc_codegen_llvm/src/builder.rs +++ b/compiler/rustc_codegen_llvm/src/builder.rs @@ -1419,6 +1419,21 @@ impl<'a, 'll, 'tcx> BuilderMethods<'a, 'tcx> for Builder<'a, 'll, 'tcx> { ) }; + if let Some(callee_instance) = callee_instance { + // Attributes on the function definition being called + let callee_attrs = self.cx.tcx.codegen_fn_attrs(callee_instance.def_id()); + + if let Some(inlining_rule) = + attributes::inline_attr(&self.cx, self.cx.tcx, callee_instance, callee_attrs) + { + attributes::apply_to_callsite( + call, + llvm::AttributePlace::Function, + &[inlining_rule], + ); + } + } + if let Some(fn_abi) = fn_abi { fn_abi.apply_attrs_callsite(self, call); } diff --git a/tests/codegen-llvm/call-site-inline-attributes.rs b/tests/codegen-llvm/call-site-inline-attributes.rs new file mode 100644 index 0000000000000..01839526c50c1 --- /dev/null +++ b/tests/codegen-llvm/call-site-inline-attributes.rs @@ -0,0 +1,40 @@ +//@ compile-flags: -O -Zinline-mir=no -Cno-prepopulate-passes -Zmerge-functions=disabled + +#![crate_type = "lib"] + +// This test checks that we add inlinehint for #[inline], noinline for #[inline(never)], and +// alwaysinline for #[inline(always)] to call sites. + +#[unsafe(no_mangle)] +fn calls_something_noinline() { + // CHECK-LABEL @calls_something_noinline + // CHECK: call void @{{.*}}noinline_fn() #[[NOINLINE:[0-9]+]] + noinline_fn(); +} + +#[inline(never)] +fn noinline_fn() {} + +#[unsafe(no_mangle)] +fn calls_something_inline() { + // CHECK-LABEL @calls_something_inlinehint + // CHECK: call void @{{.*}}inlinehint_fn() #[[INLINEHINT:[0-9]+]] + inlinehint_fn(); +} + +#[inline] +fn inlinehint_fn() {} + +#[unsafe(no_mangle)] +fn calls_something_alwaysinline() { + // CHECK-LABEL @calls_something_alwaysinline + // CHECK: call void @{{.*}}alwaysinline_fn() #[[ALWAYSINLINE:[0-9]+]] + alwaysinline_fn(); +} + +#[inline(always)] +fn alwaysinline_fn() {} + +//CHECK: attributes #[[NOINLINE]] = {{{.*}} noinline {{.*}}} +//CHECK: attributes #[[INLINEHINT]] = {{{.*}} inlinehint {{.*}}} +//CHECK: attributes #[[ALWAYSINLINE]] = {{{.*}} alwaysinline {{.*}}} From 8edc0696b9357f60518f54fd4de9a737e3640431 Mon Sep 17 00:00:00 2001 From: Josh Triplett Date: Sun, 24 May 2026 07:34:39 +0200 Subject: [PATCH 139/174] Rewrite documentation that said to implement `Into` --- library/core/src/convert/mod.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 85d42b57dc06e..46b0e8c6b67cd 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -5,11 +5,10 @@ //! //! - Implement the [`AsRef`] trait for cheap reference-to-reference conversions //! - Implement the [`AsMut`] trait for cheap mutable-to-mutable conversions -//! - Implement the [`From`] trait for consuming value-to-value conversions -//! - Implement the [`Into`] trait for consuming value-to-value conversions to types -//! outside the current crate -//! - The [`TryFrom`] and [`TryInto`] traits behave like [`From`] and [`Into`], -//! but should be implemented when the conversion can fail. +//! - Implement the [`From`] trait for consuming value-to-value conversions that cannot fail. This +//! automatically provides an implementation of [`Into`] +//! - Implement the [`TryFrom`] trait for consuming value-to-value conversions that can fail. This +//! automatically provides an implementation of [`TryInto`] //! //! The traits in this module are often used as trait bounds for generic functions such that //! arguments of multiple types are supported. See the documentation of each trait for examples. @@ -18,9 +17,9 @@ //! [`TryFrom`][`TryFrom`] rather than [`Into`][`Into`] or [`TryInto`][`TryInto`], //! as [`From`] and [`TryFrom`] provide greater flexibility and offer //! equivalent [`Into`] or [`TryInto`] implementations for free, thanks to a -//! blanket implementation in the standard library. When targeting a version prior to Rust 1.41, it -//! may be necessary to implement [`Into`] or [`TryInto`] directly when converting to a type -//! outside the current crate. +//! blanket implementation in the standard library. In versions of Rust prior to Rust 1.41, +//! it was sometimes necessary to implement [`Into`] or [`TryInto`] directly when converting to a +//! type outside the current crate. //! //! # Generic Implementations //! From ccb9e9e84ade0df4d26f83ce020bca512ec9914f Mon Sep 17 00:00:00 2001 From: Qai Juang Date: Sun, 24 May 2026 03:37:29 -0400 Subject: [PATCH 140/174] account for lifetime bounds in single_use_lifetimes --- .../rustc_resolve/src/late/diagnostics.rs | 3 ++ tests/ui/single-use-lifetime/issue-104440.rs | 6 ++-- .../single-use-lifetime/issue-104440.stderr | 28 --------------- tests/ui/single-use-lifetime/issue-117965.rs | 3 +- .../single-use-lifetime/issue-117965.stderr | 16 --------- .../ui/single-use-lifetime/lifetime-bounds.rs | 34 +++++++++++++++++++ .../lifetime-bounds.stderr | 14 ++++++++ .../two-uses-in-trait-impl.rs | 14 -------- 8 files changed, 57 insertions(+), 61 deletions(-) delete mode 100644 tests/ui/single-use-lifetime/issue-104440.stderr delete mode 100644 tests/ui/single-use-lifetime/issue-117965.stderr create mode 100644 tests/ui/single-use-lifetime/lifetime-bounds.rs create mode 100644 tests/ui/single-use-lifetime/lifetime-bounds.stderr diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index 873aad38a7854..419a47b126980 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -3630,6 +3630,9 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { }; match use_set { Some(LifetimeUseSet::Many) => {} + // A lifetime bound is a real use of that lifetime parameter, even + // though visiting a bound like `'b: 'a` only records a use of `'a`. + Some(LifetimeUseSet::One { .. }) if !param.bounds.is_empty() => {} Some(LifetimeUseSet::One { use_span, use_ctxt }) => { let param_ident = param.ident; let deletion_span = diff --git a/tests/ui/single-use-lifetime/issue-104440.rs b/tests/ui/single-use-lifetime/issue-104440.rs index cecd17fb930bf..6c4398f4372df 100644 --- a/tests/ui/single-use-lifetime/issue-104440.rs +++ b/tests/ui/single-use-lifetime/issue-104440.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![feature(decl_macro, rustc_attrs)] #![deny(single_use_lifetimes)] @@ -35,7 +37,7 @@ mod type_params { mod lifetime_params { macro m($a:lifetime) { - fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { //~ ERROR lifetime parameter `'a` only used once + fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { (t1, t2) } } @@ -60,7 +62,7 @@ mod lifetime_params { } } - m!('a); //~ ERROR lifetime parameter `'a` only used once + m!('a); n!('a); p!('a); } diff --git a/tests/ui/single-use-lifetime/issue-104440.stderr b/tests/ui/single-use-lifetime/issue-104440.stderr deleted file mode 100644 index 54ded31dcbe89..0000000000000 --- a/tests/ui/single-use-lifetime/issue-104440.stderr +++ /dev/null @@ -1,28 +0,0 @@ -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:63:8 - | -LL | m!('a); - | ^^ - | | - | this lifetime... - | ...is used only here - | -note: the lint level is defined here - --> $DIR/issue-104440.rs:2:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: lifetime parameter `'a` only used once - --> $DIR/issue-104440.rs:38:30 - | -LL | fn f<'b, 'c, $a: 'b, 'a: 'c>(t1: &$a(), t2: &'a ()) -> (&'b (), &'c ()) { - | ^^ this lifetime... -- ...is used only here -... -LL | m!('a); - | ------ in this macro invocation - | - = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info) - -error: aborting due to 2 previous errors - diff --git a/tests/ui/single-use-lifetime/issue-117965.rs b/tests/ui/single-use-lifetime/issue-117965.rs index 5eb2a03e13da4..7c3b32651736c 100644 --- a/tests/ui/single-use-lifetime/issue-117965.rs +++ b/tests/ui/single-use-lifetime/issue-117965.rs @@ -1,3 +1,5 @@ +//@ check-pass + #![deny(single_use_lifetimes)] pub enum Data<'a> { @@ -7,7 +9,6 @@ pub enum Data<'a> { impl<'a> Data<'a> { pub fn get<'b: 'a>(&'b self) -> &'a str { - //~^ ERROR lifetime parameter `'b` only used once match &self { Self::Borrowed(val) => val, Self::Owned(val) => &val, diff --git a/tests/ui/single-use-lifetime/issue-117965.stderr b/tests/ui/single-use-lifetime/issue-117965.stderr deleted file mode 100644 index ed14ab92f4d16..0000000000000 --- a/tests/ui/single-use-lifetime/issue-117965.stderr +++ /dev/null @@ -1,16 +0,0 @@ -error: lifetime parameter `'b` only used once - --> $DIR/issue-117965.rs:9:16 - | -LL | pub fn get<'b: 'a>(&'b self) -> &'a str { - | ^^ -- ...is used only here - | | - | this lifetime... - | -note: the lint level is defined here - --> $DIR/issue-117965.rs:1:9 - | -LL | #![deny(single_use_lifetimes)] - | ^^^^^^^^^^^^^^^^^^^^ - -error: aborting due to 1 previous error - diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.rs b/tests/ui/single-use-lifetime/lifetime-bounds.rs new file mode 100644 index 0000000000000..a29d68a0b4a9b --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.rs @@ -0,0 +1,34 @@ +// Regression test for https://github.com/rust-lang/rust/issues/153836. +#![deny(single_use_lifetimes)] +#![allow(dead_code)] +#![allow(unused_variables)] + +struct Foo<'a>(&'a i32); +struct Bar<'b>(&'b i32); + +fn function<'a, 'b: 'a>(_: Foo<'a>, _: Bar<'b>) {} + +type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); +//~^ ERROR bounds cannot be used in this context + +trait WhereBound<'a, 'b> {} + +fn where_bound() +where + T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + //~^ ERROR bounds cannot be used in this context +{ +} + +trait ImplTrait<'a> { + fn foo(self, foo: Foo<'a>); +} + +impl<'a, 'b: 'a> ImplTrait<'a> for Bar<'b> { + fn foo(self, foo: Foo<'a>) { + let _: &'a i32 = self.0; + let _: Foo<'a> = foo; + } +} + +fn main() {} diff --git a/tests/ui/single-use-lifetime/lifetime-bounds.stderr b/tests/ui/single-use-lifetime/lifetime-bounds.stderr new file mode 100644 index 0000000000000..3cc9e8ac90a65 --- /dev/null +++ b/tests/ui/single-use-lifetime/lifetime-bounds.stderr @@ -0,0 +1,14 @@ +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:11:26 + | +LL | type FnPtr = for<'a, 'b: 'a> fn(Foo<'a>, Bar<'b>); + | ^^ + +error: bounds cannot be used in this context + --> $DIR/lifetime-bounds.rs:18:20 + | +LL | T: for<'a, 'b: 'a> WhereBound<'a, 'b>, + | ^^ + +error: aborting due to 2 previous errors + diff --git a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs index 983157614a24d..1f0da77baee8b 100644 --- a/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs +++ b/tests/ui/single-use-lifetime/two-uses-in-trait-impl.rs @@ -19,18 +19,4 @@ impl<'f> Iterator for Foo<'f> { } } -trait BoundTrait<'a> { - fn foo(self, handler: &Handler<'a>); -} - -struct Handler<'a>(fn(&'a u32)); -struct Bar<'b>(&'b u32); - -// https://github.com/rust-lang/rust/issues/153836 -impl<'a, 'b: 'a> BoundTrait<'a> for Bar<'b> { - fn foo(self, handler: &Handler<'a>) { - (handler.0)(self.0); - } -} - fn main() {} From c43fa7a8bb96298f9fe370a6a11303b13a86a576 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 7 May 2026 20:17:43 +0900 Subject: [PATCH 141/174] Parse `mut` restrictions --- compiler/rustc_expand/src/placeholders.rs | 5 + compiler/rustc_parse/src/errors.rs | 20 ++++ compiler/rustc_parse/src/parser/item.rs | 20 +++- compiler/rustc_parse/src/parser/mod.rs | 125 ++++++++++++++-------- compiler/rustc_resolve/src/late.rs | 1 + 5 files changed, 124 insertions(+), 47 deletions(-) diff --git a/compiler/rustc_expand/src/placeholders.rs b/compiler/rustc_expand/src/placeholders.rs index 2db18429a5216..4044a414c5fee 100644 --- a/compiler/rustc_expand/src/placeholders.rs +++ b/compiler/rustc_expand/src/placeholders.rs @@ -186,6 +186,11 @@ pub(crate) fn placeholder( ty: ty(), vis, is_placeholder: true, + mut_restriction: ast::MutRestriction { + kind: ast::RestrictionKind::Unrestricted, + span: DUMMY_SP, + tokens: None, + }, safety: Safety::Default, default: None, }]), diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 12dc35d29ad9f..a0b6321a6c437 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1287,6 +1287,26 @@ pub(crate) struct IncorrectImplRestriction { pub inner_str: String, } +#[derive(Diagnostic)] +#[diag("incorrect `mut` restriction")] +#[help( + "some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path" +)] +pub(crate) struct IncorrectMutRestriction { + #[primary_span] + #[suggestion( + "help: use `in` to restrict mutations to the path `{$inner_str}`", + code = "in {inner_str}", + applicability = "machine-applicable" + )] + pub span: Span, + pub inner_str: String, +} + #[derive(Diagnostic)] #[diag(" ... else {\"{\"} ... {\"}\"} is not allowed")] pub(crate) struct AssignmentElseNotAllowed { diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 68fda3b86ddcb..bed03800d3f4c 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2108,6 +2108,7 @@ impl<'a> Parser<'a> { return Err(err); } }; + let mut_restriction = p.parse_mut_restriction()?; // Unsafe fields are not supported in tuple structs, as doing so would result in a // parsing ambiguity for `struct X(unsafe fn())`. let ty = match p.parse_ty() { @@ -2140,6 +2141,7 @@ impl<'a> Parser<'a> { FieldDef { span: lo.to(ty.span), vis, + mut_restriction, safety: Safety::Default, ident: None, id: DUMMY_NODE_ID, @@ -2164,9 +2166,18 @@ impl<'a> Parser<'a> { self.collect_tokens(None, attrs, ForceCollect::No, |this, attrs| { let lo = this.token.span; let vis = this.parse_visibility(FollowedByType::No)?; + let mut_restriction = this.parse_mut_restriction()?; let safety = this.parse_unsafe_field(); - this.parse_single_struct_field(adt_ty, lo, vis, safety, attrs, ident_span) - .map(|field| (field, Trailing::No, UsePreAttrPos::No)) + this.parse_single_struct_field( + adt_ty, + lo, + vis, + mut_restriction, + safety, + attrs, + ident_span, + ) + .map(|field| (field, Trailing::No, UsePreAttrPos::No)) }) } @@ -2176,11 +2187,12 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, safety: Safety, attrs: AttrVec, ident_span: Span, ) -> PResult<'a, FieldDef> { - let a_var = self.parse_name_and_ty(adt_ty, lo, vis, safety, attrs)?; + let a_var = self.parse_name_and_ty(adt_ty, lo, vis, mut_restriction, safety, attrs)?; match self.token.kind { token::Comma => { self.bump(); @@ -2299,6 +2311,7 @@ impl<'a> Parser<'a> { adt_ty: &str, lo: Span, vis: Visibility, + mut_restriction: MutRestriction, safety: Safety, attrs: AttrVec, ) -> PResult<'a, FieldDef> { @@ -2337,6 +2350,7 @@ impl<'a> Parser<'a> { ident: Some(name), vis, safety, + mut_restriction, id: DUMMY_NODE_ID, ty, default, diff --git a/compiler/rustc_parse/src/parser/mod.rs b/compiler/rustc_parse/src/parser/mod.rs index 850d64c8ecf44..a16ddb34b3c2e 100644 --- a/compiler/rustc_parse/src/parser/mod.rs +++ b/compiler/rustc_parse/src/parser/mod.rs @@ -36,8 +36,8 @@ use rustc_ast::util::classify; use rustc_ast::{ self as ast, AnonConst, AttrArgs, AttrId, BinOpKind, ByRef, Const, CoroutineKind, DUMMY_NODE_ID, DelimArgs, Expr, ExprKind, Extern, HasAttrs, HasTokens, ImplRestriction, - MgcaDisambiguation, Mutability, Recovered, RestrictionKind, Safety, StrLit, Visibility, - VisibilityKind, + MgcaDisambiguation, MutRestriction, Mutability, Recovered, RestrictionKind, Safety, StrLit, + Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_data_structures::fx::FxHashMap; @@ -51,8 +51,8 @@ pub use token_type::{ExpKeywordPair, ExpTokenPair, TokenType}; use tracing::debug; use crate::errors::{ - self, IncorrectImplRestriction, IncorrectVisibilityRestriction, NonStringAbiLiteral, - TokenDescription, + self, IncorrectImplRestriction, IncorrectMutRestriction, IncorrectVisibilityRestriction, + NonStringAbiLiteral, TokenDescription, }; use crate::exp; @@ -297,6 +297,13 @@ impl SeqSep { } } +/// Whether parsing `impl` or `mut` restrictions. +#[derive(Clone, Copy, Debug)] +enum ParsingRestrictionKind { + Impl, + Mut, +} + #[derive(Debug)] pub enum FollowedByType { Yes, @@ -1550,37 +1557,9 @@ impl<'a> Parser<'a> { /// Enforces the `impl_restriction` feature gate whenever an explicit restriction is encountered. fn parse_impl_restriction(&mut self) -> PResult<'a, ImplRestriction> { if self.eat_keyword(exp!(Impl)) { - let lo = self.prev_token.span; - // No units or tuples are allowed to follow `impl` here, so we can safely bump `(`. - self.expect(exp!(OpenParen))?; - if self.eat_keyword(exp!(In)) { - let path = self.parse_path(PathStyle::Mod)?; // `in path` - self.expect(exp!(CloseParen))?; // `)` - let restriction = RestrictionKind::Restricted { - path: Box::new(path), - id: ast::DUMMY_NODE_ID, - shorthand: false, - }; - let span = lo.to(self.prev_token.span); - self.psess.gated_spans.gate(sym::impl_restriction, span); - return Ok(ImplRestriction { kind: restriction, span, tokens: None }); - } else if self.look_ahead(1, |t| t == &token::CloseParen) - && self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower]) - { - let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` - self.expect(exp!(CloseParen))?; // `)` - let restriction = RestrictionKind::Restricted { - path: Box::new(path), - id: ast::DUMMY_NODE_ID, - shorthand: true, - }; - let span = lo.to(self.prev_token.span); - self.psess.gated_spans.gate(sym::impl_restriction, span); - return Ok(ImplRestriction { kind: restriction, span, tokens: None }); - } else { - self.recover_incorrect_impl_restriction(lo)?; - // Emit diagnostic, but continue with no impl restriction. - } + let (kind, span, gated_span) = self.parse_restriction(ParsingRestrictionKind::Impl)?; + self.psess.gated_spans.gate(sym::impl_restriction, gated_span); + return Ok(ImplRestriction { kind, span, tokens: None }); } Ok(ImplRestriction { kind: RestrictionKind::Unrestricted, @@ -1589,15 +1568,73 @@ impl<'a> Parser<'a> { }) } - /// Recovery for e.g. `impl(something) trait` - fn recover_incorrect_impl_restriction(&mut self, lo: Span) -> PResult<'a, ()> { - let path = self.parse_path(PathStyle::Mod)?; - self.expect(exp!(CloseParen))?; // `)` - let path_str = pprust::path_to_string(&path); - self.dcx().emit_err(IncorrectImplRestriction { span: path.span, inner_str: path_str }); - let end = self.prev_token.span; - self.psess.gated_spans.gate(sym::impl_restriction, lo.to(end)); - Ok(()) + /// Parses an optional `mut` restriction. + /// Enforces the `mut_restriction` feature gate whenever an explicit restriction is encountered. + fn parse_mut_restriction(&mut self) -> PResult<'a, MutRestriction> { + if self.eat_keyword(exp!(Mut)) { + let (kind, span, gated_span) = self.parse_restriction(ParsingRestrictionKind::Mut)?; + self.psess.gated_spans.gate(sym::mut_restriction, gated_span); + return Ok(MutRestriction { kind, span, tokens: None }); + } + Ok(MutRestriction { + kind: RestrictionKind::Unrestricted, + span: self.token.span.shrink_to_lo(), + tokens: None, + }) + } + + /// Parses `impl` or `mut` restrictions. + /// Returns the parsed restriction and its span, as well as the gated span. + fn parse_restriction( + &mut self, + restriction_kind: ParsingRestrictionKind, + ) -> PResult<'a, (RestrictionKind, Span, Span)> { + let lo = self.prev_token.span; + // No units or tuples are allowed to follow `impl` or `mut` here, so we can safely bump `(`. + self.expect(exp!(OpenParen))?; + if self.eat_keyword(exp!(In)) { + let path = self.parse_path(PathStyle::Mod)?; // `in path` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: false, + }; + let span = lo.to(self.prev_token.span); + Ok((restriction, span, span)) + } else if self.look_ahead(1, |t| t == &token::CloseParen) + && self.is_keyword_ahead(0, &[kw::Crate, kw::Super, kw::SelfLower]) + { + let path = self.parse_path(PathStyle::Mod)?; // `crate`/`super`/`self` + self.expect(exp!(CloseParen))?; // `)` + let restriction = RestrictionKind::Restricted { + path: Box::new(path), + id: ast::DUMMY_NODE_ID, + shorthand: true, + }; + let span = lo.to(self.prev_token.span); + Ok((restriction, span, span)) + } else { + // Emit diagnostic, but continue with no restrictions. + // Recovery for `impl(something) trait` or `mut (something) field`. + let path = self.parse_path(PathStyle::Mod)?; + self.expect(exp!(CloseParen))?; // `)` + let path_str = pprust::path_to_string(&path); + let end = self.prev_token.span; + match restriction_kind { + ParsingRestrictionKind::Impl => { + self.dcx().emit_err(IncorrectImplRestriction { + span: path.span, + inner_str: path_str, + }); + } + ParsingRestrictionKind::Mut => { + self.dcx() + .emit_err(IncorrectMutRestriction { span: path.span, inner_str: path_str }); + } + } + Ok((RestrictionKind::Unrestricted, self.token.span.shrink_to_lo(), lo.to(end))) + } } /// Parses `extern string_literal?`. diff --git a/compiler/rustc_resolve/src/late.rs b/compiler/rustc_resolve/src/late.rs index 21b36f1934c3a..fc8578e302923 100644 --- a/compiler/rustc_resolve/src/late.rs +++ b/compiler/rustc_resolve/src/late.rs @@ -1479,6 +1479,7 @@ impl<'ast, 'ra, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'ra, 'tc ty, is_placeholder: _, default, + mut_restriction: _, safety: _, } = f; walk_list!(self, visit_attribute, attrs); From a8a6aa8f59bb94c56aff50a3fd5a33f78424acc2 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Wed, 20 May 2026 19:54:56 +0900 Subject: [PATCH 142/174] Pretty print `mut` restrictions --- compiler/rustc_ast_pretty/src/pprust/mod.rs | 4 ++++ compiler/rustc_ast_pretty/src/pprust/state.rs | 4 ++++ .../rustc_ast_pretty/src/pprust/state/item.rs | 16 ++++++++++++++++ 3 files changed, 24 insertions(+) diff --git a/compiler/rustc_ast_pretty/src/pprust/mod.rs b/compiler/rustc_ast_pretty/src/pprust/mod.rs index 25b398b849246..19bd8fd11bf2e 100644 --- a/compiler/rustc_ast_pretty/src/pprust/mod.rs +++ b/compiler/rustc_ast_pretty/src/pprust/mod.rs @@ -84,6 +84,10 @@ pub fn impl_restriction_to_string(r: &ast::ImplRestriction) -> String { State::new().impl_restriction_to_string(r) } +pub fn mut_restriction_to_string(r: &ast::MutRestriction) -> String { + State::new().mut_restriction_to_string(r) +} + pub fn meta_list_item_to_string(li: &ast::MetaItemInner) -> String { State::new().meta_list_item_to_string(li) } diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index f46ce8fd76865..8b2da2acaa520 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1153,6 +1153,10 @@ pub trait PrintState<'a>: std::ops::Deref + std::ops::Dere Self::to_string(|s| s.print_impl_restriction(r)) } + fn mut_restriction_to_string(&self, r: &ast::MutRestriction) -> String { + Self::to_string(|s| s.print_mut_restriction(r)) + } + fn block_to_string(&self, blk: &ast::Block) -> String { Self::to_string(|s| { let (cb, ib) = s.head(""); diff --git a/compiler/rustc_ast_pretty/src/pprust/state/item.rs b/compiler/rustc_ast_pretty/src/pprust/state/item.rs index b3a8f5d8cac30..820c49cd0612c 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state/item.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state/item.rs @@ -511,6 +511,20 @@ impl<'a> State<'a> { } } + pub(crate) fn print_mut_restriction(&mut self, mut_restriction: &ast::MutRestriction) { + match &mut_restriction.kind { + ast::RestrictionKind::Restricted { path, shorthand, .. } => { + let path = Self::to_string(|s| s.print_path(path, false, 0)); + if *shorthand { + self.word_nbsp(format!("mut({path})")) + } else { + self.word_nbsp(format!("mut(in {path})")) + } + } + ast::RestrictionKind::Unrestricted => {} + } + } + fn print_defaultness(&mut self, defaultness: ast::Defaultness) { if let ast::Defaultness::Default(_) = defaultness { self.word_nbsp("default"); @@ -537,6 +551,7 @@ impl<'a> State<'a> { s.maybe_print_comment(field.span.lo()); s.print_outer_attributes(&field.attrs); s.print_visibility(&field.vis); + s.print_mut_restriction(&field.mut_restriction); s.print_type(&field.ty) }); self.pclose(); @@ -562,6 +577,7 @@ impl<'a> State<'a> { self.maybe_print_comment(field.span.lo()); self.print_outer_attributes(&field.attrs); self.print_visibility(&field.vis); + self.print_mut_restriction(&field.mut_restriction); self.print_ident(field.ident.unwrap()); self.word_nbsp(":"); self.print_type(&field.ty); From 9aa5a17cee9384083b02db8b322848958fed93ae Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Wed, 20 May 2026 20:02:16 +0900 Subject: [PATCH 143/174] Add `eq_mut_restriction` to clippy_utils --- src/tools/clippy/clippy_utils/src/ast_utils/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs index 7b408471574f9..c41ffad8431ba 100644 --- a/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs +++ b/src/tools/clippy/clippy_utils/src/ast_utils/mod.rs @@ -726,6 +726,7 @@ pub fn eq_struct_field(l: &FieldDef, r: &FieldDef) -> bool { l.is_placeholder == r.is_placeholder && over(&l.attrs, &r.attrs, eq_attr) && eq_vis(&l.vis, &r.vis) + && eq_mut_restriction(&l.mut_restriction, &r.mut_restriction) && both(l.ident.as_ref(), r.ident.as_ref(), |l, r| eq_id(*l, *r)) && eq_ty(&l.ty, &r.ty) } @@ -846,6 +847,10 @@ pub fn eq_impl_restriction(l: &ImplRestriction, r: &ImplRestriction) -> bool { eq_restriction_kind(&l.kind, &r.kind) } +pub fn eq_mut_restriction(l: &MutRestriction, r: &MutRestriction) -> bool { + eq_restriction_kind(&l.kind, &r.kind) +} + fn eq_restriction_kind(l: &RestrictionKind, r: &RestrictionKind) -> bool { match (l, r) { (RestrictionKind::Unrestricted, RestrictionKind::Unrestricted) => true, From dbb7b811ee1d2e89f815869e1f8189eb9874832f Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Wed, 20 May 2026 22:38:39 +0900 Subject: [PATCH 144/174] rustfmt `mut` restriction --- src/tools/rustfmt/src/items.rs | 5 +- src/tools/rustfmt/src/utils.rs | 11 +- .../rustfmt/tests/source/mut-restriction.rs | 108 ++++++++++++++++++ .../rustfmt/tests/target/mut-restriction.rs | 35 ++++++ 4 files changed, 155 insertions(+), 4 deletions(-) create mode 100644 src/tools/rustfmt/tests/source/mut-restriction.rs create mode 100644 src/tools/rustfmt/tests/target/mut-restriction.rs diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs index 32f71703e019f..6757381f8356e 100644 --- a/src/tools/rustfmt/src/items.rs +++ b/src/tools/rustfmt/src/items.rs @@ -1889,15 +1889,16 @@ pub(crate) fn rewrite_struct_field_prefix( field: &ast::FieldDef, ) -> RewriteResult { let vis = format_visibility(context, &field.vis); + let mut_restriction = format_mut_restriction(context, &field.mut_restriction); let safety = format_safety(field.safety); let type_annotation_spacing = type_annotation_spacing(context.config); Ok(match field.ident { Some(name) => format!( - "{vis}{safety}{}{}:", + "{vis}{mut_restriction}{safety}{}{}:", rewrite_ident(context, name), type_annotation_spacing.0 ), - None => format!("{vis}{safety}"), + None => format!("{vis}{mut_restriction}{safety}"), }) } diff --git a/src/tools/rustfmt/src/utils.rs b/src/tools/rustfmt/src/utils.rs index de72c9ce14bc3..3ec8a36eb5a8d 100644 --- a/src/tools/rustfmt/src/utils.rs +++ b/src/tools/rustfmt/src/utils.rs @@ -2,8 +2,8 @@ use std::borrow::Cow; use rustc_ast::YieldKind; use rustc_ast::ast::{ - self, Attribute, ImplRestriction, MetaItem, MetaItemInner, MetaItemKind, NodeId, Path, - RestrictionKind, Visibility, VisibilityKind, + self, Attribute, ImplRestriction, MetaItem, MetaItemInner, MetaItemKind, MutRestriction, + NodeId, Path, RestrictionKind, Visibility, VisibilityKind, }; use rustc_ast_pretty::pprust; use rustc_span::{BytePos, LocalExpnId, Span, Symbol, SyntaxContext, sym, symbol}; @@ -81,6 +81,13 @@ pub(crate) fn format_impl_restriction( format_restriction("impl", context, &impl_restriction.kind) } +pub(crate) fn format_mut_restriction( + context: &RewriteContext<'_>, + mut_restriction: &MutRestriction, +) -> String { + format_restriction("mut", context, &mut_restriction.kind) +} + fn format_restriction( kw: &'static str, context: &RewriteContext<'_>, diff --git a/src/tools/rustfmt/tests/source/mut-restriction.rs b/src/tools/rustfmt/tests/source/mut-restriction.rs new file mode 100644 index 0000000000000..990cbd476bfac --- /dev/null +++ b/src/tools/rustfmt/tests/source/mut-restriction.rs @@ -0,0 +1,108 @@ +#![feature(mut_restrictions, unsafe_fields)] + +struct FooS { + pub + mut(crate) + field1: (), + pub + mut(crate + ) + unsafe + field2: (), +} + +struct BazS { + pub + mut ( in foo + :: + bar ) + field1: (), + pub + mut( + in foo + :: + bar + ) unsafe + field2: (), +} + +struct FooS2( + pub + mut(crate) + (), +); + +struct BazS2( + pub + mut ( in foo + :: + bar ) + (), +); + +enum Enum { + Foo { + pub(crate) mut(self) + field1: (), + pub + mut(self + ) + unsafe + field2: (), + }, + Baz { + pub + mut ( in foo + :: + bar ) + field1: (), + pub( + crate + ) + mut( + in foo + :: + bar + ) unsafe field2: (), + }, + FooT( + pub(crate) + mut(self) + (), + ), + BazT( + pub(crate + ) + mut ( in foo + :: + bar ) + (), + ), + +} + +union Union { + pub + mut(crate) + field1: (), + pub(crate + ) + mut ( in foo + :: + bar ) + field2: (), + pub + mut(crate + ) + unsafe + field3: (), + pub( + crate + ) + mut( + in foo + :: + bar + ) unsafe + field4: (), +} diff --git a/src/tools/rustfmt/tests/target/mut-restriction.rs b/src/tools/rustfmt/tests/target/mut-restriction.rs new file mode 100644 index 0000000000000..3ac5d6ae912d1 --- /dev/null +++ b/src/tools/rustfmt/tests/target/mut-restriction.rs @@ -0,0 +1,35 @@ +#![feature(mut_restrictions, unsafe_fields)] + +struct FooS { + pub mut(crate) field1: (), + pub mut(crate) unsafe field2: (), +} + +struct BazS { + pub mut(in foo::bar) field1: (), + pub mut(in foo::bar) unsafe field2: (), +} + +struct FooS2(pub mut(crate) ()); + +struct BazS2(pub mut(in foo::bar) ()); + +enum Enum { + Foo { + pub(crate) mut(self) field1: (), + pub mut(self) unsafe field2: (), + }, + Baz { + pub mut(in foo::bar) field1: (), + pub(crate) mut(in foo::bar) unsafe field2: (), + }, + FooT(pub(crate) mut(self) ()), + BazT(pub(crate) mut(in foo::bar) ()), +} + +union Union { + pub mut(crate) field1: (), + pub(crate) mut(in foo::bar) field2: (), + pub mut(crate) unsafe field3: (), + pub(crate) mut(in foo::bar) unsafe field4: (), +} From cb3173473c0a92c09498b94f6067fb653e11bf29 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Thu, 7 May 2026 21:22:54 +0900 Subject: [PATCH 145/174] Add UI tests --- tests/ui/README.md | 3 + .../feature-gate-mut-restriction.rs | 104 +++++ ...e-gate-mut-restriction.without_gate.stderr | 403 ++++++++++++++++++ .../recover-incorrect-mut-restriction.rs | 25 ++ .../recover-incorrect-mut-restriction.stderr | 130 ++++++ 5 files changed, 665 insertions(+) create mode 100644 tests/ui/mut-restriction/feature-gate-mut-restriction.rs create mode 100644 tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr create mode 100644 tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs create mode 100644 tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr diff --git a/tests/ui/README.md b/tests/ui/README.md index 39402f78bb5ec..dbffa54468d52 100644 --- a/tests/ui/README.md +++ b/tests/ui/README.md @@ -943,6 +943,9 @@ Tests on moves (destructive moves). Broad category of tests on mutability, such as the `mut` keyword, borrowing a value as both immutable and mutable (and the associated error), or adding mutable references to `const` declarations. +## `tests/ui/mut-restriction/` +Tests for `#![feature(mut_restriction)]`. See [Tracking issue for restrictions #105077](https://github.com/rust-lang/rust/issues/105077). + ## `tests/ui/namespace/` Contains a single test. It imports a massive amount of very similar types from a crate, then attempts various permutations of their namespace paths, checking for errors or the lackthereof. diff --git a/tests/ui/mut-restriction/feature-gate-mut-restriction.rs b/tests/ui/mut-restriction/feature-gate-mut-restriction.rs new file mode 100644 index 0000000000000..3ec3345cf72a9 --- /dev/null +++ b/tests/ui/mut-restriction/feature-gate-mut-restriction.rs @@ -0,0 +1,104 @@ +//@ revisions: with_gate without_gate +//@[with_gate] check-pass + +#![cfg_attr(with_gate, feature(mut_restriction))] +#![feature(unsafe_fields)] + +pub struct Foo { + pub mut(crate) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(self) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); //[without_gate]~ ERROR `mut` restrictions are experimental +//[without_gate]~^ ERROR `mut` restrictions are experimental + +pub enum EnumFoo { + Var { + mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(self) i32, mut(crate) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental +} + +pub union UnionFoo { + pub mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +pub mod foo { + pub struct Bar { + pub mut(super) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(in crate::foo) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } + + pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + + pub enum EnumBar { + Var { + mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(in crate::foo) i32, mut(super) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + } + + pub union UnionBar { + pub mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } +} + +#[cfg(false)] +pub struct Baz { + pub mut(crate) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(self) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); //[without_gate]~ ERROR `mut` restrictions are experimental +//[without_gate]~^ ERROR `mut` restrictions are experimental + +#[cfg(false)] +pub enum EnumBaz { + Var { + mut(self) x: i32, //[without_gate]~ ERROR `mut` restrictions + mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(self) i32, mut(crate) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub union UnionBaz { + pub mut(self) x: i32, //[without_gate]~ ERROR `mut` + pub mut(crate) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental +} + +#[cfg(false)] +pub mod bar { + pub struct Bar { + pub mut(super) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(in crate::foo) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } + + pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + + pub enum EnumBar { + Var { + mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + }, + Tup(mut(in crate::foo) i32, mut(super) i32), //[without_gate]~ ERROR `mut` restrictions are experimental + //[without_gate]~^ ERROR `mut` restrictions are experimental + } + + pub union UnionBar { + pub mut(in crate::foo) x: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + pub mut(super) unsafe y: i32, //[without_gate]~ ERROR `mut` restrictions are experimental + } +} + +fn main() {} diff --git a/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr b/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr new file mode 100644 index 0000000000000..8f6193c030557 --- /dev/null +++ b/tests/ui/mut-restriction/feature-gate-mut-restriction.without_gate.stderr @@ -0,0 +1,403 @@ +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:8:9 + | +LL | pub mut(crate) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:9:9 + | +LL | pub mut(self) unsafe y: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:12:23 + | +LL | pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:12:43 + | +LL | pub struct TupFoo(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:17:9 + | +LL | mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:18:9 + | +LL | mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:20:9 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:20:24 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:25:9 + | +LL | pub mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:26:9 + | +LL | pub mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:31:13 + | +LL | pub mut(super) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:32:13 + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:35:27 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:35:47 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:40:13 + | +LL | mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:41:13 + | +LL | mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:43:13 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:43:37 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:48:13 + | +LL | pub mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:49:13 + | +LL | pub mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:55:9 + | +LL | pub mut(crate) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:56:9 + | +LL | pub mut(self) unsafe y: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:60:23 + | +LL | pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:60:43 + | +LL | pub struct TupBaz(pub mut(crate) i32, pub mut(self) i32); + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:66:9 + | +LL | mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:67:9 + | +LL | mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:69:9 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:69:24 + | +LL | Tup(mut(self) i32, mut(crate) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:75:9 + | +LL | pub mut(self) x: i32, + | ^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:76:9 + | +LL | pub mut(crate) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:82:13 + | +LL | pub mut(super) x: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:83:13 + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:86:27 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:86:47 + | +LL | pub struct TupBar(pub mut(super) i32, pub mut(in crate::foo) i32); + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:91:13 + | +LL | mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:92:13 + | +LL | mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:94:13 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:94:37 + | +LL | Tup(mut(in crate::foo) i32, mut(super) i32), + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:99:13 + | +LL | pub mut(in crate::foo) x: i32, + | ^^^^^^^^^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error[E0658]: `mut` restrictions are experimental + --> $DIR/feature-gate-mut-restriction.rs:100:13 + | +LL | pub mut(super) unsafe y: i32, + | ^^^^^^^^^^ + | + = note: see issue #105077 for more information + = help: add `#![feature(mut_restriction)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 40 previous errors + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs new file mode 100644 index 0000000000000..5a996b0c1d469 --- /dev/null +++ b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.rs @@ -0,0 +1,25 @@ +#![feature(mut_restriction, unsafe_fields)] + +pub mod foo { + pub struct Foo { + pub mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + pub mut(crate::foo) unsafe y: i32, //~ ERROR incorrect `mut` restriction + } + + pub struct TupFoo(pub mut(crate::foo) i32); //~ ERROR incorrect `mut` restriction + + pub enum EnumFoo { + Var { + mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + mut(crate::foo) unsafe y: i32 //~ ERROR incorrect `mut` restriction + }, + Tup(mut(crate::foo) i32), //~ ERROR incorrect `mut` restriction + } + + pub union UnionFoo { + pub mut(crate::foo) x: i32, //~ ERROR incorrect `mut` restriction + pub mut(crate::foo) unsafe y: i32, //~ ERROR incorrect `mut` restriction + } +} + +fn main() {} diff --git a/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr new file mode 100644 index 0000000000000..50e5216a16283 --- /dev/null +++ b/tests/ui/mut-restriction/recover-incorrect-mut-restriction.stderr @@ -0,0 +1,130 @@ +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:5:17 + | +LL | pub mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:6:17 + | +LL | pub mut(crate::foo) unsafe y: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:9:31 + | +LL | pub struct TupFoo(pub mut(crate::foo) i32); + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub struct TupFoo(pub mut(in crate::foo) i32); + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:13:17 + | +LL | mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:14:17 + | +LL | mut(crate::foo) unsafe y: i32 + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | mut(in crate::foo) unsafe y: i32 + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:16:17 + | +LL | Tup(mut(crate::foo) i32), + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | Tup(mut(in crate::foo) i32), + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:20:17 + | +LL | pub mut(crate::foo) x: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) x: i32, + | ++ + +error: incorrect `mut` restriction + --> $DIR/recover-incorrect-mut-restriction.rs:21:17 + | +LL | pub mut(crate::foo) unsafe y: i32, + | ^^^^^^^^^^ + | + = help: some possible `mut` restrictions are: + `mut(crate)`: can only be mutated in the current crate + `mut(super)`: can only be mutated in the parent module + `mut(self)`: can only be mutated in current module + `mut(in path::to::module)`: can only be mutated in the specified path +help: help: use `in` to restrict mutations to the path `crate::foo` + | +LL | pub mut(in crate::foo) unsafe y: i32, + | ++ + +error: aborting due to 8 previous errors + From d3bb7198e7d207f6adf6f57c9d13e1f20d4b5d1e Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Wed, 20 May 2026 19:46:08 +0900 Subject: [PATCH 146/174] Add `stringify!` tests --- tests/ui/macros/stringify.rs | 62 ++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/tests/ui/macros/stringify.rs b/tests/ui/macros/stringify.rs index fec991ec95b7b..fde68f8f760dd 100644 --- a/tests/ui/macros/stringify.rs +++ b/tests/ui/macros/stringify.rs @@ -10,14 +10,17 @@ #![feature(const_trait_impl)] #![feature(coroutines)] #![feature(decl_macro)] +#![feature(impl_restriction)] #![feature(macro_guard_matcher)] #![feature(more_qualified_paths)] #![feature(move_expr)] +#![feature(mut_restriction)] #![feature(never_patterns)] #![feature(specialization)] #![feature(trait_alias)] #![feature(try_blocks)] #![feature(yeet_expr)] +#![feature(unsafe_fields)] #![deny(unused_macros)] // These macros force the use of AST pretty-printing by converting the input to @@ -924,6 +927,65 @@ fn test_impl_restriction() { ); } +#[test] +fn test_mut_restriction() { + assert_eq!( + stringify!(pub struct Foo { pub mut(crate) x: u8, pub mut(self) unsafe y: u8 }), + "pub struct Foo { pub mut(crate) x: u8, pub mut(self) unsafe y: u8 }" + ); + assert_eq!( + stringify!(pub struct Foo { pub mut(super) x: u8, pub mut(in path::to) unsafe y: u8 }), + "pub struct Foo { pub mut(super) x: u8, pub mut(in path::to) unsafe y: u8 }" + ); + assert_eq!( + stringify!(pub struct Foo { pub mut(in crate::path::to) x: u8 }), + "pub struct Foo { pub mut(in crate::path::to) x: u8 }" + ); + + assert_eq!( + stringify!(pub struct Foo(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8)), + "pub struct Foo(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8)" + ); + assert_eq!( + stringify!(pub struct Foo(pub mut(in path::to) u8, pub mut(in crate::path::to) u8)), + "pub struct Foo(pub mut(in path::to) u8, pub mut(in crate::path::to) u8)" + ); + + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(crate) x: u8, pub mut(self) unsafe y: u8 } }), + "pub enum Foo { Var{ pub mut(crate) x: u8, pub mut(self) unsafe y: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(super) x: u8, pub mut(in path::to) y: u8 } }), + "pub enum Foo { Var{ pub mut(super) x: u8, pub mut(in path::to) y: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Var{ pub mut(in crate::path::to) x: u8 } }), + "pub enum Foo { Var{ pub mut(in crate::path::to) x: u8 } }" + ); + assert_eq!( + stringify!(pub enum Foo { Tup(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8) }), + "pub enum Foo { Tup(pub mut(crate) u8, pub mut(self) u8, pub mut(super) u8) }" + ); + assert_eq!( + stringify!(pub enum Foo { Tup(pub mut(in path::to) u8, pub mut(in crate::path::to) u8) }), + "pub enum Foo { Tup(pub mut(in path::to) u8, pub mut(in crate::path::to) u8) }" + ); + + assert_eq!( + stringify!(pub union Foo { x: pub mut(crate) u8, y: pub mut(self) unsafe u8 }), + "pub union Foo { x: pub mut(crate) u8, y: pub mut(self) unsafe u8 }" + ); + assert_eq!( + stringify!(pub union Foo { x: pub mut(super) u8, y: pub mut(in path::to) unsafe u8 }), + "pub union Foo { x: pub mut(super) u8, y: pub mut(in path::to) unsafe u8 }" + ); + assert_eq!( + stringify!(pub union Foo { x: pub mut(in crate::path::to) u8 }), + "pub union Foo { x: pub mut(in crate::path::to) u8 }" + ); +} + #[test] fn test_punct() { // For all these cases, we should preserve spaces between the tokens. From 572a2e4177edf3e83d725f15887835da39d43fd2 Mon Sep 17 00:00:00 2001 From: CoCo-Japan-pan <115922543+CoCo-Japan-pan@users.noreply.github.com> Date: Wed, 20 May 2026 19:48:07 +0900 Subject: [PATCH 147/174] Bless UI test for input-stats.rs --- tests/ui/stats/input-stats.stderr | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/ui/stats/input-stats.stderr b/tests/ui/stats/input-stats.stderr index a91a15bc63dff..b4d8277a46c00 100644 --- a/tests/ui/stats/input-stats.stderr +++ b/tests/ui/stats/input-stats.stderr @@ -33,8 +33,8 @@ ast-stats - Trait 352 (NN.N%) 4 ast-stats AssocItem 320 (NN.N%) 4 80 ast-stats - Fn 160 (NN.N%) 2 ast-stats - Type 160 (NN.N%) 2 +ast-stats FieldDef 272 (NN.N%) 2 136 ast-stats Variant 208 (NN.N%) 2 104 -ast-stats FieldDef 208 (NN.N%) 2 104 ast-stats Block 192 (NN.N%) 6 32 ast-stats Stmt 160 (NN.N%) 5 32 ast-stats - Let 32 (NN.N%) 1 @@ -57,7 +57,7 @@ ast-stats GenericArgs 40 (NN.N%) 1 40 ast-stats - AngleBracketed 40 (NN.N%) 1 ast-stats Crate 40 (NN.N%) 1 40 ast-stats ---------------------------------------------------------------- -ast-stats Total 7_560 127 +ast-stats Total 7_624 127 ast-stats ================================================================ hir-stats ================================================================ hir-stats HIR STATS: input_stats From 3e247a8ff4b13da1c17527fce3c94b13c560d441 Mon Sep 17 00:00:00 2001 From: Haoran Wang Date: Fri, 22 May 2026 21:22:26 +0800 Subject: [PATCH 148/174] Add regression test for const parameter default ICE --- .../lifetime_dependent_const_param_default.rs | 18 +++++++++++ ...etime_dependent_const_param_default.stderr | 30 +++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs create mode 100644 tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr diff --git a/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs new file mode 100644 index 0000000000000..b31603b21b5fb --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.rs @@ -0,0 +1,18 @@ +// Regression test for https://github.com/rust-lang/rust/issues/142913. + +// A const parameter introduced by `generic_const_parameter_types`, +// whose type mentions an earlier lifetime +// parameter and which carries a default value, used to ICE in `rustc_type_ir` +// with "region parameter out of range when instantiating args". It is now +// rejected with ordinary errors instead of crashing. + +#![feature(generic_const_parameter_types)] + +struct Variant; + +fn foo<'a, const N: &'a Variant = {}>() {} +//~^ ERROR: defaults for generic parameters are not allowed here +//~| ERROR: anonymous constants with lifetimes in their type are not yet supported +//~| ERROR: `&'a Variant` is forbidden as the type of a const generic parameter + +fn main() {} diff --git a/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr new file mode 100644 index 0000000000000..bdcf172cf81f3 --- /dev/null +++ b/tests/ui/const-generics/generic_const_parameter_types/lifetime_dependent_const_param_default.stderr @@ -0,0 +1,30 @@ +error: defaults for generic parameters are not allowed here + --> $DIR/lifetime_dependent_const_param_default.rs:13:12 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: anonymous constants with lifetimes in their type are not yet supported + --> $DIR/lifetime_dependent_const_param_default.rs:13:35 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^ + +error: `&'a Variant` is forbidden as the type of a const generic parameter + --> $DIR/lifetime_dependent_const_param_default.rs:13:21 + | +LL | fn foo<'a, const N: &'a Variant = {}>() {} + | ^^^^^^^^^^^ + | + = note: the only supported types are integers, `bool`, and `char` +help: add `#![feature(min_adt_const_params)]` to the crate attributes to enable more complex and user defined types + | +LL + #![feature(min_adt_const_params)] + | +help: add `#![feature(unsized_const_params)]` to the crate attributes to enable references to implement the `ConstParamTy` trait + | +LL + #![feature(unsized_const_params)] + | + +error: aborting due to 3 previous errors + From 8b382d62df971fc3ef5dfe1ebdd1efe5d770410c Mon Sep 17 00:00:00 2001 From: Bennet Blischke Date: Thu, 21 May 2026 18:21:37 +0200 Subject: [PATCH 149/174] Return result instead of option by Arc/Rc/Box::into_array --- library/alloc/src/boxed.rs | 10 ++++++---- library/alloc/src/rc.rs | 10 ++++++---- library/alloc/src/sync.rs | 10 ++++++---- library/alloc/src/vec/mod.rs | 6 +++++- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/library/alloc/src/boxed.rs b/library/alloc/src/boxed.rs index 80ce7a699efd2..bd3f10a16dd9b 100644 --- a/library/alloc/src/boxed.rs +++ b/library/alloc/src/boxed.rs @@ -1142,7 +1142,9 @@ impl Box<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Box<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1155,16 +1157,16 @@ impl Box<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *mut [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Box::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } diff --git a/library/alloc/src/rc.rs b/library/alloc/src/rc.rs index 2905170d22a76..3f223ad1c9d2c 100644 --- a/library/alloc/src/rc.rs +++ b/library/alloc/src/rc.rs @@ -1245,7 +1245,9 @@ impl Rc<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Rc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1260,16 +1262,16 @@ impl Rc<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *const [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Rc::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } diff --git a/library/alloc/src/sync.rs b/library/alloc/src/sync.rs index 229fcd2b429cf..c0e93bd61c21a 100644 --- a/library/alloc/src/sync.rs +++ b/library/alloc/src/sync.rs @@ -1392,7 +1392,9 @@ impl Arc<[T], A> { /// /// This operation does not reallocate; the underlying array of the slice is simply reinterpreted as an array type. /// - /// If `N` is not exactly equal to the length of `self`, then this method returns `None`. + /// # Errors + /// + /// Returns the original `Arc<[T]>` in the `Err` variant if `self.len()` does not equal `N`. /// /// # Examples /// @@ -1407,16 +1409,16 @@ impl Arc<[T], A> { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[inline] #[must_use] - pub fn into_array(self) -> Option> { + pub fn into_array(self) -> Result, Self> { if self.len() == N { let (ptr, alloc) = Self::into_raw_with_allocator(self); let ptr = ptr as *const [T; N]; // SAFETY: The underlying array of a slice has the exact same layout as an actual array `[T; N]` if `N` is equal to the slice's length. let me = unsafe { Arc::from_raw_in(ptr, alloc) }; - Some(me) + Ok(me) } else { - None + Err(self) } } } diff --git a/library/alloc/src/vec/mod.rs b/library/alloc/src/vec/mod.rs index de04eaf97226a..d2b7837d98fa6 100644 --- a/library/alloc/src/vec/mod.rs +++ b/library/alloc/src/vec/mod.rs @@ -1761,7 +1761,11 @@ impl Vec { #[unstable(feature = "alloc_slice_into_array", issue = "148082")] #[must_use] pub fn into_array(self) -> Result, Self> { - if self.len() == N { Ok(self.into_boxed_slice().into_array().unwrap()) } else { Err(self) } + if self.len() == N { + Ok(self.into_boxed_slice().into_array().ok().unwrap()) + } else { + Err(self) + } } /// Shortens the vector, keeping the first `len` elements and dropping From 5a93cdbc09084fbe0b9f2f2f59301faf079eb6ad Mon Sep 17 00:00:00 2001 From: fallofpheonix Date: Sun, 24 May 2026 16:26:23 +0530 Subject: [PATCH 150/174] Remove useless `-Zunpretty=identified` option Fixes #10671 The `-Zunpretty=identified` option (without `expanded`) is effectively useless because `NodeId`s are unassigned prior to macro expansion, leading them to just be `NodeId::MAX_AS_U32`. This commit removes the `Identified` variant from `PpSourceMode` and removes `-Zunpretty=identified` from the command-line options. Users should use `-Zunpretty=expanded,identified` instead (which is retained) if they want to inspect `NodeId`s. --- compiler/rustc_driver_impl/src/pretty.rs | 1 - compiler/rustc_session/src/config.rs | 7 ++----- compiler/rustc_session/src/options.rs | 2 +- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/compiler/rustc_driver_impl/src/pretty.rs b/compiler/rustc_driver_impl/src/pretty.rs index 426ead704ab83..8138fb6a2ff40 100644 --- a/compiler/rustc_driver_impl/src/pretty.rs +++ b/compiler/rustc_driver_impl/src/pretty.rs @@ -239,7 +239,6 @@ pub fn print<'tcx>(sess: &Session, ppm: PpMode, ex: PrintExtra<'tcx>) { let annotation: Box = match s { Normal => Box::new(AstNoAnn), Expanded => Box::new(AstNoAnn), - Identified => Box::new(AstIdentifiedAnn), ExpandedIdentified => Box::new(AstIdentifiedAnn), ExpandedHygiene => Box::new(AstHygieneAnn { sess }), }; diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 2040cd48ec44f..e82f67eac5e9f 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -2787,7 +2787,6 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O let first = match unstable_opts.unpretty.as_deref()? { "normal" => Source(PpSourceMode::Normal), - "identified" => Source(PpSourceMode::Identified), "expanded" => Source(PpSourceMode::Expanded), "expanded,identified" => Source(PpSourceMode::ExpandedIdentified), "expanded,hygiene" => Source(PpSourceMode::ExpandedHygiene), @@ -2803,7 +2802,7 @@ fn parse_pretty(early_dcx: &EarlyDiagCtxt, unstable_opts: &UnstableOptions) -> O "stable-mir" => StableMir, "mir-cfg" => MirCFG, name => early_dcx.early_fatal(format!( - "argument to `unpretty` must be one of `normal`, `identified`, \ + "argument to `unpretty` must be one of `normal`, \ `expanded`, `expanded,identified`, `expanded,hygiene`, \ `ast-tree`, `ast-tree,expanded`, `hir`, `hir,identified`, \ `hir,typed`, `hir-tree`, `thir-tree`, `thir-flat`, `mir`, `stable-mir`, or \ @@ -2933,8 +2932,6 @@ pub enum PpSourceMode { Normal, /// `-Zunpretty=expanded` Expanded, - /// `-Zunpretty=identified` - Identified, /// `-Zunpretty=expanded,identified` ExpandedIdentified, /// `-Zunpretty=expanded,hygiene` @@ -2982,7 +2979,7 @@ impl PpMode { use PpMode::*; use PpSourceMode::*; match *self { - Source(Normal | Identified) | AstTree => false, + Source(Normal) | AstTree => false, Source(Expanded | ExpandedIdentified | ExpandedHygiene) | AstTreeExpanded diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs index aa9331ee8f659..1b2e24a684bc9 100644 --- a/compiler/rustc_session/src/options.rs +++ b/compiler/rustc_session/src/options.rs @@ -2723,7 +2723,7 @@ written to standard error output)"), "take the brakes off const evaluation. NOTE: this is unsound (default: no)"), unpretty: Option = (None, parse_unpretty, [UNTRACKED], "present the input source, unstable (and less-pretty) variants; - `normal`, `identified`, + `normal`, `expanded`, `expanded,identified`, `expanded,hygiene` (with internal representations), `ast-tree` (raw AST before expansion), From 20d113eff15641d47a0e1c79e76d67f2e85dfb9d Mon Sep 17 00:00:00 2001 From: Camille Gillot Date: Sun, 24 May 2026 09:20:52 +0000 Subject: [PATCH 151/174] Drop skip_move_check_fns query. --- compiler/rustc_middle/src/queries.rs | 6 --- .../rustc_monomorphize/src/mono_checks/mod.rs | 6 +-- .../src/mono_checks/move_check.rs | 45 +++++++------------ 3 files changed, 18 insertions(+), 39 deletions(-) diff --git a/compiler/rustc_middle/src/queries.rs b/compiler/rustc_middle/src/queries.rs index d62e4ec941d21..f9b6bf02ad7c4 100644 --- a/compiler/rustc_middle/src/queries.rs +++ b/compiler/rustc_middle/src/queries.rs @@ -2710,12 +2710,6 @@ rustc_queries! { desc { "monomorphization-time checking" } } - /// Builds the set of functions that should be skipped for the move-size check. - query skip_move_check_fns(_: ()) -> &'tcx FxIndexSet { - arena_cache - desc { "functions to skip for move-size check" } - } - query items_of_instance(key: (ty::Instance<'tcx>, CollectionMode)) -> Result<(&'tcx [Spanned>], &'tcx [Spanned>]), NormalizationErrorInMono> { desc { "collecting items used by `{}`", key.0 } cache_on_disk diff --git a/compiler/rustc_monomorphize/src/mono_checks/mod.rs b/compiler/rustc_monomorphize/src/mono_checks/mod.rs index 1ecda824fb8c2..6569eeafec17d 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/mod.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/mod.rs @@ -15,9 +15,5 @@ fn check_mono_item<'tcx>(tcx: TyCtxt<'tcx>, instance: Instance<'tcx>) { } pub(super) fn provide(providers: &mut Providers) { - *providers = Providers { - check_mono_item, - skip_move_check_fns: move_check::skip_move_check_fns, - ..*providers - } + *providers = Providers { check_mono_item, ..*providers } } diff --git a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs index a24b0443d39c9..af03e2a0d2d66 100644 --- a/compiler/rustc_monomorphize/src/mono_checks/move_check.rs +++ b/compiler/rustc_monomorphize/src/mono_checks/move_check.rs @@ -1,12 +1,12 @@ use rustc_abi::Size; -use rustc_data_structures::fx::FxIndexSet; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::limit::Limit; use rustc_middle::mir::visit::Visitor as MirVisitor; use rustc_middle::mir::{self, Location, traversal}; -use rustc_middle::ty::{self, AssocTag, Instance, Ty, TyCtxt, TypeFoldable}; +use rustc_middle::ty::{self, Instance, Ty, TyCtxt, TypeFoldable}; use rustc_session::lint::builtin::LARGE_ASSIGNMENTS; -use rustc_span::{Ident, Span, Spanned, sym}; +use rustc_span::{Span, Spanned, sym}; use tracing::{debug, trace}; use crate::errors::LargeAssignmentsLint; @@ -98,7 +98,7 @@ impl<'tcx> MoveCheckVisitor<'tcx> { let ty::FnDef(def_id, _) = *callee_ty.kind() else { return; }; - if self.tcx.skip_move_check_fns(()).contains(&def_id) { + if should_skip_fn(self.tcx, def_id) { return; } @@ -188,29 +188,18 @@ impl<'tcx> MoveCheckVisitor<'tcx> { } } -fn assoc_fn_of_type<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId, fn_ident: Ident) -> Option { - for &impl_def_id in tcx.inherent_impls(def_id) { - if let Some(new) = tcx.associated_items(impl_def_id).find_by_ident_and_kind( - tcx, - fn_ident, - AssocTag::Fn, - def_id, - ) { - return Some(new.def_id); - } +/// Return `true` if `def_id` is `Box::new`, `Rc::new` or `Arc::new`. +fn should_skip_fn(tcx: TyCtxt<'_>, def_id: DefId) -> bool { + if let DefKind::AssocFn = tcx.def_kind(def_id) + && tcx.item_name(def_id) == sym::new + && let parent = tcx.parent(def_id) + && let DefKind::Impl { of_trait: false } = tcx.def_kind(parent) + && let ty::Adt(adt_def, ..) = + tcx.type_of(parent).instantiate_identity().skip_normalization().kind() + { + return Some(adt_def.did()) == tcx.lang_items().owned_box() + || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Rc) + || Some(adt_def.did()) == tcx.get_diagnostic_item(sym::Arc); } - None -} - -pub(crate) fn skip_move_check_fns(tcx: TyCtxt<'_>, _: ()) -> FxIndexSet { - let fns = [ - (tcx.lang_items().owned_box(), "new"), - (tcx.get_diagnostic_item(sym::Rc), "new"), - (tcx.get_diagnostic_item(sym::Arc), "new"), - ]; - fns.into_iter() - .filter_map(|(def_id, fn_name)| { - def_id.and_then(|def_id| assoc_fn_of_type(tcx, def_id, Ident::from_str(fn_name))) - }) - .collect() + false } From 74760ffec4463ce10ac0dd521e5eff16d5a00d24 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 24 May 2026 20:29:16 +0800 Subject: [PATCH 152/174] Add suggest-compatible-variants-macro-issue-142359 --- ...-compatible-variants-macro-issue-142359.rs | 6 +++++ ...-compatible-variants-macro-issue-142359.rs | 16 ++++++++++++ ...patible-variants-macro-issue-142359.stderr | 25 +++++++++++++++++++ 3 files changed, 47 insertions(+) create mode 100644 tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs create mode 100644 tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs create mode 100644 tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr diff --git a/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..a94f6244b17e7 --- /dev/null +++ b/tests/ui/typeck/suggestions/auxiliary/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,6 @@ +#[macro_export] +macro_rules! unit { + () => {{ + () + }}; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs new file mode 100644 index 0000000000000..f84ebd6f9ea50 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.rs @@ -0,0 +1,16 @@ +// Make sure we don't suggest compatible variants cross macro context. (issue #142359) +//@ aux-crate:ext=suggest-compatible-variants-macro-issue-142359.rs + +extern crate ext; + +use std::ops::ControlFlow; + +fn main() { + let x: Result = Err(1); + + let _ = match x { + Err(r) => ControlFlow::Break(r), + Ok(r) => { ext::unit!() } //~ ERROR `match` arms have incompatible types [E0308] + + }; +} diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr new file mode 100644 index 0000000000000..63c599d1431d0 --- /dev/null +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr @@ -0,0 +1,25 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:19 + | +LL | let _ = match x { + | _____________- +LL | | Err(r) => ControlFlow::Break(r), + | | --------------------- this is found to be of type `ControlFlow` +LL | | Ok(r) => {ext::unit!()} + | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected enum `ControlFlow` + found unit type `()` + = note: this error originates in the macro `ext::unit` (in Nightly builds, run with -Z macro-backtrace for more info) +help: try wrapping the expression in `std::ops::ControlFlow::Continue` + --> $DIR/auxiliary/suggest-compatible-variants-macro-issue-142359.rs:4:9 + | +LL | std::ops::ControlFlow::Continue(()) + | ++++++++++++++++++++++++++++++++ + + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. From bbd3554304e61fb2af02c5a06f7e58a7d2c61771 Mon Sep 17 00:00:00 2001 From: xizheyin Date: Sun, 24 May 2026 20:37:20 +0800 Subject: [PATCH 153/174] Do not suggest compatible variants crossing macro contexts --- compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs | 7 ++++--- ...gest-compatible-variants-macro-issue-142359.stderr | 11 +++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs index 323f0fb040d6b..6a9a2065f4d31 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/suggestions.rs @@ -2707,12 +2707,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { Some(CtorKind::Const) => unreachable!("unit variants don't have fields"), }; - // Suggest constructor as deep into the block tree as possible. - // This fixes https://github.com/rust-lang/rust/issues/101065, - // and also just helps make the most minimal suggestions. + // Suggest constructor as deep into the block tree as possible, + // but don't cross macro contexts. This fixes #101065 while + // keeping suggestions out of macro definitions (#142359). let mut expr = expr; while let hir::ExprKind::Block(block, _) = &expr.kind && let Some(expr_) = &block.expr + && expr_.span.eq_ctxt(expr.span) { expr = expr_ } diff --git a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr index 63c599d1431d0..7344060a91e4e 100644 --- a/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr +++ b/tests/ui/typeck/suggestions/suggest-compatible-variants-macro-issue-142359.stderr @@ -1,12 +1,12 @@ error[E0308]: `match` arms have incompatible types - --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:19 + --> $DIR/suggest-compatible-variants-macro-issue-142359.rs:13:20 | LL | let _ = match x { | _____________- LL | | Err(r) => ControlFlow::Break(r), | | --------------------- this is found to be of type `ControlFlow` -LL | | Ok(r) => {ext::unit!()} - | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` +LL | | Ok(r) => { ext::unit!() } + | | ^^^^^^^^^^^^ expected `ControlFlow`, found `()` LL | | LL | | }; | |_____- `match` arms have incompatible types @@ -15,10 +15,9 @@ LL | | }; found unit type `()` = note: this error originates in the macro `ext::unit` (in Nightly builds, run with -Z macro-backtrace for more info) help: try wrapping the expression in `std::ops::ControlFlow::Continue` - --> $DIR/auxiliary/suggest-compatible-variants-macro-issue-142359.rs:4:9 | -LL | std::ops::ControlFlow::Continue(()) - | ++++++++++++++++++++++++++++++++ + +LL | Ok(r) => std::ops::ControlFlow::Continue({ ext::unit!() }) + | ++++++++++++++++++++++++++++++++ + error: aborting due to 1 previous error From 3b170fa5fcdb1e7c624ff4da97701476b3311844 Mon Sep 17 00:00:00 2001 From: apiraino Date: Sun, 24 May 2026 15:24:27 +0200 Subject: [PATCH 154/174] Replace code of conduct with link --- src/tools/rustfmt/CODE_OF_CONDUCT.md | 39 +--------------------------- 1 file changed, 1 insertion(+), 38 deletions(-) diff --git a/src/tools/rustfmt/CODE_OF_CONDUCT.md b/src/tools/rustfmt/CODE_OF_CONDUCT.md index 2acddfeefdf6a..e3708bc485399 100644 --- a/src/tools/rustfmt/CODE_OF_CONDUCT.md +++ b/src/tools/rustfmt/CODE_OF_CONDUCT.md @@ -1,40 +1,3 @@ # The Rust Code of Conduct -A version of this document [can be found online](https://www.rust-lang.org/conduct.html). - -## Conduct - -**Contact**: [rust-mods@rust-lang.org](mailto:rust-mods@rust-lang.org) - -* We are committed to providing a friendly, safe and welcoming environment for all, regardless of level of experience, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, religion, nationality, or other similar characteristic. -* On IRC, please avoid using overtly sexual nicknames or other nicknames that might detract from a friendly, safe and welcoming environment for all. -* Please be kind and courteous. There's no need to be mean or rude. -* Respect that people have differences of opinion and that every design or implementation choice carries a trade-off and numerous costs. There is seldom a right answer. -* Please keep unstructured critique to a minimum. If you have solid ideas you want to experiment with, make a fork and see how it works. -* We will exclude you from interaction if you insult, demean or harass anyone. That is not welcome behavior. We interpret the term "harassment" as including the definition in the Citizen Code of Conduct; if you have any lack of clarity about what might be included in that concept, please read their definition. In particular, we don't tolerate behavior that excludes people in socially marginalized groups. -* Private harassment is also unacceptable. No matter who you are, if you feel you have been or are being harassed or made uncomfortable by a community member, please contact one of the channel ops or any of the [Rust moderation team][mod_team] immediately. Whether you're a regular contributor or a newcomer, we care about making this community a safe place for you and we've got your back. -* Likewise any spamming, trolling, flaming, baiting or other attention-stealing behavior is not welcome. - -## Moderation - - -These are the policies for upholding our community's standards of conduct. If you feel that a thread needs moderation, please contact the [Rust moderation team][mod_team]. - -1. Remarks that violate the Rust standards of conduct, including hateful, hurtful, oppressive, or exclusionary remarks, are not allowed. (Cursing is allowed, but never targeting another user, and never in a hateful manner.) -2. Remarks that moderators find inappropriate, whether listed in the code of conduct or not, are also not allowed. -3. Moderators will first respond to such remarks with a warning. -4. If the warning is unheeded, the user will be "kicked," i.e., kicked out of the communication channel to cool off. -5. If the user comes back and continues to make trouble, they will be banned, i.e., indefinitely excluded. -6. Moderators may choose at their discretion to un-ban the user if it was a first offense and they offer the offended party a genuine apology. -7. If a moderator bans someone and you think it was unjustified, please take it up with that moderator, or with a different moderator, **in private**. Complaints about bans in-channel are not allowed. -8. Moderators are held to a higher standard than other community members. If a moderator creates an inappropriate situation, they should expect less leeway than others. - -In the Rust community we strive to go the extra step to look out for each other. Don't just aim to be technically unimpeachable, try to be your best self. In particular, avoid flirting with offensive or sensitive issues, particularly if they're off-topic; this all too often leads to unnecessary fights, hurt feelings, and damaged trust; worse, it can drive people away from the community entirely. - -And if someone takes issue with something you said or did, resist the urge to be defensive. Just stop doing what it was they complained about and apologize. Even if you feel you were misinterpreted or unfairly accused, chances are good there was something you could've communicated better — remember that it's your responsibility to make your fellow Rustaceans comfortable. Everyone wants to get along and we are all here first and foremost because we want to talk about cool technology. You will find that people will be eager to assume good intent and forgive as long as you earn their trust. - -The enforcement policies listed above apply to all official Rust venues; including official IRC channels (#rust, #rust-internals, #rust-tools, #rust-libs, #rustc, #rust-beginners, #rust-docs, #rust-community, #rust-lang, and #cargo); GitHub repositories under rust-lang, rust-lang-nursery, and rust-lang-deprecated; and all forums under rust-lang.org (users.rust-lang.org, internals.rust-lang.org). For other projects adopting the Rust Code of Conduct, please contact the maintainers of those projects for enforcement. If you wish to use this code of conduct for your own project, consider explicitly mentioning your moderation policy or making a copy with your own moderation policy so as to avoid confusion. - -*Adapted from the [Node.js Policy on Trolling](http://blog.izs.me/post/30036893703/policy-on-trolling) as well as the [Contributor Covenant v1.3.0](https://www.contributor-covenant.org/version/1/3/0/).* - -[mod_team]: https://www.rust-lang.org/team.html#Moderation-team +The Code of Conduct for this repository [can be found online](https://www.rust-lang.org/conduct.html). From 22abceee051e3e74281df3e4dde585e446ee5dae Mon Sep 17 00:00:00 2001 From: qaijuang <237468078+qaijuang@users.noreply.github.com> Date: Sun, 24 May 2026 09:24:32 -0400 Subject: [PATCH 155/174] codegen: re-enable FileCheck for scalable-vector tuple intrinsics --- .../scalable-vectors/tuple-intrinsics.rs | 55 ++++++++++--------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs index 7a990b29e6ca1..26bb364c53d07 100644 --- a/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs +++ b/tests/codegen-llvm/scalable-vectors/tuple-intrinsics.rs @@ -1,5 +1,3 @@ -// FIXME: The FileCheck directives in this test are unchecked and probably broken. -//@ skip-filecheck //@ only-aarch64 #![crate_type = "lib"] #![allow(incomplete_features, internal_features)] @@ -38,27 +36,29 @@ pub fn svdup_n_f32(op: f32) -> svfloat32_t { unsafe { _svdup_n_f32(op) } } -// CHECK: define { , } @svcreate2_f32( %x0, %x1) +// CHECK-LABEL: define { , } @svcreate2_f32( +// CHECK-SAME: %x0, %x1) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate2_f32(x0: svfloat32_t, x1: svfloat32_t) -> svfloat32x2_t { - // CHECK: %1 = insertvalue { , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , } %1, %x1, 1 + // CHECK: [[TUP0:%.*]] = insertvalue { , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , } [[TUP0]], %x1, 1 unsafe { std::intrinsics::simd::scalable::sve_tuple_create2(x0, x1) } } -// CHECK: define { , , } @svcreate3_f32( %x0, %x1, %x2) +// CHECK-LABEL: define { , , } @svcreate3_f32( +// CHECK-SAME: %x0, %x1, %x2) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate3_f32(x0: svfloat32_t, x1: svfloat32_t, x2: svfloat32_t) -> svfloat32x3_t { - // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate3_f32 - // CHECK: %1 = insertvalue { , , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , , } %1, %x1, 1 - // CHECK-NEXT: %3 = insertvalue { , , } %2, %x2, 2 + // CHECK: [[TUP0:%.*]] = insertvalue { , , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , , } [[TUP0]], %x1, 1 + // CHECK-NEXT: [[TUP2:%.*]] = insertvalue { , , } [[TUP1]], %x2, 2 unsafe { std::intrinsics::simd::scalable::sve_tuple_create3(x0, x1, x2) } } -// CHECK: define { , , , } @svcreate4_f32( %x0, %x1, %x2, %x3) +// CHECK-LABEL: define { , , , } @svcreate4_f32( +// CHECK-SAME: %x0, %x1, %x2, %x3) #[no_mangle] #[target_feature(enable = "sve")] pub fn svcreate4_f32( @@ -67,35 +67,36 @@ pub fn svcreate4_f32( x2: svfloat32_t, x3: svfloat32_t, ) -> svfloat32x4_t { - // CHECK-LABEL: @_RNvCsk3YxfLN8zWY_6tuples13svcreate4_f32 - // CHECK: %1 = insertvalue { , , , } poison, %x0, 0 - // CHECK-NEXT: %2 = insertvalue { , , , } %1, %x1, 1 - // CHECK-NEXT: %3 = insertvalue { , , , } %2, %x2, 2 - // CHECK-NEXT: %4 = insertvalue { , , , } %3, %x3, 3 + // CHECK: [[TUP0:%.*]] = insertvalue { , , , } poison, %x0, 0 + // CHECK-NEXT: [[TUP1:%.*]] = insertvalue { , , , } [[TUP0]], %x1, 1 + // CHECK-NEXT: [[TUP2:%.*]] = insertvalue { , , , } [[TUP1]], %x2, 2 + // CHECK-NEXT: [[TUP3:%.*]] = insertvalue { , , , } [[TUP2]], %x3, 3 unsafe { std::intrinsics::simd::scalable::sve_tuple_create4(x0, x1, x2, x3) } } -// CHECK: define @svget2_f32({ , } %tup) +// CHECK-LABEL: define @svget2_f32( +// CHECK-SAME: { , } %tup) #[no_mangle] #[target_feature(enable = "sve")] -pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { - // CHECK: %1 = extractvalue { , } %tup, 0 - unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, { IDX }>(tup) } +pub fn svget2_f32(tup: svfloat32x2_t) -> svfloat32_t { + // CHECK: [[X:%.*]] = extractvalue { , } %tup, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_get::<_, _, 0>(tup) } } -// CHECK: define { , } @svset2_f32({ , } %tup, %x) +// CHECK-LABEL: define { , } @svset2_f32( +// CHECK-SAME: { , } %tup, %x) #[no_mangle] #[target_feature(enable = "sve")] -pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { - // CHECK: %1 = insertvalue { , } %tup, %x, 0 - unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, { IDX }>(tup, x) } +pub fn svset2_f32(tup: svfloat32x2_t, x: svfloat32_t) -> svfloat32x2_t { + // CHECK: [[TUP:%.*]] = insertvalue { , } %tup, %x, 0 + unsafe { std::intrinsics::simd::scalable::sve_tuple_set::<_, _, 0>(tup, x) } } -// This function exists only so there are calls to the generic functions +// This function exists only so there are calls to the intrinsics #[target_feature(enable = "sve")] pub fn test() { let x = svdup_n_f32(2f32); let tup = svcreate2_f32(x, x); - let x = svget2_f32::<0>(tup); - let tup = svset2_f32::<0>(tup, x); + let x = svget2_f32(tup); + let _tup = svset2_f32(tup, x); } From fd9acfb4283b20cddfa348b7ef87d85c4095d26e Mon Sep 17 00:00:00 2001 From: sayantn Date: Mon, 11 May 2026 19:03:06 +0530 Subject: [PATCH 156/174] Add intrinsic-test GCC run --- library/stdarch/.github/workflows/main.yml | 7 ++- .../aarch64-unknown-linux-gnu/Dockerfile | 3 +- .../aarch64_be-unknown-linux-gnu/Dockerfile | 5 +- .../armv7-unknown-linux-gnueabihf/Dockerfile | 3 +- .../x86_64-unknown-linux-gnu/Dockerfile | 3 +- library/stdarch/ci/intrinsic-test-docker.sh | 9 ++-- library/stdarch/ci/intrinsic-test.sh | 48 +++++++++++------ .../missing_aarch64_be_clang.txt | 31 +++++++++++ ...ch64.txt => missing_aarch64_be_common.txt} | 3 -- .../intrinsic-test/missing_aarch64_be_gcc.txt | 20 ++++++++ .../intrinsic-test/missing_aarch64_clang.txt | 0 ...ch64_be.txt => missing_aarch64_common.txt} | 43 ---------------- .../intrinsic-test/missing_aarch64_gcc.txt | 19 +++++++ .../intrinsic-test/missing_arm_clang.txt | 0 ...missing_arm.txt => missing_arm_common.txt} | 0 .../intrinsic-test/missing_x86_clang.txt | 24 +++++++++ ...missing_x86.txt => missing_x86_common.txt} | 51 ++++--------------- .../crates/intrinsic-test/missing_x86_gcc.txt | 33 ++++++++++++ .../crates/intrinsic-test/src/arm/mod.rs | 12 +++-- .../crates/intrinsic-test/src/common/cli.rs | 36 +++++++++---- .../intrinsic-test/src/common/gen_rust.rs | 24 ++++++++- .../crates/intrinsic-test/src/common/mod.rs | 10 ++-- .../stdarch/crates/intrinsic-test/src/main.rs | 14 +++-- .../crates/intrinsic-test/src/x86/mod.rs | 7 ++- 24 files changed, 262 insertions(+), 143 deletions(-) create mode 100644 library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt rename library/stdarch/crates/intrinsic-test/{missing_aarch64.txt => missing_aarch64_be_common.txt} (93%) create mode 100644 library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt create mode 100644 library/stdarch/crates/intrinsic-test/missing_aarch64_clang.txt rename library/stdarch/crates/intrinsic-test/{missing_aarch64_be.txt => missing_aarch64_common.txt} (57%) create mode 100644 library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt create mode 100644 library/stdarch/crates/intrinsic-test/missing_arm_clang.txt rename library/stdarch/crates/intrinsic-test/{missing_arm.txt => missing_arm_common.txt} (100%) create mode 100644 library/stdarch/crates/intrinsic-test/missing_x86_clang.txt rename library/stdarch/crates/intrinsic-test/{missing_x86.txt => missing_x86_common.txt} (59%) create mode 100644 library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 42e0b573ea219..9dabe4dd24fa6 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -281,10 +281,13 @@ jobs: - armv7-unknown-linux-gnueabihf - x86_64-unknown-linux-gnu profile: [dev, release] + cc: [clang, gcc] include: - target: aarch64_be-unknown-linux-gnu build_std: true - + exclude: + - target: armv7-unknown-linux-gnueabihf + cc: gcc steps: - uses: actions/checkout@v6 - name: Install Rust @@ -301,7 +304,7 @@ jobs: # Configure some env vars based on matrix configuration - run: echo "PROFILE=${{ matrix.profile }}" >> $GITHUB_ENV - - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} + - run: ./ci/intrinsic-test-docker.sh ${{ matrix.target }} ${{ matrix.cc }} if: ${{ !startsWith(matrix.target, 'thumb') }} env: TARGET: ${{ matrix.target }} diff --git a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile index e2b3d95585efe..1b61dd0c1b87a 100644 --- a/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64-unknown-linux-gnu/Dockerfile @@ -15,7 +15,8 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=aarch64-linux-gnu-gcc ENV CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=aarch64-linux-gnu-gcc \ CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64 -cpu max -L /usr/aarch64-linux-gnu" \ diff --git a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile index d7c12493ad9cf..70acc2d22a418 100644 --- a/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/aarch64_be-unknown-linux-gnu/Dockerfile @@ -23,11 +23,12 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" - ENV AARCH64_BE_TOOLCHAIN="/toolchains/${TOOLCHAIN}" ENV AARCH64_BE_LIBC="${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-gcc" + ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_LINKER="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-gcc" ENV CARGO_TARGET_AARCH64_BE_UNKNOWN_LINUX_GNU_RUNNER="qemu-aarch64_be -cpu max -L ${AARCH64_BE_LIBC}" ENV OBJDUMP="${AARCH64_BE_TOOLCHAIN}/bin/aarch64_be-none-linux-gnu-objdump" diff --git a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile index 02744917af6df..3c8a1b5add27f 100644 --- a/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile +++ b/library/stdarch/ci/docker/armv7-unknown-linux-gnueabihf/Dockerfile @@ -14,7 +14,8 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH=arm-linux-gnueabihf-gcc ENV CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_LINKER=arm-linux-gnueabihf-gcc \ CARGO_TARGET_ARMV7_UNKNOWN_LINUX_GNUEABIHF_RUNNER="qemu-arm -cpu max -L /usr/arm-linux-gnueabihf" \ diff --git a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 17d1ac67e714f..556a948699524 100644 --- a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -16,7 +16,8 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm -ENV PATH="/llvm/bin:$PATH" +ENV CLANG_PATH="/llvm/bin/clang" +ENV GCC_PATH="gcc" ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="/intel-sde/sde64 \ -cpuid-in /checkout/ci/docker/x86_64-unknown-linux-gnu/cpuid.def \ diff --git a/library/stdarch/ci/intrinsic-test-docker.sh b/library/stdarch/ci/intrinsic-test-docker.sh index 948b53dc67bc9..c1d44dca91c5e 100755 --- a/library/stdarch/ci/intrinsic-test-docker.sh +++ b/library/stdarch/ci/intrinsic-test-docker.sh @@ -5,8 +5,8 @@ set -ex -if [ $# -lt 1 ]; then - >&2 echo "Usage: $0 " +if [ $# -lt 2 ]; then + >&2 echo "Usage: $0 " exit 1 fi @@ -29,7 +29,6 @@ run() { --user "$(id -u)":"$(id -g)" \ --env CARGO_HOME=/cargo \ --env CARGO_TARGET_DIR=/checkout/target \ - --env TARGET="${1}" \ --env PROFILE \ --env "${HOST_LINKER}"="cc" \ --env STDARCH_DISABLE_ASSERT_INSTR \ @@ -48,12 +47,12 @@ run() { --workdir /checkout \ --privileged \ stdarch \ - sh -c "HOME=/tmp PATH=\$PATH:/rust/bin exec ci/intrinsic-test.sh" + sh -c "HOME=/tmp PATH=\$PATH:/rust/bin exec ci/intrinsic-test.sh ${1} ${2}" } if [ -z "$1" ]; then >&2 echo "No target specified!" exit 1 else - run "${1}" + run "${1}" "${2}" fi diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index ded53f1a1f332..18aa009c24f2e 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -2,7 +2,23 @@ set -ex -: "${TARGET?The TARGET environment variable must be set.}" +if [ $# -lt 2 ]; then + >&2 echo "Usage: $0 " + exit 1 +fi + +case ${2} in + clang) + export CC="${CLANG_PATH}" + ;; + gcc) + export CC="${GCC_PATH}" + ;; + *) + >&2 echo "Unknown compiler: ${2}" + exit 1 + ;; +esac export RUSTFLAGS="${RUSTFLAGS} -D warnings -Z merge-functions=disabled -Z verify-llvm-ir" export PROFILE="${PROFILE:="release"}" @@ -12,49 +28,49 @@ echo "PROFILE=${PROFILE}" INTRINSIC_TEST="--manifest-path=crates/intrinsic-test/Cargo.toml" -export CC="clang" - -case ${TARGET} in +case ${1} in aarch64_be*) export CFLAGS="-I${AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc/usr/include --sysroot={AARCH64_BE_TOOLCHAIN}/aarch64_be-none-linux-gnu/libc -Wno-nonportable-vector-initialization" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_aarch64_be.txt + ARCH=aarch64_be ;; aarch64*) export CFLAGS="-I/usr/aarch64-linux-gnu/include/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_aarch64.txt + ARCH=aarch64 ;; armv7*) export CFLAGS="-I/usr/arm-linux-gnueabihf/include/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_arm.txt + ARCH=arm ;; x86_64*) export CFLAGS="-I/usr/include/x86_64-linux-gnu/" - TEST_SKIP_INTRINSICS=crates/intrinsic-test/missing_x86.txt + ARCH=x86 ;; *) ;; esac -case "${TARGET}" in +case "${1}" in x86_64-unknown-linux-gnu*) env -u CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER \ cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/x86-intel.xml \ - --skip "${TEST_SKIP_INTRINSICS}" \ - --target "${TARGET}" - - echo "${CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER}" + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ + --target "${1}" \ + --cc-arg-style "${2}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ --bin intrinsic-test -- intrinsics_data/arm_intrinsics.json \ - --skip "${TEST_SKIP_INTRINSICS}" \ - --target "${TARGET}" + --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ + --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ + --target "${1}" \ + --cc-arg-style "${2}" ;; esac -cargo test --manifest-path=rust_programs/Cargo.toml --target "${TARGET}" --profile "${PROFILE}" --tests +cargo test --manifest-path=rust_programs/Cargo.toml --target "${1}" --profile "${PROFILE}" --tests diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt new file mode 100644 index 0000000000000..001538b3ea504 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_clang.txt @@ -0,0 +1,31 @@ +# Bad LLVM codegen for BE in O2 in clang, and release in rust (https://github.com/llvm/llvm-project/issues/166190) +vcmla_laneq_f16 +vcmla_rot180_laneq_f16 +vcmla_rot270_laneq_f16 +vcmla_rot90_laneq_f16 +vcmlaq_lane_f16 +vcmlaq_laneq_f16 +vcmlaq_rot180_lane_f16 +vcmlaq_rot180_laneq_f16 +vcmlaq_rot270_lane_f16 +vcmlaq_rot270_laneq_f16 +vcmlaq_rot90_lane_f16 +vcmlaq_rot90_laneq_f16 + +# Bad codegen for BE in O2 in clang, correct in rust. Same cause as above issue. +vdot_lane_s32 +vdot_lane_u32 +vdot_laneq_s32 +vdot_laneq_u32 +vdotq_lane_s32 +vdotq_lane_u32 +vdotq_laneq_s32 +vdotq_laneq_u32 +vsudot_lane_s32 +vsudot_laneq_s32 +vsudotq_lane_s32 +vsudotq_laneq_s32 +vusdot_lane_s32 +vusdot_laneq_s32 +vusdotq_lane_s32 +vusdotq_laneq_s32 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt similarity index 93% rename from library/stdarch/crates/intrinsic-test/missing_aarch64.txt rename to library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt index f0c9eeb6ce2c9..327c8207a0ff5 100644 --- a/library/stdarch/crates/intrinsic-test/missing_aarch64.txt +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_common.txt @@ -79,6 +79,3 @@ vcvtns_s64_f32 vcvtns_u64_f32 vcvtps_s64_f32 vcvtps_u64_f32 - -# Broken in Clang (fixed in https://github.com/llvm/llvm-project/pull/156029) -vcvth_s16_f16 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt new file mode 100644 index 0000000000000..eccd8a8a14dff --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_be_gcc.txt @@ -0,0 +1,20 @@ +# Broken in LLVM llvm/llvm-project#196999 +vmull_p64 +vmull_high_p64 + +# Broken in LLVM llvm/llvm-project#197083 +vcvth_n_s32_f16 +vcvth_n_u32_f16 +vcvth_n_s64_f16 +vcvth_n_u64_f16 +vcvth_n_f16_s32 +vcvth_n_f16_u32 +vcvth_n_f16_s64 +vcvth_n_f16_u64 + +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125279 +vmaxh_f16 +vminh_f16 + +# Rounding errors +vfms_n_f64 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_clang.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_clang.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt similarity index 57% rename from library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt rename to library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt index 9163aaa1c8db0..327c8207a0ff5 100644 --- a/library/stdarch/crates/intrinsic-test/missing_aarch64_be.txt +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_common.txt @@ -1,43 +1,3 @@ -# Bad LLVM codegen for BE in O2 in clang, and release in rust (https://github.com/llvm/llvm-project/issues/166190) -vcmla_lane_f16 -vcmla_laneq_f16 -vcmla_rot180_lane_f16 -vcmla_rot180_laneq_f16 -vcmla_rot270_lane_f16 -vcmla_rot270_laneq_f16 -vcmla_rot90_lane_f16 -vcmla_rot90_laneq_f16 -vcmlaq_lane_f16 -vcmlaq_laneq_f16 -vcmlaq_laneq_f32 -vcmlaq_rot180_lane_f16 -vcmlaq_rot180_laneq_f16 -vcmlaq_rot180_laneq_f32 -vcmlaq_rot270_lane_f16 -vcmlaq_rot270_laneq_f16 -vcmlaq_rot270_laneq_f32 -vcmlaq_rot90_lane_f16 -vcmlaq_rot90_laneq_f16 -vcmlaq_rot90_laneq_f32 -# Bad codegen for BE in O2 in clang, correct in rust. Same cause as above issue. -vdot_lane_s32 -vdot_lane_u32 -vdot_laneq_s32 -vdot_laneq_u32 -vdotq_lane_s32 -vdotq_lane_u32 -vdotq_laneq_s32 -vdotq_laneq_u32 -vsudot_lane_s32 -vsudot_laneq_s32 -vsudotq_lane_s32 -vsudotq_laneq_s32 -vusdot_lane_s32 -vusdot_laneq_s32 -vusdotq_lane_s32 -vusdotq_laneq_s32 - -# Below are in common to missing_aarch64.txt # Not supported by qemu (will throw illegal instruction) vamin_f16 vaminq_f16 @@ -119,6 +79,3 @@ vcvtns_s64_f32 vcvtns_u64_f32 vcvtps_s64_f32 vcvtps_u64_f32 - -# Broken in Clang -vcvth_s16_f16 diff --git a/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt new file mode 100644 index 0000000000000..a7e90142e4bcd --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_aarch64_gcc.txt @@ -0,0 +1,19 @@ +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=123584, fixed in GCC 16 +vxarq_u64 + +# Broken in LLVM llvm/llvm-project#197083 +vcvth_n_s32_f16 +vcvth_n_u32_f16 +vcvth_n_s64_f16 +vcvth_n_u64_f16 +vcvth_n_f16_s32 +vcvth_n_f16_u32 +vcvth_n_f16_s64 +vcvth_n_f16_u64 + +# Broken in GCC https://gcc.gnu.org/bugzilla/show_bug.cgi?id=125279 +vmaxh_f16 +vminh_f16 + +# Rounding errors +vfms_n_f64 diff --git a/library/stdarch/crates/intrinsic-test/missing_arm_clang.txt b/library/stdarch/crates/intrinsic-test/missing_arm_clang.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/library/stdarch/crates/intrinsic-test/missing_arm.txt b/library/stdarch/crates/intrinsic-test/missing_arm_common.txt similarity index 100% rename from library/stdarch/crates/intrinsic-test/missing_arm.txt rename to library/stdarch/crates/intrinsic-test/missing_arm_common.txt diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt b/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt new file mode 100644 index 0000000000000..b1531830c1a19 --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_clang.txt @@ -0,0 +1,24 @@ +# not present in Clang +_bswap +_bswap64 +_mm_cvtsd_si64x +_mm_cvtsi128_si64x +_mm_cvtsi64x_sd +_mm_cvtsi64x_si128 +_mm_cvttsd_si64x +_popcnt32 +_popcnt64 + +# Clang bug +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/missing_x86.txt b/library/stdarch/crates/intrinsic-test/missing_x86_common.txt similarity index 59% rename from library/stdarch/crates/intrinsic-test/missing_x86.txt rename to library/stdarch/crates/intrinsic-test/missing_x86_common.txt index c7aabb95a84b0..f9b71bbe8c2ca 100644 --- a/library/stdarch/crates/intrinsic-test/missing_x86.txt +++ b/library/stdarch/crates/intrinsic-test/missing_x86_common.txt @@ -1,19 +1,22 @@ -# Are defined under a similar name - -#__bswap_64 -_bswap64 - -# not present in Clang and Rust +# not present in Rust _bit_scan_forward _bit_scan_reverse _castf32_u32 _castf64_u64 _castu32_f32 _castu64_f64 +_cvtsh_ss +_cvtss_sh _lrotl _lrotr _may_i_use_cpu_feature _may_i_use_cpu_feature_ext +_mm256_set1_pch +_mm512_set1_pch +_mm_malloc +_mm_popcnt_u32 +_mm_popcnt_u64 +_mm_set1_pch _rdpmc _rotl _rotl64 @@ -21,29 +24,9 @@ _rotr _rotr64 _rotwl _rotwr -_urdmsr - -# not present in Clang -_bswap -_mm_cvtsd_si64x -_mm_cvtsi128_si64x -_mm_cvtsi64x_sd -_mm_cvtsi64x_si128 -_mm_cvttsd_si64x -_popcnt32 -_popcnt64 - -# not present in Rust -_cvtsh_ss -_cvtss_sh -_mm256_set1_pch -_mm512_set1_pch -_mm_malloc -_mm_popcnt_u32 -_mm_popcnt_u64 -_mm_set1_pch _tpause _umwait +_urdmsr # SDE ERROR: Cannot execute XGETBV with ECX != 0 _xgetbv @@ -63,17 +46,3 @@ _mm512_castph256_ph512 _mm512_castps256_ps512 _mm512_castpd256_pd512 _mm512_castsi256_si512 - -# Clang bug -_mm512_mask_reduce_max_pd -_mm512_mask_reduce_max_ps -_mm512_mask_reduce_min_pd -_mm512_mask_reduce_min_ps - -# Rounding errors in release mode -_mm_maskz_fmadd_sd -_mm_maskz_fmadd_ss -_mm_maskz_fmsub_sd -_mm_maskz_fmsub_ss -_mm_maskz_fnmadd_sd -_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt b/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt new file mode 100644 index 0000000000000..5b71b0698eafc --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_gcc.txt @@ -0,0 +1,33 @@ +# not present in GCC +_bextr2_u32 +_bextr2_u64 +_mm512_cvtepi32lo_pd +_mm512_mask_cvtepi32lo_pd +_mm512_cvtepu32lo_pd +_mm512_mask_cvtepu32lo_pd +_mm512_cvtpd_pslo +_mm512_mask_cvtpd_pslo +_mm512_cvtpslo_pd +_mm512_mask_cvtpslo_pd +_mm512_permutevar_epi32 +_mm512_mask_permutevar_epi32 +_mm_tzcnt_32 +_mm_tzcnt_64 + +# GCC bug +_mm512_reduce_max_pd +_mm512_reduce_max_ps +_mm512_reduce_min_pd +_mm512_reduce_min_ps +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss diff --git a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs index 8935b3ca66f0a..a78a22fde333e 100644 --- a/library/stdarch/crates/intrinsic-test/src/arm/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/arm/mod.rs @@ -5,7 +5,7 @@ mod json_parser; mod types; use crate::common::SupportedArchitectureTest; -use crate::common::cli::ProcessedCli; +use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; use intrinsic::ArmIntrinsicType; @@ -29,11 +29,15 @@ impl SupportedArchitectureTest for ArmArchitectureTest { const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - fn arch_flags(&self) -> Vec<&str> { - vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"] + fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str> { + // GCC uses an extra `-` in the arch name + match cli_options.cc_arg_style { + CcArgStyle::Clang => vec!["-march=armv8.6a+crypto+crc+dotprod+fp16"], + CcArgStyle::Gcc => vec!["-march=armv8.6-a+crypto+crc+dotprod+fp16+sha3+sm4"], + } } - fn create(cli_options: ProcessedCli) -> Self { + fn create(cli_options: &ProcessedCli) -> Self { let a32 = cli_options.target.starts_with("armv7"); let mut intrinsics = get_neon_intrinsics(&cli_options.filename).expect("Error parsing input file"); diff --git a/library/stdarch/crates/intrinsic-test/src/common/cli.rs b/library/stdarch/crates/intrinsic-test/src/common/cli.rs index 07f94eba18f33..3ea87df51fda2 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/cli.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/cli.rs @@ -13,7 +13,7 @@ pub struct Cli { /// Filename for a list of intrinsics to skip (one per line) #[arg(long)] - pub skip: Option, + pub skip: Vec, /// Pass a target the test suite #[arg(long)] @@ -22,6 +22,16 @@ pub struct Cli { /// Percentage of intrinsics to test (used to limit testing to keep CI times manageable) #[arg(long, default_value_t = 100u8)] pub sample_percentage: u8, + + /// Argument style of the C compiler + #[arg(long)] + pub cc_arg_style: CcArgStyle, +} + +#[derive(Copy, Clone, clap::ValueEnum)] +pub enum CcArgStyle { + Gcc, + Clang, } pub struct ProcessedCli { @@ -29,6 +39,7 @@ pub struct ProcessedCli { pub target: String, pub skip: Vec, pub sample_percentage: u8, + pub cc_arg_style: CcArgStyle, } impl ProcessedCli { @@ -37,22 +48,25 @@ impl ProcessedCli { let target = cli_options.target; let sample_percentage = cli_options.sample_percentage; - let skip = if let Some(filename) = cli_options.skip { - let data = std::fs::read_to_string(&filename).expect("Failed to open file"); - data.lines() - .map(str::trim) - .filter(|s| !s.contains('#')) - .map(String::from) - .collect_vec() - } else { - Default::default() - }; + let skip = cli_options + .skip + .iter() + .flat_map(|filename| { + std::fs::read_to_string(&filename) + .expect("Failed to open file") + .lines() + .map(|line| line.trim().to_owned()) + .filter(|line| !line.contains('#')) + .collect_vec() + }) + .collect_vec(); Self { target, skip, filename, sample_percentage, + cc_arg_style: cli_options.cc_arg_style, } } } diff --git a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs index 039e78f5778bf..cb07fa600474c 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/gen_rust.rs @@ -4,6 +4,7 @@ use itertools::Itertools; use super::intrinsic_helpers::IntrinsicTypeDefinition; use crate::common::argument::ArgumentList; +use crate::common::cli::{CcArgStyle, ProcessedCli}; use crate::common::intrinsic::Intrinsic; use crate::common::intrinsic_helpers::TypeKind; @@ -314,8 +315,18 @@ pub fn write_build_rs( w: &mut impl std::io::Write, i: usize, arch_flags: &[&str], + cli_options: &ProcessedCli, ) -> std::io::Result<()> { - const COMMON_FLAGS: &[&str] = &["-ffp-contract=off", "-ffp-model=strict", "-Wno-narrowing"]; + const COMMON_FLAGS: &[&str] = &["-ffp-contract=off", "-Wno-narrowing"]; + const CLANG_FLAGS: &[&str] = &["-ffp-model=strict"]; + const GCC_FLAGS: &[&str] = &[ + "-flax-vector-conversions", + "-fno-fast-math", + "-frounding-math", + "-fexcess-precision=standard", + "-ftrapping-math", + "-fsignaling-nans", + ]; write!( w, @@ -329,7 +340,16 @@ pub fn write_build_rs( i = i )?; - for flag in COMMON_FLAGS.iter().chain(arch_flags) { + let compiler_specific_flags = match cli_options.cc_arg_style { + CcArgStyle::Gcc => GCC_FLAGS, + CcArgStyle::Clang => CLANG_FLAGS, + }; + + for flag in COMMON_FLAGS + .iter() + .chain(compiler_specific_flags) + .chain(arch_flags) + { writeln!(w, "\"{flag}\",")?; } diff --git a/library/stdarch/crates/intrinsic-test/src/common/mod.rs b/library/stdarch/crates/intrinsic-test/src/common/mod.rs index b5f8dd5aba151..3775e453c24b0 100644 --- a/library/stdarch/crates/intrinsic-test/src/common/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/common/mod.rs @@ -30,7 +30,7 @@ pub trait SupportedArchitectureTest { fn intrinsics(&self) -> &[Intrinsic]; - fn create(cli_options: ProcessedCli) -> Self; + fn create(cli_options: &ProcessedCli) -> Self; const NOTICE: &str; @@ -39,7 +39,7 @@ pub trait SupportedArchitectureTest { const PLATFORM_RUST_CFGS: &str; const PLATFORM_RUST_DEFINITIONS: &str; - fn arch_flags(&self) -> Vec<&str>; + fn arch_flags(&self, cli_options: &ProcessedCli) -> Vec<&str>; fn generate_c_file(&self) { let (max_chunk_size, _chunk_count) = manual_chunk(self.intrinsics().len()); @@ -57,8 +57,8 @@ pub trait SupportedArchitectureTest { .unwrap(); } - fn generate_rust_file(&self) { - let arch_flags = self.arch_flags(); + fn generate_rust_file(&self, cli_options: &ProcessedCli) { + let arch_flags = self.arch_flags(cli_options); std::fs::create_dir_all("rust_programs").unwrap(); @@ -97,7 +97,7 @@ pub trait SupportedArchitectureTest { trace!("generating `{build_rs_filename}`"); let mut file = File::create(&build_rs_filename).unwrap(); - write_build_rs(&mut file, i, &arch_flags).unwrap(); + write_build_rs(&mut file, i, &arch_flags, &cli_options).unwrap(); run_rustfmt(&build_rs_filename); Ok(()) diff --git a/library/stdarch/crates/intrinsic-test/src/main.rs b/library/stdarch/crates/intrinsic-test/src/main.rs index 9f57c99f12cf5..4c0136041fc35 100644 --- a/library/stdarch/crates/intrinsic-test/src/main.rs +++ b/library/stdarch/crates/intrinsic-test/src/main.rs @@ -18,18 +18,24 @@ fn main() { if processed_cli_options.target.starts_with("arm") | processed_cli_options.target.starts_with("aarch64") { - run(ArmArchitectureTest::create(processed_cli_options)) + run( + ArmArchitectureTest::create(&processed_cli_options), + processed_cli_options, + ) } else if processed_cli_options.target.starts_with("x86") { - run(X86ArchitectureTest::create(processed_cli_options)) + run( + X86ArchitectureTest::create(&processed_cli_options), + processed_cli_options, + ) } else { unimplemented!("Unsupported target {}", processed_cli_options.target) } } -fn run(test_environment: impl SupportedArchitectureTest) { +fn run(test_environment: impl SupportedArchitectureTest, processed_cli_options: ProcessedCli) { info!("building C binaries"); test_environment.generate_c_file(); info!("building Rust binaries"); - test_environment.generate_rust_file(); + test_environment.generate_rust_file(&processed_cli_options); } diff --git a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs index 5d4798482a1d3..288bd8bdf8961 100644 --- a/library/stdarch/crates/intrinsic-test/src/x86/mod.rs +++ b/library/stdarch/crates/intrinsic-test/src/x86/mod.rs @@ -29,8 +29,11 @@ impl SupportedArchitectureTest for X86ArchitectureTest { const PLATFORM_RUST_DEFINITIONS: &str = config::PLATFORM_RUST_DEFINITIONS; const PLATFORM_RUST_CFGS: &str = config::PLATFORM_RUST_CFGS; - fn arch_flags(&self) -> Vec<&str> { + fn arch_flags(&self, _cli_options: &ProcessedCli) -> Vec<&str> { vec![ + "-maes", + "-mf16c", + "-mfma", "-mavx", "-mavx2", "-mavx512f", @@ -66,7 +69,7 @@ impl SupportedArchitectureTest for X86ArchitectureTest { ] } - fn create(cli_options: ProcessedCli) -> Self { + fn create(cli_options: &ProcessedCli) -> Self { let mut intrinsics = get_xml_intrinsics(&cli_options.filename).expect("Error parsing input file"); From 44475cc694b885c7b8d4d299d02672a0d5251274 Mon Sep 17 00:00:00 2001 From: sayantn Date: Thu, 14 May 2026 17:24:05 +0530 Subject: [PATCH 157/174] Add intrinsic-test ICX run --- library/stdarch/.github/workflows/main.yml | 6 ++++++ .../x86_64-unknown-linux-gnu/Dockerfile | 13 +++++++++++- library/stdarch/ci/intrinsic-test.sh | 11 ++++++++-- .../crates/intrinsic-test/missing_x86_icx.txt | 20 +++++++++++++++++++ 4 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 library/stdarch/crates/intrinsic-test/missing_x86_icx.txt diff --git a/library/stdarch/.github/workflows/main.yml b/library/stdarch/.github/workflows/main.yml index 9dabe4dd24fa6..98f6b842d135f 100644 --- a/library/stdarch/.github/workflows/main.yml +++ b/library/stdarch/.github/workflows/main.yml @@ -285,6 +285,12 @@ jobs: include: - target: aarch64_be-unknown-linux-gnu build_std: true + - target: x86_64-unknown-linux-gnu + cc: icx + profile: dev + - target: x86_64-unknown-linux-gnu + cc: icx + profile: release exclude: - target: armv7-unknown-linux-gnueabihf cc: gcc diff --git a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile index 556a948699524..efbb2b0853371 100644 --- a/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile +++ b/library/stdarch/ci/docker/x86_64-unknown-linux-gnu/Dockerfile @@ -6,7 +6,8 @@ RUN apt-get update && apt-get install -y --no-install-recommends \ make \ ca-certificates \ wget \ - xz-utils + xz-utils \ + gpg RUN wget http://ci-mirrors.rust-lang.org/sde-external-10.8.0-2026-03-15-lin.tar.xz -O sde.tar.xz RUN mkdir intel-sde @@ -16,8 +17,18 @@ RUN wget https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-22.1.4-x86_64 RUN mkdir llvm RUN tar -xvf llvm.tar.xz --strip-components=1 -C llvm +RUN wget -O- https://apt.repos.intel.com/intel-gpg-keys/GPG-PUB-KEY-INTEL-SW-PRODUCTS.PUB |\ + gpg --dearmor |\ + tee /usr/share/keyrings/oneapi-archive-keyring.gpg > /dev/null + +RUN echo "deb [signed-by=/usr/share/keyrings/oneapi-archive-keyring.gpg] https://apt.repos.intel.com/oneapi all main" |\ + tee /etc/apt/sources.list.d/oneAPI.list + +RUN apt-get update && apt-get install -y --no-install-recommends intel-oneapi-compiler-dpcpp-cpp + ENV CLANG_PATH="/llvm/bin/clang" ENV GCC_PATH="gcc" +ENV ICX_PATH="/opt/intel/oneapi/compiler/2026.0/bin/icx" ENV CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_RUNNER="/intel-sde/sde64 \ -cpuid-in /checkout/ci/docker/x86_64-unknown-linux-gnu/cpuid.def \ diff --git a/library/stdarch/ci/intrinsic-test.sh b/library/stdarch/ci/intrinsic-test.sh index 18aa009c24f2e..0441611f38fd2 100755 --- a/library/stdarch/ci/intrinsic-test.sh +++ b/library/stdarch/ci/intrinsic-test.sh @@ -10,9 +10,16 @@ fi case ${2} in clang) export CC="${CLANG_PATH}" + CC_ARG_STYLE=clang ;; gcc) export CC="${GCC_PATH}" + CC_ARG_STYLE=gcc + ;; + icx) + export CC="${ICX_PATH}" + # `icx` uses clang-style arguments + CC_ARG_STYLE=clang ;; *) >&2 echo "Unknown compiler: ${2}" @@ -61,7 +68,7 @@ case "${1}" in --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ --target "${1}" \ - --cc-arg-style "${2}" + --cc-arg-style "${CC_ARG_STYLE}" ;; *) cargo run "${INTRINSIC_TEST}" --release \ @@ -69,7 +76,7 @@ case "${1}" in --skip "crates/intrinsic-test/missing_${ARCH}_common.txt" \ --skip "crates/intrinsic-test/missing_${ARCH}_${2}.txt" \ --target "${1}" \ - --cc-arg-style "${2}" + --cc-arg-style "${CC_ARG_STYLE}" ;; esac diff --git a/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt b/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt new file mode 100644 index 0000000000000..6d3133c85db9c --- /dev/null +++ b/library/stdarch/crates/intrinsic-test/missing_x86_icx.txt @@ -0,0 +1,20 @@ +# not present in ICX +_mm_cvtsd_si64x +_mm_cvtsi128_si64x +_mm_cvtsi64x_sd +_mm_cvtsi64x_si128 +_mm_cvttsd_si64x + +# ICX bug +_mm512_mask_reduce_max_pd +_mm512_mask_reduce_max_ps +_mm512_mask_reduce_min_pd +_mm512_mask_reduce_min_ps + +# Rounding errors in release mode +_mm_maskz_fmadd_sd +_mm_maskz_fmadd_ss +_mm_maskz_fmsub_sd +_mm_maskz_fmsub_ss +_mm_maskz_fnmadd_sd +_mm_maskz_fnmadd_ss From ccac0d974451a0888f3ca616fd622c6c41aac96c Mon Sep 17 00:00:00 2001 From: Dnreikronos Date: Sun, 24 May 2026 12:01:41 -0300 Subject: [PATCH 158/174] Suppress garbled suggestions from strict provenance lints in macros --- compiler/rustc_hir_typeck/src/cast.rs | 50 +++++++++------- compiler/rustc_hir_typeck/src/errors.rs | 4 +- .../lint-strict-provenance-macro-casts.rs | 33 ++++++++++ .../lint-strict-provenance-macro-casts.stderr | 60 +++++++++++++++++++ 4 files changed, 122 insertions(+), 25 deletions(-) create mode 100644 tests/ui/lint/lint-strict-provenance-macro-casts.rs create mode 100644 tests/ui/lint/lint-strict-provenance-macro-casts.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 4d934703f4670..12429218a6fc1 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -1134,28 +1134,30 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_prec = fcx.precedence(self.expr); - let needs_parens = expr_prec < ExprPrecedence::Unambiguous; - - let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - let expr_span = self.expr_span.shrink_to_lo(); - let sugg = match (needs_parens, needs_cast) { - (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_ty, - }, - (true, false) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } + + let sugg = self.span.can_be_used_for_suggestions().then(|| { + let expr_prec = fcx.precedence(self.expr); + let needs_parens = expr_prec < ExprPrecedence::Unambiguous; + let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); + let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); + let expr_span = self.expr_span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { + expr_span, + cast_span, + cast_ty, + }, + (true, false) => { + errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } + } + (false, true) => { + errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } + } + (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, } - (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, - }; + }); let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; fcx.tcx.emit_node_span_lint( @@ -1167,10 +1169,12 @@ impl<'a, 'tcx> CastCheck<'tcx> { } fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let sugg = errors::LossyProvenanceInt2PtrSuggestion { - lo: self.expr_span.shrink_to_lo(), - hi: self.expr_span.shrink_to_hi().to(self.cast_span), - }; + let sugg = self.span.can_be_used_for_suggestions().then(|| { + errors::LossyProvenanceInt2PtrSuggestion { + lo: self.expr_span.shrink_to_lo(), + hi: self.expr_span.shrink_to_hi().to(self.cast_span), + } + }); let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index 3f8ac61eed084..fcbb8a75f9567 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -386,7 +386,7 @@ pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: LossyProvenanceInt2PtrSuggestion, + pub sugg: Option, } #[derive(Diagnostic)] @@ -427,7 +427,7 @@ pub(crate) struct LossyProvenancePtr2Int<'tcx> { pub expr_ty: Ty<'tcx>, pub cast_ty: Ty<'tcx>, #[subdiagnostic] - pub sugg: LossyProvenancePtr2IntSuggestion<'tcx>, + pub sugg: Option>, } #[derive(Subdiagnostic)] diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.rs b/tests/ui/lint/lint-strict-provenance-macro-casts.rs new file mode 100644 index 0000000000000..6d1c02c91fdbd --- /dev/null +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.rs @@ -0,0 +1,33 @@ +#![feature(strict_provenance_lints)] +#![deny(lossy_provenance_casts)] +#![deny(fuzzy_provenance_casts)] + +macro_rules! cast { + ($e:expr, $t:ty) => { + $e as $t + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + //~| ERROR strict provenance disallows casting integer `usize` to pointer `*const u8` + }; +} + +macro_rules! p2i { + ($e:expr) => { $e as usize }; + //~^ ERROR under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` +} + +macro_rules! i2p { + ($e:expr) => { $e as *const () }; + //~^ ERROR strict provenance disallows casting integer `usize` to pointer `*const ()` +} + +fn main() { + let ptr = &0u8 as *const u8; + let _addr = cast!(ptr, usize); + + let _ptr = cast!(0usize, *const u8); + + let x = 1u8; + p2i!(&raw const x); + + i2p!(0x42); +} diff --git a/tests/ui/lint/lint-strict-provenance-macro-casts.stderr b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr new file mode 100644 index 0000000000000..a7ec3d56965f3 --- /dev/null +++ b/tests/ui/lint/lint-strict-provenance-macro-casts.stderr @@ -0,0 +1,60 @@ +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 + | +LL | $e as $t + | ^^^^^^^^ +... +LL | let _addr = cast!(ptr, usize); + | ----------------- in this macro invocation + | + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead +note: the lint level is defined here + --> $DIR/lint-strict-provenance-macro-casts.rs:2:9 + | +LL | #![deny(lossy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: strict provenance disallows casting integer `usize` to pointer `*const u8` + --> $DIR/lint-strict-provenance-macro-casts.rs:7:9 + | +LL | $e as $t + | ^^^^^^^^ +... +LL | let _ptr = cast!(0usize, *const u8); + | ------------------------ in this macro invocation + | + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead +note: the lint level is defined here + --> $DIR/lint-strict-provenance-macro-casts.rs:3:9 + | +LL | #![deny(fuzzy_provenance_casts)] + | ^^^^^^^^^^^^^^^^^^^^^^ + = note: this error originates in the macro `cast` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: under strict provenance it is considered bad style to cast pointer `*const u8` to integer `usize` + --> $DIR/lint-strict-provenance-macro-casts.rs:14:20 + | +LL | ($e:expr) => { $e as usize }; + | ^^^^^^^^^^^ +... +LL | p2i!(&raw const x); + | ------------------ in this macro invocation + | + = help: if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead + = note: this error originates in the macro `p2i` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: strict provenance disallows casting integer `usize` to pointer `*const ()` + --> $DIR/lint-strict-provenance-macro-casts.rs:19:20 + | +LL | ($e:expr) => { $e as *const () }; + | ^^^^^^^^^^^^^^^ +... +LL | i2p!(0x42); + | ---------- in this macro invocation + | + = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead + = note: this error originates in the macro `i2p` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: aborting due to 4 previous errors + From 00ada0feb14335ad5f18f284c0efb5d73168583e Mon Sep 17 00:00:00 2001 From: Folkert de Vries Date: Sun, 24 May 2026 18:59:08 +0200 Subject: [PATCH 159/174] Revert "Allow `global_asm!` in statement positions" This reverts commit 450cdb550130dfaab2c29500d5e1b4d0093f7b09. --- compiler/rustc_builtin_macros/src/asm.rs | 42 ++++---------- tests/ui/asm/naked-functions.rs | 8 +-- tests/ui/asm/naked-functions.stderr | 56 +++++++++---------- tests/ui/asm/statement-global-asm-error.rs | 13 ----- .../ui/asm/statement-global-asm-error.stderr | 14 ----- tests/ui/asm/statement-global-asm.rs | 8 --- 6 files changed, 37 insertions(+), 104 deletions(-) delete mode 100644 tests/ui/asm/statement-global-asm-error.rs delete mode 100644 tests/ui/asm/statement-global-asm-error.stderr delete mode 100644 tests/ui/asm/statement-global-asm.rs diff --git a/compiler/rustc_builtin_macros/src/asm.rs b/compiler/rustc_builtin_macros/src/asm.rs index d779089eeaa4c..a1e14b5245137 100644 --- a/compiler/rustc_builtin_macros/src/asm.rs +++ b/compiler/rustc_builtin_macros/src/asm.rs @@ -10,7 +10,7 @@ use rustc_parse_format as parse; use rustc_session::lint; use rustc_span::{ErrorGuaranteed, InnerSpan, Span, Symbol, sym}; use rustc_target::asm::InlineAsmArch; -use smallvec::{SmallVec, smallvec}; +use smallvec::smallvec; use crate::errors; use crate::util::{ExprToSpannedString, expr_to_spanned_string}; @@ -26,24 +26,6 @@ struct ValidatedAsmArgs { pub options_spans: Vec, } -struct MacGlobalAsm { - item: ast::Item, -} - -impl MacResult for MacGlobalAsm { - fn make_items(self: Box) -> Option; 1]>> { - Some(smallvec![Box::new(self.item)]) - } - - fn make_stmts(self: Box) -> Option> { - Some(smallvec![ast::Stmt { - id: ast::DUMMY_NODE_ID, - span: self.item.span, - kind: ast::StmtKind::Item(Box::new(self.item)), - }]) - } -} - fn parse_args<'a>( ecx: &ExtCtxt<'a>, sp: Span, @@ -668,20 +650,18 @@ pub(super) fn expand_global_asm<'cx>( return ExpandResult::Retry(()); }; match mac { - Ok(inline_asm) => Box::new(MacGlobalAsm { - item: ast::Item { - attrs: ast::AttrVec::new(), - id: ast::DUMMY_NODE_ID, - kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), - vis: ast::Visibility { - span: sp.shrink_to_lo(), - kind: ast::VisibilityKind::Inherited, - tokens: None, - }, - span: sp, + Ok(inline_asm) => MacEager::items(smallvec![Box::new(ast::Item { + attrs: ast::AttrVec::new(), + id: ast::DUMMY_NODE_ID, + kind: ast::ItemKind::GlobalAsm(Box::new(inline_asm)), + vis: ast::Visibility { + span: sp.shrink_to_lo(), + kind: ast::VisibilityKind::Inherited, tokens: None, }, - }), + span: sp, + tokens: None, + })]), Err(guar) => DummyResult::any(sp, guar), } } diff --git a/tests/ui/asm/naked-functions.rs b/tests/ui/asm/naked-functions.rs index abe26ca91e5d8..55f2f552ad31c 100644 --- a/tests/ui/asm/naked-functions.rs +++ b/tests/ui/asm/naked-functions.rs @@ -6,7 +6,7 @@ #![feature(asm_unwind, linkage, rustc_attrs, cfg_target_object_format)] #![crate_type = "lib"] -use std::arch::{asm, global_asm, naked_asm}; +use std::arch::{asm, naked_asm}; #[unsafe(naked)] pub extern "C" fn inline_asm_macro() { @@ -14,12 +14,6 @@ pub extern "C" fn inline_asm_macro() { //~^ERROR the `asm!` macro is not allowed in naked functions } -#[unsafe(naked)] -pub extern "C" fn global_asm_macro() { - //~^ERROR naked functions must contain a single `naked_asm!` invocation - global_asm!(""); -} - #[repr(C)] pub struct P { x: u8, diff --git a/tests/ui/asm/naked-functions.stderr b/tests/ui/asm/naked-functions.stderr index e8963f7e1d52c..2b67c3aecd73c 100644 --- a/tests/ui/asm/naked-functions.stderr +++ b/tests/ui/asm/naked-functions.stderr @@ -1,71 +1,71 @@ error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:53:29 + --> $DIR/naked-functions.rs:47:29 | LL | naked_asm!("/* {0} */", in(reg) a) | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `in` operand cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:74:10 + --> $DIR/naked-functions.rs:68:10 | LL | in(reg) a, | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it error: the `noreturn` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:94:28 + --> $DIR/naked-functions.rs:88:28 | LL | naked_asm!("", options(noreturn)); | ^^^^^^^^ the `noreturn` option is not meaningful for global-scoped inline assembly error: the `nomem` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:28 + --> $DIR/naked-functions.rs:105:28 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly error: the `preserves_flags` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:111:35 + --> $DIR/naked-functions.rs:105:35 | LL | naked_asm!("", options(nomem, preserves_flags)); | ^^^^^^^^^^^^^^^ the `preserves_flags` option is not meaningful for global-scoped inline assembly error: the `readonly` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:28 + --> $DIR/naked-functions.rs:112:28 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^^ the `readonly` option is not meaningful for global-scoped inline assembly error: the `nostack` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:38 + --> $DIR/naked-functions.rs:112:38 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^^^^ the `nostack` option is not meaningful for global-scoped inline assembly error: the `pure` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:118:56 + --> $DIR/naked-functions.rs:112:56 | LL | naked_asm!("", options(readonly, nostack), options(pure)); | ^^^^ the `pure` option is not meaningful for global-scoped inline assembly error: the `may_unwind` option cannot be used with `naked_asm!` - --> $DIR/naked-functions.rs:126:28 + --> $DIR/naked-functions.rs:120:28 | LL | naked_asm!("", options(may_unwind)); | ^^^^^^^^^^ the `may_unwind` option is not meaningful for global-scoped inline assembly error: this is a user specified error - --> $DIR/naked-functions.rs:157:5 + --> $DIR/naked-functions.rs:151:5 | LL | compile_error!("this is a user specified error") | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: this is a user specified error - --> $DIR/naked-functions.rs:163:5 + --> $DIR/naked-functions.rs:157:5 | LL | compile_error!("this is a user specified error"); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: asm template must be a string literal - --> $DIR/naked-functions.rs:170:16 + --> $DIR/naked-functions.rs:164:16 | LL | naked_asm!(invalid_syntax) | ^^^^^^^^^^^^^^ @@ -76,38 +76,32 @@ error[E0787]: the `asm!` macro is not allowed in naked functions LL | unsafe { asm!("", options(raw)) }; | ^^^^^^^^^^^^^^^^^^^^^^ consider using the `naked_asm!` macro instead -error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:18:1 - | -LL | pub extern "C" fn global_asm_macro() { - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:31:5 + --> $DIR/naked-functions.rs:25:5 | LL | mut a: u32, | ^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:33:5 + --> $DIR/naked-functions.rs:27:5 | LL | &b: &i32, | ^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:35:6 + --> $DIR/naked-functions.rs:29:6 | LL | (None | Some(_)): Option>, | ^^^^^^^^^^^^^^ error: patterns not allowed in naked function parameters - --> $DIR/naked-functions.rs:37:5 + --> $DIR/naked-functions.rs:31:5 | LL | P { x, y }: P, | ^^^^^^^^^^ error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:46:5 + --> $DIR/naked-functions.rs:40:5 | LL | a + 1 | ^ @@ -115,7 +109,7 @@ LL | a + 1 = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:44:1 + --> $DIR/naked-functions.rs:38:1 | LL | pub extern "C" fn inc(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -124,7 +118,7 @@ LL | a + 1 | ----- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:58:1 + --> $DIR/naked-functions.rs:52:1 | LL | pub extern "C" fn inc_closure(a: u32) -> u32 { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -133,7 +127,7 @@ LL | (|| a + 1)() | ------------ not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:64:1 + --> $DIR/naked-functions.rs:58:1 | LL | pub extern "C" fn unsupported_operands() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -150,13 +144,13 @@ LL | let mut e = 0usize; | ------------------- not allowed in naked functions error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:86:1 + --> $DIR/naked-functions.rs:80:1 | LL | pub extern "C" fn missing_assembly() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:91:1 + --> $DIR/naked-functions.rs:85:1 | LL | pub extern "C" fn too_many_asm_blocks() { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -165,7 +159,7 @@ LL | naked_asm!(""); | -------------- multiple `naked_asm!` invocations are not allowed in naked functions error: referencing function parameters is not allowed in naked functions - --> $DIR/naked-functions.rs:103:11 + --> $DIR/naked-functions.rs:97:11 | LL | *&y | ^ @@ -173,7 +167,7 @@ LL | *&y = help: follow the calling convention in asm block to use parameters error[E0787]: naked functions must contain a single `naked_asm!` invocation - --> $DIR/naked-functions.rs:101:5 + --> $DIR/naked-functions.rs:95:5 | LL | pub extern "C" fn inner(y: usize) -> usize { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -181,6 +175,6 @@ LL | LL | *&y | --- not allowed in naked functions -error: aborting due to 26 previous errors +error: aborting due to 25 previous errors For more information about this error, try `rustc --explain E0787`. diff --git a/tests/ui/asm/statement-global-asm-error.rs b/tests/ui/asm/statement-global-asm-error.rs deleted file mode 100644 index f32ba73357a19..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ needs-asm-support - -use std::arch::global_asm; - -fn main() { - let x = 42; - global_asm!("{}", in(x)); - //~^ ERROR the `in` operand cannot be used with `global_asm!` - //~^^ NOTE the `in` operand is not meaningful for global-scoped inline assembly, remove it - - let y = global_asm!(""); - //~^ ERROR non-expression macro in expression position: global_asm -} diff --git a/tests/ui/asm/statement-global-asm-error.stderr b/tests/ui/asm/statement-global-asm-error.stderr deleted file mode 100644 index 35dde6ac1a781..0000000000000 --- a/tests/ui/asm/statement-global-asm-error.stderr +++ /dev/null @@ -1,14 +0,0 @@ -error: the `in` operand cannot be used with `global_asm!` - --> $DIR/statement-global-asm-error.rs:7:23 - | -LL | global_asm!("{}", in(x)); - | ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it - -error: non-expression macro in expression position: global_asm - --> $DIR/statement-global-asm-error.rs:11:13 - | -LL | let y = global_asm!(""); - | ^^^^^^^^^^^^^^^ - -error: aborting due to 2 previous errors - diff --git a/tests/ui/asm/statement-global-asm.rs b/tests/ui/asm/statement-global-asm.rs deleted file mode 100644 index 597495546d955..0000000000000 --- a/tests/ui/asm/statement-global-asm.rs +++ /dev/null @@ -1,8 +0,0 @@ -//@ needs-asm-support -//@ run-pass - -use std::arch::global_asm; - -fn main() { - global_asm!(""); -} From a6361c8127b0979645b5218d40a778db581c6bca Mon Sep 17 00:00:00 2001 From: Jieyou Xu Date: Sun, 24 May 2026 20:09:10 +0200 Subject: [PATCH 160/174] Disable `tests/debuginfo/numeric-types.rs` `v nz_{i,u}size` checks On lldb 22 (locally lldb 22.1.6) I see ``` v nz_isize error: Invalid type: Cannot determine size [...] v nz_usize error: Invalid type: Cannot determine size ``` AFAICT this might be an upstream issue, as reported in . Let's disable for now to not have `./x test` fail for contributors using lldb 22. --- tests/debuginfo/numeric-types.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/debuginfo/numeric-types.rs b/tests/debuginfo/numeric-types.rs index a27102da96923..31b439ba3f900 100644 --- a/tests/debuginfo/numeric-types.rs +++ b/tests/debuginfo/numeric-types.rs @@ -213,8 +213,9 @@ //@ lldb-command:v nz_i128 //@ lldb-check:[...] 55 { 0 = { 0 = 55 } } -//@ lldb-command:v nz_isize -//@ lldb-check:[...] 66 { 0 = { 0 = 66 } } +// FIXME(#156886): "error: Invalid type: Cannot determine size" +//(DISABLED) @ lldb-command:v nz_isize +//(DISABLED) @ lldb-check:[...] 66 { 0 = { 0 = 66 } } //@ lldb-command:v/d nz_u8 //@ lldb-check:[...] 77 { 0 = { 0 = 77 } } @@ -231,8 +232,9 @@ //@ lldb-command:v nz_u128 //@ lldb-check:[...] 111 { 0 = { 0 = 111 } } -//@ lldb-command:v nz_usize -//@ lldb-check:[...] 122 { 0 = { 0 = 122 } } +// FIXME(#156886): "error: Invalid type: Cannot determine size" +//(DISABLED) @ lldb-command:v nz_usize +//(DISABLED) @ lldb-check:[...] 122 { 0 = { 0 = 122 } } use std::num::*; use std::sync::atomic::*; From 035d7ddfa3a40054d43e366d71618e4b11fd5c44 Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Sat, 23 May 2026 16:32:14 +0200 Subject: [PATCH 161/174] loongarch: CRC intrinsics require loongarch64 GCC and LLVM both only allow using the CRC intrinsics on LA64. --- .../crates/core_arch/src/loongarch64/mod.rs | 54 +++++++++++++++++++ .../core_arch/src/loongarch_shared/mod.rs | 54 ------------------- 2 files changed, 54 insertions(+), 54 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index f464dbd356b7f..11b882b4280f9 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -22,8 +22,20 @@ pub fn rdtime_d() -> (i64, isize) { #[allow(improper_ctypes)] unsafe extern "unadjusted" { + #[link_name = "llvm.loongarch.crc.w.b.w"] + fn __crc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crc.w.h.w"] + fn __crc_w_h_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crc.w.w.w"] + fn __crc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.crc.w.d.w"] fn __crc_w_d_w(a: i64, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.b.w"] + fn __crcc_w_b_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.h.w"] + fn __crcc_w_h_w(a: i32, b: i32) -> i32; + #[link_name = "llvm.loongarch.crcc.w.w.w"] + fn __crcc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.crcc.w.d.w"] fn __crcc_w_d_w(a: i64, b: i32) -> i32; #[link_name = "llvm.loongarch.cacop.d"] @@ -48,6 +60,27 @@ unsafe extern "unadjusted" { fn __ldpte(a: i64, b: i64); } +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_b_w(a: i32, b: i32) -> i32 { + unsafe { __crc_w_b_w(a, b) } +} + +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_h_w(a: i32, b: i32) -> i32 { + unsafe { __crc_w_h_w(a, b) } +} + +/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crc_w_w_w(a: i32, b: i32) -> i32 { + unsafe { __crc_w_w_w(a, b) } +} + /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] @@ -55,6 +88,27 @@ pub fn crc_w_d_w(a: i64, b: i32) -> i32 { unsafe { __crc_w_d_w(a, b) } } +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_b_w(a: i32, b: i32) -> i32 { + unsafe { __crcc_w_b_w(a, b) } +} + +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_h_w(a: i32, b: i32) -> i32 { + unsafe { __crcc_w_h_w(a, b) } +} + +/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) +#[inline(always)] +#[unstable(feature = "stdarch_loongarch", issue = "117427")] +pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { + unsafe { __crcc_w_w_w(a, b) } +} + /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] diff --git a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs index 948c98df61971..4f24ff2932210 100644 --- a/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch_shared/mod.rs @@ -22,18 +22,6 @@ pub fn rdtimeh_w() -> (i32, isize) { #[allow(improper_ctypes)] unsafe extern "unadjusted" { - #[link_name = "llvm.loongarch.crc.w.b.w"] - fn __crc_w_b_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crc.w.h.w"] - fn __crc_w_h_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crc.w.w.w"] - fn __crc_w_w_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.b.w"] - fn __crcc_w_b_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.h.w"] - fn __crcc_w_h_w(a: i32, b: i32) -> i32; - #[link_name = "llvm.loongarch.crcc.w.w.w"] - fn __crcc_w_w_w(a: i32, b: i32) -> i32; #[link_name = "llvm.loongarch.dbar"] fn __dbar(a: i32); #[link_name = "llvm.loongarch.ibar"] @@ -70,48 +58,6 @@ unsafe extern "unadjusted" { fn __frsqrte_d(a: f64) -> f64; } -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_b_w(a, b) } -} - -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_h_w(a, b) } -} - -/// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_w_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_w_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a, b) } -} - -/// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) -#[inline(always)] -#[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_w_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_w_w(a, b) } -} - /// Generates the memory barrier instruction #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] From 606d022b9970e04f252733728d5813592aafc8ce Mon Sep 17 00:00:00 2001 From: Aelin Reidel Date: Sun, 24 May 2026 23:50:52 +0200 Subject: [PATCH 162/174] loongarch: Align CRC intrinsic signatures with clang and GCC Clang and GCC use char and short for the data parameter type where appropriate. The LLVM intrinsics take an i32. We should match clang and perform a cast here. --- .../crates/core_arch/src/loongarch64/mod.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs index 11b882b4280f9..e8bf098a33327 100644 --- a/library/stdarch/crates/core_arch/src/loongarch64/mod.rs +++ b/library/stdarch/crates/core_arch/src/loongarch64/mod.rs @@ -63,15 +63,15 @@ unsafe extern "unadjusted" { /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_b_w(a, b) } +pub fn crc_w_b_w(a: i8, b: i32) -> i32 { + unsafe { __crc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crc_w_h_w(a, b) } +pub fn crc_w_h_w(a: i16, b: i32) -> i32 { + unsafe { __crc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the IEEE 802.3 polynomial (0xEDB88320) @@ -91,15 +91,15 @@ pub fn crc_w_d_w(a: i64, b: i32) -> i32 { /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_b_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_b_w(a, b) } +pub fn crcc_w_b_w(a: i8, b: i32) -> i32 { + unsafe { __crcc_w_b_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) #[inline(always)] #[unstable(feature = "stdarch_loongarch", issue = "117427")] -pub fn crcc_w_h_w(a: i32, b: i32) -> i32 { - unsafe { __crcc_w_h_w(a, b) } +pub fn crcc_w_h_w(a: i16, b: i32) -> i32 { + unsafe { __crcc_w_h_w(a as i32, b) } } /// Calculate the CRC value using the Castagnoli polynomial (0x82F63B78) From 68695a6e06d2a1b1e246b076d57e46159c622a12 Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Mon, 25 May 2026 08:40:34 +0300 Subject: [PATCH 163/174] Sort unexpected and unimportant errors --- src/tools/compiletest/src/runtest.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index a2bf6c0839df4..72817ad64521a 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -745,6 +745,11 @@ impl<'test> TestCx<'test> { } } + unexpected.sort_by_key(|e| (e.line_num, e.column_num)); + unimportant.sort_by_key(|e| (e.line_num, e.column_num)); + + // `not_found` are sorted because `expected_errors` are sorted as they are read from file + // line by line. let mut not_found = Vec::new(); // anything not yet found is a problem for (index, expected_error) in expected_errors.iter().enumerate() { From bf7a021d0e1e0758b8f8a5535a3efca47d46de42 Mon Sep 17 00:00:00 2001 From: Walnut <39544927+Walnut356@users.noreply.github.com> Date: Mon, 25 May 2026 01:32:05 -0500 Subject: [PATCH 164/174] fix breakpoint callback registration --- src/etc/lldb_batchmode.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/etc/lldb_batchmode.py b/src/etc/lldb_batchmode.py index 8728ddec8e6a6..54e796bb2bb79 100644 --- a/src/etc/lldb_batchmode.py +++ b/src/etc/lldb_batchmode.py @@ -97,10 +97,9 @@ def execute_command(command_interpreter, command): print_debug( "registering breakpoint callback, id = " + str(breakpoint_id) ) - callback_command = ( - "breakpoint command add -F breakpoint_callback " - + str(breakpoint_id) - ) + callback_command = f"breakpoint command add -s python {str(breakpoint_id)} -o \ + 'import lldb_batchmode; lldb_batchmode.breakpoint_callback'" + command_interpreter.HandleCommand(callback_command, res) if res.Succeeded(): print_debug( From 028b8b6c54508aa0a25bcab9d16350b670419d85 Mon Sep 17 00:00:00 2001 From: Zalathar Date: Mon, 25 May 2026 16:57:53 +1000 Subject: [PATCH 165/174] Simplify `//@ needs-asm-mnemonic: ret` to just `//@ needs-asm-ret` The `needs-asm-mnemonic` directive was very general, but in practice was only being used for `ret`. There are very few other mnemonics that it could plausibly be useful for (e.g. `nop`), because any instruction that requires arguments is probably going to be non-portable. This PR replaces `needs-asm-mnemonic` with a simpler `needs-asm-ret` directive that uses the same machinery as other simple needs directives. If we happend to need more mnemonics in the future, we can just add more simple directives as appropriate (e.g. `needs-asm-nop`). --- .../rustc-dev-guide/src/tests/compiletest.md | 11 ---- .../rustc-dev-guide/src/tests/directives.md | 7 +-- .../src/directives/directive_names.rs | 2 +- src/tools/compiletest/src/directives/needs.rs | 50 ++++--------------- src/tools/compiletest/src/directives/tests.rs | 26 ++++------ tests/codegen-llvm/cffi/c-variadic-naked.rs | 2 +- tests/codegen-llvm/naked-fn/aligned.rs | 2 +- .../naked-fn/min-function-alignment.rs | 2 +- .../naked-dead-code-elimination/rmake.rs | 2 +- 9 files changed, 27 insertions(+), 77 deletions(-) diff --git a/src/doc/rustc-dev-guide/src/tests/compiletest.md b/src/doc/rustc-dev-guide/src/tests/compiletest.md index 7ffc9e3018471..36dc71a3ac0cc 100644 --- a/src/doc/rustc-dev-guide/src/tests/compiletest.md +++ b/src/doc/rustc-dev-guide/src/tests/compiletest.md @@ -346,17 +346,6 @@ See also the [codegen tests](#codegen-tests) for a similar set of tests. If you need to work with `#![no_std]` cross-compiling tests, consult the [`minicore` test auxiliary](./minicore.md) chapter. -#### Conditional assembly tests based on instruction support - -Tests that depend on specific assembly instructions being available can use the -`//@ needs-asm-mnemonic: ` directive. -This will skip the test if the target backend does not support the specified instruction mnemonic. - -For example, a test that requires the `RET` instruction: -```rust,ignore -//@ needs-asm-mnemonic: RET -``` - [`tests/assembly-llvm`]: https://github.com/rust-lang/rust/tree/HEAD/tests/assembly-llvm diff --git a/src/doc/rustc-dev-guide/src/tests/directives.md b/src/doc/rustc-dev-guide/src/tests/directives.md index d1b923edd3957..5dce2ad13a5fc 100644 --- a/src/doc/rustc-dev-guide/src/tests/directives.md +++ b/src/doc/rustc-dev-guide/src/tests/directives.md @@ -164,9 +164,10 @@ The following directives will check rustc build settings and target settings: For tests that cross-compile to explicit targets via `--target`, use `needs-llvm-components` instead to ensure the appropriate backend is available. -- `needs-asm-mnemonic: ` — ignores if the target backend does not - support the specified assembly mnemonic (e.g., `RET`, `NOP`). - Only supported with the LLVM backend. +- `needs-asm-ret` - ignores if the target does not have a `ret` instruction + in its assembly syntax. Most target architectures have this instruction, + making it handy for portable inline-assembly tests, but some architectures + (e.g. 32-bit ARM) do not have it. - `needs-profiler-runtime` — ignores the test if the profiler runtime was not enabled for the target (`build.profiler = true` in `bootstrap.toml`) - `needs-sanitizer-support` — ignores if the sanitizer support was not enabled diff --git a/src/tools/compiletest/src/directives/directive_names.rs b/src/tools/compiletest/src/directives/directive_names.rs index fb795c6b447c9..41ea492d6f38f 100644 --- a/src/tools/compiletest/src/directives/directive_names.rs +++ b/src/tools/compiletest/src/directives/directive_names.rs @@ -159,7 +159,7 @@ pub(crate) const KNOWN_DIRECTIVE_NAMES: &[&str] = &[ "min-llvm-version", "min-system-llvm-version", "minicore-compile-flags", - "needs-asm-mnemonic", + "needs-asm-ret", "needs-asm-support", "needs-backends", "needs-crate-type", diff --git a/src/tools/compiletest/src/directives/needs.rs b/src/tools/compiletest/src/directives/needs.rs index 64c488aaa7935..ce67b11fba211 100644 --- a/src/tools/compiletest/src/directives/needs.rs +++ b/src/tools/compiletest/src/directives/needs.rs @@ -101,37 +101,6 @@ pub(super) fn handle_needs( } } - if name == "needs-asm-mnemonic" { - let Some(rest) = ln.value_after_colon() else { - return IgnoreDecision::Error { - message: "expected `needs-asm-mnemonic` to have a mnemonic name after colon" - .to_string(), - }; - }; - - if !config.default_codegen_backend.is_llvm() { - return IgnoreDecision::Ignore { - reason: "skipping test as non-LLVM backend does not support mnemonic queries" - .to_string(), - }; - } - - let mnemonic = rest.trim(); - let has_mnemonic = match mnemonic { - "ret" => conditions.has_ret_mnemonic, - "nop" => conditions.has_nop_mnemonic, - _ => has_mnemonic(config, mnemonic), - }; - - if has_mnemonic { - return IgnoreDecision::Continue; - } else { - return IgnoreDecision::Ignore { - reason: format!("skipping test as target does not have `{mnemonic}` mnemonic"), - }; - } - } - // Handled elsewhere. if name == "needs-llvm-components" || name == "needs-backends" { return IgnoreDecision::Continue; @@ -163,11 +132,6 @@ struct Need { pub(crate) struct PreparedNeedsConditions { /// The `//@ needs-*` conditions that can be treated as a simple name->boolean mapping. simple_needs: HashMap<&'static str, Need>, - - /// Might add particular other mnemonics heavily needed by tests here. - /// Otherwise call into llvm for every check - has_ret_mnemonic: bool, - has_nop_mnemonic: bool, } pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditions { @@ -177,6 +141,14 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio // Note that we intentionally still put the needs- prefix here to make the file show up when // grepping for a directive name, even though we could technically strip that. let simple_needs = vec![ + // This used to be a more general `//@ needs-asm-mnemonic: ret` directive, + // but was simplified to just `//@ needs-asm-ret` because there are very + // few other mnemonics (`nop`?) that it could ever be useful with. + Need { + name: "needs-asm-ret", + condition: has_mnemonic(config, "ret"), + ignore_reason: "ignored on targets without a `ret` assembly instruction", + }, Need { name: "needs-asm-support", condition: config.has_asm_support(), @@ -398,11 +370,7 @@ pub(crate) fn prepare_needs_conditions(config: &Config) -> PreparedNeedsConditio }) .collect::>(); - PreparedNeedsConditions { - simple_needs, - has_ret_mnemonic: has_mnemonic(config, "ret"), - has_nop_mnemonic: has_mnemonic(config, "nop"), - } + PreparedNeedsConditions { simple_needs } } fn find_dlltool(config: &Config) -> bool { diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index bc016cfb1cb43..65d7b5360ae0d 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -1270,23 +1270,15 @@ fn test_edition_range_edition_to_test() { } #[test] -fn needs_asm_mnemonic() { +fn needs_asm_ret() { let config_x86_64 = cfg().target("x86_64-unknown-linux-gnu").build(); let config_aarch64 = cfg().target("aarch64-unknown-linux-gnu").build(); - - // invalid mnemonic - assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:GRUGGY")); - assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:gruggy")); - - // valid x86 and aarch64 - assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:RET")); - assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:ret")); - - // this is aarch64 specific - assert!(check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:ldrsbwui")); - assert!(!check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:LDRSBWui")); - - // this is x86 specific - assert!(check_ignore(&config_aarch64, "//@ needs-asm-mnemonic:CMPxCHG16B")); - assert!(!check_ignore(&config_x86_64, "//@ needs-asm-mnemonic:CMPXchg16B")); + // 32-bit ARM does not have a "ret" mnemonic. + let config_arm32 = cfg().target("armv7a-none-eabi").build(); + let config_wasm = cfg().target("wasm32v1-none").build(); + + assert!(!check_ignore(&config_x86_64, "//@ needs-asm-ret")); + assert!(!check_ignore(&config_aarch64, "//@ needs-asm-ret")); + assert!(check_ignore(&config_arm32, "//@ needs-asm-ret")); + assert!(check_ignore(&config_wasm, "//@ needs-asm-ret")); } diff --git a/tests/codegen-llvm/cffi/c-variadic-naked.rs b/tests/codegen-llvm/cffi/c-variadic-naked.rs index caca6d327dd63..a04d3efca9cd9 100644 --- a/tests/codegen-llvm/cffi/c-variadic-naked.rs +++ b/tests/codegen-llvm/cffi/c-variadic-naked.rs @@ -1,5 +1,5 @@ //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret // tests that `va_start` is not injected into naked functions diff --git a/tests/codegen-llvm/naked-fn/aligned.rs b/tests/codegen-llvm/naked-fn/aligned.rs index 8c4ac57a7bf90..77e637701fc95 100644 --- a/tests/codegen-llvm/naked-fn/aligned.rs +++ b/tests/codegen-llvm/naked-fn/aligned.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) #![crate_type = "lib"] diff --git a/tests/codegen-llvm/naked-fn/min-function-alignment.rs b/tests/codegen-llvm/naked-fn/min-function-alignment.rs index 2619f4ef476a7..059eed06b66bb 100644 --- a/tests/codegen-llvm/naked-fn/min-function-alignment.rs +++ b/tests/codegen-llvm/naked-fn/min-function-alignment.rs @@ -1,6 +1,6 @@ //@ compile-flags: -C no-prepopulate-passes -Copt-level=0 -Zmin-function-alignment=16 //@ needs-asm-support -//@ needs-asm-mnemonic: ret +//@ needs-asm-ret //@ ignore-wasm32 aligning functions is not currently supported on wasm (#143368) // FIXME(#82232, #143834): temporarily renamed to mitigate `#[align]` nameres ambiguity diff --git a/tests/run-make/naked-dead-code-elimination/rmake.rs b/tests/run-make/naked-dead-code-elimination/rmake.rs index 8e4c26fc34508..c726f7ce04da7 100644 --- a/tests/run-make/naked-dead-code-elimination/rmake.rs +++ b/tests/run-make/naked-dead-code-elimination/rmake.rs @@ -1,6 +1,6 @@ //@ ignore-cross-compile //@ needs-asm-support -//@ needs-asm-mnemonic: RET +//@ needs-asm-ret use run_make_support::symbols::object_contains_any_symbol; use run_make_support::{bin_name, rustc}; From 4539a7571630137ac39f474f964b670bfe720722 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sat, 23 May 2026 20:10:55 +0200 Subject: [PATCH 166/174] convert fuzzy_provenance_casts to late lint --- compiler/rustc_hir_typeck/src/cast.rs | 23 +---- compiler/rustc_hir_typeck/src/errors.rs | 24 ----- .../rustc_lint/src/fuzzy_provenance_casts.rs | 79 ++++++++++++++++ compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 24 +++++ compiler/rustc_lint_defs/src/builtin.rs | 45 --------- ...-fuzzy-provenance-casts-with-inner-attr.rs | 24 ----- ...zy-provenance-casts-with-inner-attr.stderr | 93 ------------------- 8 files changed, 107 insertions(+), 208 deletions(-) create mode 100644 compiler/rustc_lint/src/fuzzy_provenance_casts.rs delete mode 100644 tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs delete mode 100644 tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 12429218a6fc1..94dac64c5071b 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -869,10 +869,7 @@ impl<'a, 'tcx> CastCheck<'tcx> { Ok(CastKind::FnPtrAddrCast) } // addr-ptr-cast - (Int(_), Ptr(mt)) => { - self.fuzzy_provenance_int2ptr_lint(fcx); - self.check_addr_ptr_cast(fcx, mt) - } + (Int(_), Ptr(mt)) => self.check_addr_ptr_cast(fcx, mt), // fn-ptr-cast (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), @@ -1168,24 +1165,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { ); } - fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let sugg = self.span.can_be_used_for_suggestions().then(|| { - errors::LossyProvenanceInt2PtrSuggestion { - lo: self.expr_span.shrink_to_lo(), - hi: self.expr_span.shrink_to_hi().to(self.cast_span), - } - }); - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - let lint = errors::LossyProvenanceInt2Ptr { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::FUZZY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index fcbb8a75f9567..a5c76f1e7d0f9 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -377,18 +377,6 @@ impl Subdiagnostic for TypeMismatchFruTypo { } } -#[derive(Diagnostic)] -#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] -#[help( - "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" -)] -pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: Option, -} - #[derive(Diagnostic)] #[diag("cannot add {$traits_len -> [1] auto trait {$traits} @@ -404,18 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject { pub traits: DiagSymbolList, } -#[derive(Subdiagnostic)] -#[multipart_suggestion( - "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", - applicability = "has-placeholders" -)] -pub(crate) struct LossyProvenanceInt2PtrSuggestion { - #[suggestion_part(code = "(...).with_addr(")] - pub lo: Span, - #[suggestion_part(code = ")")] - pub hi: Span, -} - #[derive(Diagnostic)] #[diag( "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" diff --git a/compiler/rustc_lint/src/fuzzy_provenance_casts.rs b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs new file mode 100644 index 0000000000000..699004a3b37e0 --- /dev/null +++ b/compiler/rustc_lint/src/fuzzy_provenance_casts.rs @@ -0,0 +1,79 @@ +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenanceInt2Ptr, LossyProvenanceInt2PtrSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer + /// and a pointer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(fuzzy_provenance_casts)] + /// + /// fn main() { + /// let _dangling = 16_usize as *const u8; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting an integer to a pointer is considered bad style, as a pointer + /// contains, besides the *address* also a *provenance*, indicating what + /// memory the pointer is allowed to read/write. Casting an integer, which + /// doesn't have provenance, to a pointer requires the compiler to assign + /// (guess) provenance. The compiler assigns "all exposed valid" (see the + /// docs of [`ptr::with_exposed_provenance`] for more information about this + /// "exposing"). This penalizes the optimiser and is not well suited for + /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI + /// platforms). + /// + /// It is much better to use [`ptr::with_addr`] instead to specify the + /// provenance you want. If using this function is not possible because the + /// code relies on exposed provenance then there is as an escape hatch + /// [`ptr::with_exposed_provenance`]. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr + /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html + pub FUZZY_PROVENANCE_CASTS, + Allow, + "a fuzzy integer to pointer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between an integer and a pointer. + FuzzyProvenanceCasts => [FUZZY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for FuzzyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from integer to pointer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_integral() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_raw_ptr() { + return; + } + + let sugg = + expr.span.can_be_used_for_suggestions().then(|| LossyProvenanceInt2PtrSuggestion { + lo: cast_from_expr.span.shrink_to_lo(), + hi: cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span), + }); + let lint = LossyProvenanceInt2Ptr { expr_ty: cast_from_ty, cast_ty: cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(FUZZY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint) + } +} diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 2ae338d34b1f3..e5cbc8ad00c1b 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -45,6 +45,7 @@ mod expect; mod for_loops_over_fallibles; mod foreign_modules; mod function_cast_as_integer; +mod fuzzy_provenance_casts; mod gpukernel_abi; mod if_let_rescope; mod impl_trait_overcaptures; @@ -92,6 +93,7 @@ use drop_forget_useless::*; use enum_intrinsics_non_enums::EnumIntrinsicsNonEnums; use for_loops_over_fallibles::*; use function_cast_as_integer::*; +use fuzzy_provenance_casts::FuzzyProvenanceCasts; use gpukernel_abi::*; use if_let_rescope::IfLetRescope; use impl_trait_overcaptures::ImplTraitOvercaptures; @@ -250,6 +252,7 @@ late_lint_methods!( CheckTransmutes: CheckTransmutes, LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, + FuzzyProvenanceCasts: FuzzyProvenanceCasts, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index 091b7ac228b54..d64d19c97a8c7 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2932,3 +2932,27 @@ impl Subdiagnostic for MismatchedLifetimeSyntaxesSuggestion { #[diag("`Eq::assert_receiver_is_total_eq` should never be implemented by hand")] #[note("this method was used to add checks to the `Eq` derive macro")] pub(crate) struct EqInternalMethodImplemented; + +#[derive(Diagnostic)] +#[diag("strict provenance disallows casting integer `{$expr_ty}` to pointer `{$cast_ty}`")] +#[help( + "if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead" +)] +pub(crate) struct LossyProvenanceInt2Ptr<'tcx> { + pub expr_ty: Ty<'tcx>, + pub cast_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option, +} + +#[derive(Subdiagnostic)] +#[multipart_suggestion( + "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address", + applicability = "has-placeholders" +)] +pub(crate) struct LossyProvenanceInt2PtrSuggestion { + #[suggestion_part(code = "(...).with_addr(")] + pub lo: Span, + #[suggestion_part(code = ")")] + pub hi: Span, +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 46069a5868a91..92e67c5242ced 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -51,7 +51,6 @@ declare_lint_pass! { FLOAT_LITERAL_F32_FALLBACK, FORBIDDEN_LINT_GROUPS, FUNCTION_ITEM_REFERENCES, - FUZZY_PROVENANCE_CASTS, HIDDEN_GLOB_REEXPORTS, ILL_FORMED_ATTRIBUTE_INPUT, INCOMPLETE_INCLUDE, @@ -2593,50 +2592,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `fuzzy_provenance_casts` lint detects an `as` cast between an integer - /// and a pointer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(fuzzy_provenance_casts)] - /// - /// fn main() { - /// let _dangling = 16_usize as *const u8; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting an integer to a pointer is considered bad style, as a pointer - /// contains, besides the *address* also a *provenance*, indicating what - /// memory the pointer is allowed to read/write. Casting an integer, which - /// doesn't have provenance, to a pointer requires the compiler to assign - /// (guess) provenance. The compiler assigns "all exposed valid" (see the - /// docs of [`ptr::with_exposed_provenance`] for more information about this - /// "exposing"). This penalizes the optimiser and is not well suited for - /// dynamic analysis/dynamic program verification (e.g. Miri or CHERI - /// platforms). - /// - /// It is much better to use [`ptr::with_addr`] instead to specify the - /// provenance you want. If using this function is not possible because the - /// code relies on exposed provenance then there is as an escape hatch - /// [`ptr::with_exposed_provenance`]. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::with_addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.with_addr - /// [`ptr::with_exposed_provenance`]: https://doc.rust-lang.org/core/ptr/fn.with_exposed_provenance.html - pub FUZZY_PROVENANCE_CASTS, - Allow, - "a fuzzy integer to pointer cast is used", - @feature_gate = strict_provenance_lints; -} - declare_lint! { /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer /// and an integer. diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs deleted file mode 100644 index b8deb6ab3c64a..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Regression test for #137588. -// The compiler used to ICE when emitting a `fuzzy_provenance_casts` lint -// diagnostic for code with an inner attribute spanning the entire file, -// causing `draw_code_line` to panic on an empty `file_lines` from a dummy span. - -//@ edition:2024 -//@ compile-flags: -Wfuzzy-provenance-casts - -#![feature(strict_provenance_lints)] -//~^ ERROR too many leading `super` keywords [E0433] -//~| ERROR cannot find type `Ts` in this scope [E0425] -//~| ERROR `#[prelude_import]` is for use by rustc only [E0658] -//~| WARN strict provenance disallows casting integer `usize` to pointer `*const u32` -#![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -//~^ ERROR use of unstable library feature `contracts` [E0658] -//~| ERROR inner macro attributes are unstable [E0658] -//~| ERROR cannot find type `Stars` in this scope [E0433] - -pub(super) fn foo() -> *const Ts { - unsafe { - let p2 = 0x52 as *const u32; - } -} -//~^ ERROR `main` function not found in crate diff --git a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr b/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr deleted file mode 100644 index 342f39d2450d8..0000000000000 --- a/tests/ui/lint/ice-fuzzy-provenance-casts-with-inner-attr.stderr +++ /dev/null @@ -1,93 +0,0 @@ -error[E0658]: use of unstable library feature `contracts` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #128044 for more information - = help: add `#![feature(contracts)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0658]: inner macro attributes are unstable - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:4 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^^^^^^^^^^^^^^^^^^^^ - | - = note: see issue #54726 for more information - = help: add `#![feature(custom_inner_attributes)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0433]: too many leading `super` keywords - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ there are too many leading `super` keywords - -error[E0425]: cannot find type `Ts` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ not found in this scope - -error[E0658]: `#[prelude_import]` is for use by rustc only - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: add `#![feature(prelude_import)]` to the crate attributes to enable - = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date - -error[E0601]: `main` function not found in crate `ice_fuzzy_provenance_casts_with_inner_attr` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:23:2 - | -LL | } - | ^ consider adding a `main` function to `$DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs` - -error[E0433]: cannot find type `Stars` in this scope - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:14:50 - | -LL | #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] - | ^^^^^ use of undeclared type `Stars` - -warning: strict provenance disallows casting integer `usize` to pointer `*const u32` - --> $DIR/ice-fuzzy-provenance-casts-with-inner-attr.rs:9:1 - | -LL | / #![feature(strict_provenance_lints)] -... | -LL | | } - | |_^ - | - = help: if you can't comply with strict provenance and don't have a pointer with the correct provenance you can use `std::ptr::with_exposed_provenance()` instead - = note: requested on the command line with `-W fuzzy-provenance-casts` -help: use `.with_addr()` to adjust a valid pointer in the same allocation, to this address - | -LL - #![feature(strict_provenance_lints)] -LL - -LL - -LL - -LL - -LL - #![core::contracts::ensures(|ret| ret.is_none_or(Stars::is_valid))] -LL - -LL - -LL - -LL - -LL - pub(super) fn foo() -> *const Ts { -LL - unsafe { -LL - let p2 = 0x52 as *const u32; -LL - } -LL - } -LL + (...).with_addr() - | - -error: aborting due to 7 previous errors; 1 warning emitted - -Some errors have detailed explanations: E0425, E0433, E0601, E0658. -For more information about an error, try `rustc --explain E0425`. From f8f740baf0f04da9bd805732e796df0bebd9d454 Mon Sep 17 00:00:00 2001 From: Hanna Kruppe Date: Sat, 23 May 2026 21:23:51 +0200 Subject: [PATCH 167/174] convert lossy_provenance_casts to late lint --- compiler/rustc_hir_typeck/src/cast.rs | 42 +------- compiler/rustc_hir_typeck/src/errors.rs | 58 ----------- compiler/rustc_lint/src/lib.rs | 3 + compiler/rustc_lint/src/lints.rs | 58 +++++++++++ .../rustc_lint/src/lossy_provenance_casts.rs | 98 +++++++++++++++++++ compiler/rustc_lint_defs/src/builtin.rs | 47 --------- 6 files changed, 161 insertions(+), 145 deletions(-) create mode 100644 compiler/rustc_lint/src/lossy_provenance_casts.rs diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs index 94dac64c5071b..8902505c11762 100644 --- a/compiler/rustc_hir_typeck/src/cast.rs +++ b/compiler/rustc_hir_typeck/src/cast.rs @@ -28,7 +28,6 @@ //! expression, `e as U2` is not necessarily so (in fact it will only be valid if //! `U1` coerces to `U2`). -use rustc_ast::util::parser::ExprPrecedence; use rustc_data_structures::fx::FxHashSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed}; @@ -860,10 +859,8 @@ impl<'a, 'tcx> CastCheck<'tcx> { (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast // ptr-addr-cast - (Ptr(m_expr), Int(t_c)) => { - self.lossy_provenance_ptr2int_lint(fcx, t_c); - self.check_ptr_addr_cast(fcx, m_expr) - } + (Ptr(m_expr), Int(_)) => self.check_ptr_addr_cast(fcx, m_expr), + (FnPtr, Int(_)) => { // FIXME(#95489): there should eventually be a lint for these casts Ok(CastKind::FnPtrAddrCast) @@ -1130,41 +1127,6 @@ impl<'a, 'tcx> CastCheck<'tcx> { } } - fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let cast_ty = fcx.resolve_vars_if_possible(self.cast_ty); - - let sugg = self.span.can_be_used_for_suggestions().then(|| { - let expr_prec = fcx.precedence(self.expr); - let needs_parens = expr_prec < ExprPrecedence::Unambiguous; - let needs_cast = !matches!(t_c, ty::cast::IntTy::U(ty::UintTy::Usize)); - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); - let expr_span = self.expr_span.shrink_to_lo(); - match (needs_parens, needs_cast) { - (true, true) => errors::LossyProvenancePtr2IntSuggestion::NeedsParensCast { - expr_span, - cast_span, - cast_ty, - }, - (true, false) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } - } - (false, true) => { - errors::LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_ty } - } - (false, false) => errors::LossyProvenancePtr2IntSuggestion::Other { cast_span }, - } - }); - - let lint = errors::LossyProvenancePtr2Int { expr_ty, cast_ty, sugg }; - fcx.tcx.emit_node_span_lint( - lint::builtin::LOSSY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - lint, - ); - } - /// Attempt to suggest using `.is_empty` when trying to cast from a /// collection type to a boolean. fn try_suggest_collection_to_bool(&self, fcx: &FnCtxt<'a, 'tcx>, err: &mut Diag<'_>) { diff --git a/compiler/rustc_hir_typeck/src/errors.rs b/compiler/rustc_hir_typeck/src/errors.rs index a5c76f1e7d0f9..3457cc373413a 100644 --- a/compiler/rustc_hir_typeck/src/errors.rs +++ b/compiler/rustc_hir_typeck/src/errors.rs @@ -392,64 +392,6 @@ pub(crate) struct PtrCastAddAutoToObject { pub traits: DiagSymbolList, } -#[derive(Diagnostic)] -#[diag( - "under strict provenance it is considered bad style to cast pointer `{$expr_ty}` to integer `{$cast_ty}`" -)] -#[help( - "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" -)] -pub(crate) struct LossyProvenancePtr2Int<'tcx> { - pub expr_ty: Ty<'tcx>, - pub cast_ty: Ty<'tcx>, - #[subdiagnostic] - pub sugg: Option>, -} - -#[derive(Subdiagnostic)] -pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParensCast { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr() as {cast_ty}")] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[multipart_suggestion( - "use `.addr()` to obtain the address of a pointer", - applicability = "maybe-incorrect" - )] - NeedsParens { - #[suggestion_part(code = "(")] - expr_span: Span, - #[suggestion_part(code = ").addr()")] - cast_span: Span, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr() as {cast_ty}", - applicability = "maybe-incorrect" - )] - NeedsCast { - #[primary_span] - cast_span: Span, - cast_ty: Ty<'tcx>, - }, - #[suggestion( - "use `.addr()` to obtain the address of a pointer", - code = ".addr()", - applicability = "maybe-incorrect" - )] - Other { - #[primary_span] - cast_span: Span, - }, -} - #[derive(Subdiagnostic)] pub(crate) enum HelpUseLatestEdition { #[help("set `edition = \"{$edition}\"` in `Cargo.toml`")] diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index e5cbc8ad00c1b..1efc8b70ef22d 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -57,6 +57,7 @@ mod let_underscore; mod levels; pub mod lifetime_syntax; mod lints; +mod lossy_provenance_casts; mod macro_expr_fragment_specifier_2024_migration; mod map_unit_fn; mod multiple_supertrait_upcastable; @@ -102,6 +103,7 @@ use internal::*; use invalid_from_utf8::*; use let_underscore::*; use lifetime_syntax::*; +use lossy_provenance_casts::LossyProvenanceCasts; use macro_expr_fragment_specifier_2024_migration::*; use map_unit_fn::*; use multiple_supertrait_upcastable::*; @@ -253,6 +255,7 @@ late_lint_methods!( LifetimeSyntax: LifetimeSyntax, InternalEqTraitMethodImpls: InternalEqTraitMethodImpls, FuzzyProvenanceCasts: FuzzyProvenanceCasts, + LossyProvenanceCasts: LossyProvenanceCasts, ] ] ); diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs index d64d19c97a8c7..c77e70dcbe9bb 100644 --- a/compiler/rustc_lint/src/lints.rs +++ b/compiler/rustc_lint/src/lints.rs @@ -2956,3 +2956,61 @@ pub(crate) struct LossyProvenanceInt2PtrSuggestion { #[suggestion_part(code = ")")] pub hi: Span, } + +#[derive(Diagnostic)] +#[diag( + "under strict provenance it is considered bad style to cast pointer `{$cast_from_ty}` to integer `{$cast_to_ty}`" +)] +#[help( + "if you can't comply with strict provenance and need to expose the pointer provenance you can use `.expose_provenance()` instead" +)] +pub(crate) struct LossyProvenancePtr2Int<'tcx> { + pub cast_from_ty: Ty<'tcx>, + pub cast_to_ty: Ty<'tcx>, + #[subdiagnostic] + pub sugg: Option>, +} + +#[derive(Subdiagnostic)] +pub(crate) enum LossyProvenancePtr2IntSuggestion<'tcx> { + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParensCast { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr() as {cast_to_ty}")] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[multipart_suggestion( + "use `.addr()` to obtain the address of a pointer", + applicability = "maybe-incorrect" + )] + NeedsParens { + #[suggestion_part(code = "(")] + expr_span: Span, + #[suggestion_part(code = ").addr()")] + cast_span: Span, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr() as {cast_to_ty}", + applicability = "maybe-incorrect" + )] + NeedsCast { + #[primary_span] + cast_span: Span, + cast_to_ty: Ty<'tcx>, + }, + #[suggestion( + "use `.addr()` to obtain the address of a pointer", + code = ".addr()", + applicability = "maybe-incorrect" + )] + Other { + #[primary_span] + cast_span: Span, + }, +} diff --git a/compiler/rustc_lint/src/lossy_provenance_casts.rs b/compiler/rustc_lint/src/lossy_provenance_casts.rs new file mode 100644 index 0000000000000..bdb5bdf6bb0e3 --- /dev/null +++ b/compiler/rustc_lint/src/lossy_provenance_casts.rs @@ -0,0 +1,98 @@ +use rustc_ast::util::parser::ExprPrecedence; +use rustc_hir as hir; +use rustc_session::{declare_lint, declare_lint_pass}; + +use crate::lints::{LossyProvenancePtr2Int, LossyProvenancePtr2IntSuggestion}; +use crate::{LateContext, LateLintPass}; + +declare_lint! { + /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer + /// and an integer. + /// + /// ### Example + /// + /// ```rust + /// #![feature(strict_provenance_lints)] + /// #![warn(lossy_provenance_casts)] + /// + /// fn main() { + /// let x: u8 = 37; + /// let _addr: usize = &x as *const u8 as usize; + /// } + /// ``` + /// + /// {{produces}} + /// + /// ### Explanation + /// + /// This lint is part of the strict provenance effort, see [issue #95228]. + /// Casting a pointer to an integer is a lossy operation, because beyond + /// just an *address* a pointer may be associated with a particular + /// *provenance*. This information is used by the optimiser and for dynamic + /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). + /// + /// Since this cast is lossy, it is considered good style to use the + /// [`ptr::addr`] method instead, which has a similar effect, but doesn't + /// "expose" the pointer provenance. This improves optimisation potential. + /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information + /// about exposing pointer provenance. + /// + /// If your code can't comply with strict provenance and needs to expose + /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, + /// which preserves the behaviour of `as usize` casts while being explicit + /// about the semantics. + /// + /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 + /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr + /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance + pub LOSSY_PROVENANCE_CASTS, + Allow, + "a lossy pointer to integer cast is used", + @feature_gate = strict_provenance_lints; +} + +declare_lint_pass!( + /// Lint for `as` casts between a pointer and an integer. + LossyProvenanceCasts => [LOSSY_PROVENANCE_CASTS] +); + +impl<'tcx> LateLintPass<'tcx> for LossyProvenanceCasts { + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>) { + let hir::ExprKind::Cast(cast_from_expr, cast_to_hir) = expr.kind else { return }; + + let typeck_results = cx.typeck_results(); + // Only lint casts from pointer to integer + let cast_from_ty = typeck_results.expr_ty(cast_from_expr); + if !cast_from_ty.is_raw_ptr() { + return; + } + let cast_to_ty = typeck_results.expr_ty(expr); + if !cast_to_ty.is_integral() { + return; + } + + let sugg = expr.span.can_be_used_for_suggestions().then(|| { + let needs_parens = cx.precedence(cast_from_expr) < ExprPrecedence::Unambiguous; + let needs_cast = !cast_to_ty.is_usize(); + let cast_span = cast_from_expr.span.shrink_to_hi().to(cast_to_hir.span); + let expr_span = cast_from_expr.span.shrink_to_lo(); + match (needs_parens, needs_cast) { + (true, true) => LossyProvenancePtr2IntSuggestion::NeedsParensCast { + expr_span, + cast_span, + cast_to_ty, + }, + (true, false) => { + LossyProvenancePtr2IntSuggestion::NeedsParens { expr_span, cast_span } + } + (false, true) => { + LossyProvenancePtr2IntSuggestion::NeedsCast { cast_span, cast_to_ty } + } + (false, false) => LossyProvenancePtr2IntSuggestion::Other { cast_span }, + } + }); + + let lint = LossyProvenancePtr2Int { cast_from_ty, cast_to_ty, sugg }; + cx.tcx.emit_node_span_lint(LOSSY_PROVENANCE_CASTS, expr.hir_id, expr.span, lint); + } +} diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 92e67c5242ced..caa41fd0f6ab3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -66,7 +66,6 @@ declare_lint_pass! { LINKER_INFO, LINKER_MESSAGES, LONG_RUNNING_CONST_EVAL, - LOSSY_PROVENANCE_CASTS, MACRO_EXPANDED_MACRO_EXPORTS_ACCESSED_BY_ABSOLUTE_PATHS, MACRO_USE_EXTERN_CRATE, MALFORMED_DIAGNOSTIC_ATTRIBUTES, @@ -2592,52 +2591,6 @@ declare_lint! { @edition Edition2024 => Warn; } -declare_lint! { - /// The `lossy_provenance_casts` lint detects an `as` cast between a pointer - /// and an integer. - /// - /// ### Example - /// - /// ```rust - /// #![feature(strict_provenance_lints)] - /// #![warn(lossy_provenance_casts)] - /// - /// fn main() { - /// let x: u8 = 37; - /// let _addr: usize = &x as *const u8 as usize; - /// } - /// ``` - /// - /// {{produces}} - /// - /// ### Explanation - /// - /// This lint is part of the strict provenance effort, see [issue #95228]. - /// Casting a pointer to an integer is a lossy operation, because beyond - /// just an *address* a pointer may be associated with a particular - /// *provenance*. This information is used by the optimiser and for dynamic - /// analysis/dynamic program verification (e.g. Miri or CHERI platforms). - /// - /// Since this cast is lossy, it is considered good style to use the - /// [`ptr::addr`] method instead, which has a similar effect, but doesn't - /// "expose" the pointer provenance. This improves optimisation potential. - /// See the docs of [`ptr::addr`] and [`ptr::expose_provenance`] for more information - /// about exposing pointer provenance. - /// - /// If your code can't comply with strict provenance and needs to expose - /// the provenance, then there is [`ptr::expose_provenance`] as an escape hatch, - /// which preserves the behaviour of `as usize` casts while being explicit - /// about the semantics. - /// - /// [issue #95228]: https://github.com/rust-lang/rust/issues/95228 - /// [`ptr::addr`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.addr - /// [`ptr::expose_provenance`]: https://doc.rust-lang.org/core/primitive.pointer.html#method.expose_provenance - pub LOSSY_PROVENANCE_CASTS, - Allow, - "a lossy pointer to integer cast is used", - @feature_gate = strict_provenance_lints; -} - declare_lint! { /// The `const_evaluatable_unchecked` lint detects a generic constant used /// in a type. From a25ec1a018391e2bf1e204bb5c2921a0b42f52f8 Mon Sep 17 00:00:00 2001 From: zedddie Date: Mon, 25 May 2026 16:16:38 +0200 Subject: [PATCH 168/174] move batch --- .../issue-16922-rpass.rs => coercion/fn-item-to-dyn-any.rs} | 0 .../auxiliary/dyn-trait-lifetime-infer-metadata.rs} | 0 .../dyn-trait-lifetime-infer-metadata.rs} | 0 .../deprecated-unstable-method-in-staged-api.rs} | 0 .../deprecated-unstable-method-in-staged-api.stderr} | 0 .../issue-17216.rs => drop/drop-in-for-loop-destructure.rs} | 0 .../need_type_info/unconstrained-type-in-closure.rs} | 0 .../need_type_info/unconstrained-type-in-closure.stderr} | 0 .../relate-bound-region-in-method-return.rs} | 0 .../tuple-variant-as-struct-variant.rs} | 0 .../tuple-variant-as-struct-variant.stderr} | 0 .../thread-local-borrow-past-function-end.rs} | 0 .../thread-local-borrow-past-function-end.stderr} | 0 .../where-clause-on-struct-definitions.rs} | 0 .../where-clause-on-unit-struct.rs} | 0 .../where-clause-on-unit-struct.stderr} | 0 16 files changed, 0 insertions(+), 0 deletions(-) rename tests/ui/{issues/issue-16922-rpass.rs => coercion/fn-item-to-dyn-any.rs} (100%) rename tests/ui/{issues/auxiliary/issue-17662.rs => cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs} (100%) rename tests/ui/{issues/issue-17662.rs => cross-crate/dyn-trait-lifetime-infer-metadata.rs} (100%) rename tests/ui/{issues/issue-17337.rs => deprecation/deprecated-unstable-method-in-staged-api.rs} (100%) rename tests/ui/{issues/issue-17337.stderr => deprecation/deprecated-unstable-method-in-staged-api.stderr} (100%) rename tests/ui/{issues/issue-17216.rs => drop/drop-in-for-loop-destructure.rs} (100%) rename tests/ui/{issues/issue-17551.rs => inference/need_type_info/unconstrained-type-in-closure.rs} (100%) rename tests/ui/{issues/issue-17551.stderr => inference/need_type_info/unconstrained-type-in-closure.stderr} (100%) rename tests/ui/{issues/issue-17121.rs => regions/relate-bound-region-in-method-return.rs} (100%) rename tests/ui/{issues/issue-17800.rs => structs-enums/tuple-variant-as-struct-variant.rs} (100%) rename tests/ui/{issues/issue-17800.stderr => structs-enums/tuple-variant-as-struct-variant.stderr} (100%) rename tests/ui/{issues/issue-17954.rs => thread-local/thread-local-borrow-past-function-end.rs} (100%) rename tests/ui/{issues/issue-17954.stderr => thread-local/thread-local-borrow-past-function-end.stderr} (100%) rename tests/ui/{issues/issue-17904.rs => where-clauses/where-clause-on-struct-definitions.rs} (100%) rename tests/ui/{issues/issue-17904-2.rs => where-clauses/where-clause-on-unit-struct.rs} (100%) rename tests/ui/{issues/issue-17904-2.stderr => where-clauses/where-clause-on-unit-struct.stderr} (100%) diff --git a/tests/ui/issues/issue-16922-rpass.rs b/tests/ui/coercion/fn-item-to-dyn-any.rs similarity index 100% rename from tests/ui/issues/issue-16922-rpass.rs rename to tests/ui/coercion/fn-item-to-dyn-any.rs diff --git a/tests/ui/issues/auxiliary/issue-17662.rs b/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs similarity index 100% rename from tests/ui/issues/auxiliary/issue-17662.rs rename to tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs diff --git a/tests/ui/issues/issue-17662.rs b/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs similarity index 100% rename from tests/ui/issues/issue-17662.rs rename to tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs diff --git a/tests/ui/issues/issue-17337.rs b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs similarity index 100% rename from tests/ui/issues/issue-17337.rs rename to tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs diff --git a/tests/ui/issues/issue-17337.stderr b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr similarity index 100% rename from tests/ui/issues/issue-17337.stderr rename to tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr diff --git a/tests/ui/issues/issue-17216.rs b/tests/ui/drop/drop-in-for-loop-destructure.rs similarity index 100% rename from tests/ui/issues/issue-17216.rs rename to tests/ui/drop/drop-in-for-loop-destructure.rs diff --git a/tests/ui/issues/issue-17551.rs b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs similarity index 100% rename from tests/ui/issues/issue-17551.rs rename to tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs diff --git a/tests/ui/issues/issue-17551.stderr b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr similarity index 100% rename from tests/ui/issues/issue-17551.stderr rename to tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr diff --git a/tests/ui/issues/issue-17121.rs b/tests/ui/regions/relate-bound-region-in-method-return.rs similarity index 100% rename from tests/ui/issues/issue-17121.rs rename to tests/ui/regions/relate-bound-region-in-method-return.rs diff --git a/tests/ui/issues/issue-17800.rs b/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs similarity index 100% rename from tests/ui/issues/issue-17800.rs rename to tests/ui/structs-enums/tuple-variant-as-struct-variant.rs diff --git a/tests/ui/issues/issue-17800.stderr b/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr similarity index 100% rename from tests/ui/issues/issue-17800.stderr rename to tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr diff --git a/tests/ui/issues/issue-17954.rs b/tests/ui/thread-local/thread-local-borrow-past-function-end.rs similarity index 100% rename from tests/ui/issues/issue-17954.rs rename to tests/ui/thread-local/thread-local-borrow-past-function-end.rs diff --git a/tests/ui/issues/issue-17954.stderr b/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr similarity index 100% rename from tests/ui/issues/issue-17954.stderr rename to tests/ui/thread-local/thread-local-borrow-past-function-end.stderr diff --git a/tests/ui/issues/issue-17904.rs b/tests/ui/where-clauses/where-clause-on-struct-definitions.rs similarity index 100% rename from tests/ui/issues/issue-17904.rs rename to tests/ui/where-clauses/where-clause-on-struct-definitions.rs diff --git a/tests/ui/issues/issue-17904-2.rs b/tests/ui/where-clauses/where-clause-on-unit-struct.rs similarity index 100% rename from tests/ui/issues/issue-17904-2.rs rename to tests/ui/where-clauses/where-clause-on-unit-struct.rs diff --git a/tests/ui/issues/issue-17904-2.stderr b/tests/ui/where-clauses/where-clause-on-unit-struct.stderr similarity index 100% rename from tests/ui/issues/issue-17904-2.stderr rename to tests/ui/where-clauses/where-clause-on-unit-struct.stderr From 480b44c8e1b9f4eb0885430bbc7a4ac89ff2b3f6 Mon Sep 17 00:00:00 2001 From: zedddie Date: Mon, 25 May 2026 17:48:48 +0200 Subject: [PATCH 169/174] bless batch --- tests/ui/coercion/fn-item-to-dyn-any.rs | 1 + .../auxiliary/dyn-trait-lifetime-infer-metadata.rs | 1 + tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs | 6 +++--- .../deprecation/deprecated-unstable-method-in-staged-api.rs | 1 + .../deprecated-unstable-method-in-staged-api.stderr | 4 ++-- tests/ui/drop/drop-in-for-loop-destructure.rs | 1 + .../need_type_info/unconstrained-type-in-closure.rs | 2 ++ .../need_type_info/unconstrained-type-in-closure.stderr | 2 +- tests/ui/regions/relate-bound-region-in-method-return.rs | 1 + tests/ui/structs-enums/tuple-variant-as-struct-variant.rs | 2 ++ .../ui/structs-enums/tuple-variant-as-struct-variant.stderr | 2 +- .../thread-local/thread-local-borrow-past-function-end.rs | 1 + .../thread-local-borrow-past-function-end.stderr | 2 +- .../ui/where-clauses/where-clause-on-struct-definitions.rs | 1 + tests/ui/where-clauses/where-clause-on-unit-struct.rs | 1 + tests/ui/where-clauses/where-clause-on-unit-struct.stderr | 2 +- 16 files changed, 21 insertions(+), 9 deletions(-) diff --git a/tests/ui/coercion/fn-item-to-dyn-any.rs b/tests/ui/coercion/fn-item-to-dyn-any.rs index f7ffcfb1d94e3..0e6e5aa5f89ce 100644 --- a/tests/ui/coercion/fn-item-to-dyn-any.rs +++ b/tests/ui/coercion/fn-item-to-dyn-any.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass use std::any::Any; diff --git a/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs b/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs index 5ecec31deb018..b2265886cb243 100644 --- a/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs +++ b/tests/ui/cross-crate/auxiliary/dyn-trait-lifetime-infer-metadata.rs @@ -1,3 +1,4 @@ +//! Auxiliary crate for . #![crate_type = "lib"] pub trait Foo<'a, T> { diff --git a/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs b/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs index e75613e04d333..aacc91f5f6f99 100644 --- a/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs +++ b/tests/ui/cross-crate/dyn-trait-lifetime-infer-metadata.rs @@ -1,8 +1,8 @@ +//! Regression test for . //@ run-pass -//@ aux-build:issue-17662.rs +//@ aux-build:dyn-trait-lifetime-infer-metadata.rs - -extern crate issue_17662 as i; +extern crate dyn_trait_lifetime_infer_metadata as i; use std::marker; diff --git a/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs index 193f89f837897..a4142ff701aec 100644 --- a/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs +++ b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.rs @@ -1,3 +1,4 @@ +//! Regression test for . #![feature(staged_api)] #![deny(deprecated)] diff --git a/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr index 8fbf6882699de..6c21b45dfd3cc 100644 --- a/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr +++ b/tests/ui/deprecation/deprecated-unstable-method-in-staged-api.stderr @@ -1,11 +1,11 @@ error: use of deprecated method `Foo::foo`: text - --> $DIR/issue-17337.rs:16:6 + --> $DIR/deprecated-unstable-method-in-staged-api.rs:17:6 | LL | .foo(); | ^^^ | note: the lint level is defined here - --> $DIR/issue-17337.rs:2:9 + --> $DIR/deprecated-unstable-method-in-staged-api.rs:3:9 | LL | #![deny(deprecated)] | ^^^^^^^^^^ diff --git a/tests/ui/drop/drop-in-for-loop-destructure.rs b/tests/ui/drop/drop-in-for-loop-destructure.rs index 31b16ef3a2f7b..2c277777eaf6a 100644 --- a/tests/ui/drop/drop-in-for-loop-destructure.rs +++ b/tests/ui/drop/drop-in-for-loop-destructure.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ run-pass #![allow(unused_variables)] struct Leak<'a> { diff --git a/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs index a65957ce074ef..3b6c20fe5bfbb 100644 --- a/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs +++ b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.rs @@ -1,3 +1,5 @@ +//! Regression test for . + use std::marker; struct B(marker::PhantomData); diff --git a/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr index b9cb76fc298d9..18c7f669fae7d 100644 --- a/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr +++ b/tests/ui/inference/need_type_info/unconstrained-type-in-closure.stderr @@ -1,5 +1,5 @@ error[E0282]: type annotations needed for `B<_>` - --> $DIR/issue-17551.rs:6:9 + --> $DIR/unconstrained-type-in-closure.rs:8:9 | LL | let foo = B(marker::PhantomData); | ^^^ ------------------- type must be known at this point diff --git a/tests/ui/regions/relate-bound-region-in-method-return.rs b/tests/ui/regions/relate-bound-region-in-method-return.rs index 6bb89a4aa7b43..a994f7f1af0cb 100644 --- a/tests/ui/regions/relate-bound-region-in-method-return.rs +++ b/tests/ui/regions/relate-bound-region-in-method-return.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass #![allow(dead_code)] diff --git a/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs b/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs index 5254f45d7c2de..53da4622013ba 100644 --- a/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs +++ b/tests/ui/structs-enums/tuple-variant-as-struct-variant.rs @@ -1,3 +1,5 @@ +//! Regression test for . + enum MyOption { MySome(T), MyNone, diff --git a/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr b/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr index 322c77eaa1dc5..1170e3b7bfd8a 100644 --- a/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr +++ b/tests/ui/structs-enums/tuple-variant-as-struct-variant.stderr @@ -1,5 +1,5 @@ error[E0769]: tuple variant `MyOption::MySome` written as struct variant - --> $DIR/issue-17800.rs:8:9 + --> $DIR/tuple-variant-as-struct-variant.rs:10:9 | LL | MyOption::MySome { x: 42 } => (), | ^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/tests/ui/thread-local/thread-local-borrow-past-function-end.rs b/tests/ui/thread-local/thread-local-borrow-past-function-end.rs index eb6a3d70f58e2..12938f0a0a2d4 100644 --- a/tests/ui/thread-local/thread-local-borrow-past-function-end.rs +++ b/tests/ui/thread-local/thread-local-borrow-past-function-end.rs @@ -1,3 +1,4 @@ +//! Regression test for . #![feature(thread_local)] #[thread_local] diff --git a/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr b/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr index 0dddea8336483..7686a6b714764 100644 --- a/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr +++ b/tests/ui/thread-local/thread-local-borrow-past-function-end.stderr @@ -1,5 +1,5 @@ error[E0712]: thread-local variable borrowed past end of function - --> $DIR/issue-17954.rs:7:13 + --> $DIR/thread-local-borrow-past-function-end.rs:8:13 | LL | let a = &FOO; | ^^^^ thread-local variables cannot be borrowed beyond the end of the function diff --git a/tests/ui/where-clauses/where-clause-on-struct-definitions.rs b/tests/ui/where-clauses/where-clause-on-struct-definitions.rs index fba71f70dd98e..a49df546ac31b 100644 --- a/tests/ui/where-clauses/where-clause-on-struct-definitions.rs +++ b/tests/ui/where-clauses/where-clause-on-struct-definitions.rs @@ -1,3 +1,4 @@ +//! Regression test for . //@ check-pass #![allow(dead_code)] // Test that we can parse where clauses on various forms of tuple diff --git a/tests/ui/where-clauses/where-clause-on-unit-struct.rs b/tests/ui/where-clauses/where-clause-on-unit-struct.rs index 9603da097b1cf..46fdd3737fa70 100644 --- a/tests/ui/where-clauses/where-clause-on-unit-struct.rs +++ b/tests/ui/where-clauses/where-clause-on-unit-struct.rs @@ -1,3 +1,4 @@ +//! Regression test for . // Test that we can parse a unit struct with a where clause, even if // it leads to an error later on since `T` is unused. diff --git a/tests/ui/where-clauses/where-clause-on-unit-struct.stderr b/tests/ui/where-clauses/where-clause-on-unit-struct.stderr index 9965106d1401a..174c2d9e22e8d 100644 --- a/tests/ui/where-clauses/where-clause-on-unit-struct.stderr +++ b/tests/ui/where-clauses/where-clause-on-unit-struct.stderr @@ -1,5 +1,5 @@ error[E0392]: type parameter `T` is never used - --> $DIR/issue-17904-2.rs:4:12 + --> $DIR/where-clause-on-unit-struct.rs:5:12 | LL | struct Foo where T: Copy; | ^ unused type parameter From 76b479930ff1649c599a3c5811bdac42670d4ee1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 25 May 2026 22:41:36 +0200 Subject: [PATCH 170/174] Explain what each GCC dockerfile is testing --- .../docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile | 6 ++++-- src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile | 3 +++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile index e1ac8e5bb8176..57bc0a0d0abb5 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile @@ -1,3 +1,6 @@ +# This Dockerfile builds sysroot crates with the GCC backend and then run libcore tests +# with it. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive @@ -41,5 +44,4 @@ ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ --set rust.codegen-backends=[\\\"gcc\\\"]" ENV SCRIPT="python3 ../x.py \ --stage 1 \ - test library/coretests \ - --set rust.codegen-backends=[\\\"gcc\\\"]" + test library/coretests" diff --git a/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile b/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile index 2208ed3ffbe15..dc7a680a095d3 100644 --- a/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile +++ b/src/ci/docker/host-x86_64/x86_64-gnu-gcc/Dockerfile @@ -1,3 +1,6 @@ +# This Dockerfile builds sysroot with the LLVM backendsthen run tests with the +# GCC backend. + FROM ubuntu:22.04 ARG DEBIAN_FRONTEND=noninteractive From b328c251b736ea89d14af55bd14a257d620531a4 Mon Sep 17 00:00:00 2001 From: Eric Huss Date: Mon, 25 May 2026 17:59:47 -0700 Subject: [PATCH 171/174] Update mdbook to 0.5.3 This is a small update with a few bug fixes. Changelog: https://github.com/rust-lang/mdBook/blob/master/CHANGELOG.md#mdbook-053 --- src/tools/rustbook/Cargo.lock | 685 +++++++++++++++++++++++----------- src/tools/rustbook/Cargo.toml | 2 +- 2 files changed, 474 insertions(+), 213 deletions(-) diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock index 89a581c90aa88..36b3705027aa1 100644 --- a/src/tools/rustbook/Cargo.lock +++ b/src/tools/rustbook/Cargo.lock @@ -28,9 +28,9 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.21" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", "anstyle-parse", @@ -43,15 +43,15 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" -version = "0.2.7" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e" dependencies = [ "utf8parse", ] @@ -78,15 +78,15 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "autocfg" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "bincode" @@ -114,9 +114,9 @@ checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "block-buffer" @@ -127,17 +127,26 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdd35008169921d80bc60d3d0ab416eecb028c4cd653352907921d95084790be" +dependencies = [ + "hybrid-array", +] + [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" [[package]] name = "cc" -version = "1.2.45" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "shlex", @@ -151,9 +160,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", @@ -164,9 +173,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.51" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c26d721170e0295f191a69bd9a1f93efcdb0aff38684b61ab5750468972e5f5" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -174,9 +183,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.51" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75835f0c7bf681bfd05abe44e965760fea999a5286c6eb2d59883634fd02011a" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ "anstream", "anstyle", @@ -186,9 +195,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.49" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck", "proc-macro2", @@ -198,15 +207,21 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.6" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" + +[[package]] +name = "const-oid" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" [[package]] name = "core-foundation-sys" @@ -223,6 +238,15 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + [[package]] name = "crc32fast" version = "1.5.0" @@ -234,14 +258,23 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" dependencies = [ "generic-array", "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "darling" version = "0.20.11" @@ -330,8 +363,19 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", - "crypto-common", + "block-buffer 0.10.4", + "crypto-common 0.1.7", +] + +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.0", + "const-oid", + "crypto-common 0.2.2", ] [[package]] @@ -342,9 +386,9 @@ checksum = "780955b8b195a21ab8e4ac6b60dd1dbdcec1dc6c51c0617964b08c81785e12c9" [[package]] name = "ego-tree" -version = "0.10.0" +version = "0.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2972feb8dffe7bc8c5463b1dacda1b0dfbed3710e50f977d965429692d74cd8" +checksum = "b04dc5a38e4f151a79d9f2451ae6037fb6eaf5cba34771f44781f80e508498e3" [[package]] name = "elasticlunr-rs" @@ -395,21 +439,21 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" -version = "0.1.4" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "flate2" -version = "1.1.5" +version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" dependencies = [ "crc32fast", "miniz_oxide", @@ -421,27 +465,47 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "font-awesome-as-a-crate" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "932dcfbd51320af5f27f1ba02d2e567dec332cac7d2c221ba45d8e767264c4dc" +checksum = "b40fbe89eb7639971503bf5f17bd31c41338e8f92e03c5744d07f2e03d43f679" [[package]] -name = "futf" -version = "0.1.5" +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-task" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df420e2e84819663797d1ec6544b13c5be84629e7bb00dc960d6917db2987843" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ - "mac", - "new_debug_unreachable", + "futures-core", + "futures-task", + "pin-project-lite", + "slab", ] [[package]] name = "generic-array" -version = "0.14.9" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4bb6743198531e02858aeaea5398fcc883e71851fcbcb5a2f773e2fb6cb1edf2" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ "typenum", "version_check", @@ -458,14 +522,15 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.3.4" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", "r-efi", "wasip2", + "wasip3", ] [[package]] @@ -480,9 +545,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.3.2" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759e2d5aea3287cb1190c8ec394f42866cb5bf74fcbf213f354e3c856ea26098" +checksum = "d43ccdfe15a81ab0a8af639e90254227c9a46afd9c5f5b6ec7efaa345c4b0f00" dependencies = [ "derive_builder", "log", @@ -491,14 +556,23 @@ dependencies = [ "pest_derive", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] name = "hashbrown" -version = "0.16.1" +version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "foldhash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -514,9 +588,9 @@ checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] name = "html5ever" -version = "0.36.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6452c4751a24e1b99c3260d505eaeee76a050573e61f30ac2c924ddc7236f01e" +checksum = "46a1761807faccc9a19e86944bbf40610014066306f96edcdedc2fb714bcb7b8" dependencies = [ "log", "markup5ever", @@ -537,11 +611,20 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "hybrid-array" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9155a582abd142abc056962c29e3ce5ff2ad5469f4246b537ed42c5deba857da" +dependencies = [ + "typenum", +] + [[package]] name = "iana-time-zone" -version = "0.1.64" +version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", @@ -561,6 +644,12 @@ dependencies = [ "cc", ] +[[package]] +name = "id-arena" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" + [[package]] name = "ident_case" version = "1.0.1" @@ -569,12 +658,14 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "indexmap" -version = "2.12.1" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.17.1", + "serde", + "serde_core", ] [[package]] @@ -585,16 +676,18 @@ checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "js-sys" -version = "0.3.82" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b011eec8cc36da2aab2d5cff675ec18454fad408585853910a202391cf9f8e65" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -605,11 +698,17 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +[[package]] +name = "leb128fmt" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" + [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "linereader" @@ -622,9 +721,9 @@ dependencies = [ [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "lock_api" @@ -637,21 +736,15 @@ dependencies = [ [[package]] name = "log" -version = "0.4.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" - -[[package]] -name = "mac" -version = "0.1.1" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "markup5ever" -version = "0.36.1" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c3294c4d74d0742910f8c7b466f44dda9eb2d5742c1e430138df290a1e8451c" +checksum = "7122d987ec5f704ee56f6e5b41a7d93722e9aae27ae07cafa4036c4d3f9757de" dependencies = [ "log", "tendril", @@ -669,23 +762,23 @@ dependencies = [ [[package]] name = "mdbook-core" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39a3873d4afac65583f1acb56ff058df989d5b4a2464bb02c785549727d307ee" +checksum = "6fc1c4da7fd9e2e412f3891428f9468fab890ed159723ed0892bb85a049ac1c1" dependencies = [ "anyhow", "regex", "serde", "serde_json", - "toml 0.9.8", + "toml 1.1.2+spec-1.1.0", "tracing", ] [[package]] name = "mdbook-driver" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a229930b29a9908560883e1f386eae25d8a971d259a80f49916a50627f04a42d" +checksum = "2068566fc3c100cfd19f4a13e4e0eb4fdcd2250cfd0aa633ec25102b1c98d53e" dependencies = [ "anyhow", "indexmap", @@ -700,16 +793,16 @@ dependencies = [ "serde_json", "shlex", "tempfile", - "toml 0.9.8", + "toml 1.1.2+spec-1.1.0", "topological-sort", "tracing", ] [[package]] name = "mdbook-html" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dee80c03c65e3212fb528b8c9be5568a6a85cf795d03cf9fd6ba39ad52069ca" +checksum = "85ed2140251689f928615f0a5615413eb072eb28e003d179257aa4f034c95513" dependencies = [ "anyhow", "ego-tree", @@ -722,11 +815,11 @@ dependencies = [ "mdbook-core", "mdbook-markdown", "mdbook-renderer", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "regex", "serde", "serde_json", - "sha2", + "sha2 0.11.0", "tracing", ] @@ -742,7 +835,7 @@ dependencies = [ "mdbook-preprocessor", "mdbook-renderer", "polib", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "pulldown-cmark-to-cmark 21.1.0", "regex", "semver", @@ -753,20 +846,20 @@ dependencies = [ [[package]] name = "mdbook-markdown" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07c41bf35212f5d8b83e543aa6a4887dc5709c8489c5fb9ed00f1b51ce1a2cc6" +checksum = "2cb3ca9eadf02ce206118a0b9c264718723d669b110c01a720fa9a0786f30642" dependencies = [ - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "regex", "tracing", ] [[package]] name = "mdbook-preprocessor" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d87bf40be0597f26f0822f939a64f02bf92c4655ba04490aadbf83601a013bb" +checksum = "8eff7b4afaafd664a649a03b8891ad8199aef359a35b911ad6c31acab38ed209" dependencies = [ "anyhow", "mdbook-core", @@ -776,9 +869,9 @@ dependencies = [ [[package]] name = "mdbook-renderer" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93ed59f225b3ae4283c56bea633db83184627a090d892908bd66990c68e10b43" +checksum = "e607259410d53aa5cdaf5b6c1c6b3fd61f2e0f0523ebf457d34cd4f0b71e2a88" dependencies = [ "anyhow", "mdbook-core", @@ -807,14 +900,14 @@ dependencies = [ [[package]] name = "mdbook-summary" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c00d85b291d67a69c92e939450390fe34d6ea418a868c8f7b42f0b300af35a7b" +checksum = "cae8a734e5e35b0bc145b46d01fd27e266ba647dcdb9b8c907cb6a29eca9122b" dependencies = [ "anyhow", "mdbook-core", "memchr", - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", "serde", "tracing", ] @@ -837,9 +930,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.7.6" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "miniz_oxide" @@ -892,9 +985,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -933,9 +1026,9 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pest" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "989e7521a040efde50c3ab6bbadafbe15ab6dc042686926be59ac35d74607df4" +checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" dependencies = [ "memchr", "ucd-trie", @@ -943,9 +1036,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "187da9a3030dbafabbbfb20cb323b976dc7b7ce91fcd84f2f74d6e31d378e2de" +checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" dependencies = [ "pest", "pest_generator", @@ -953,9 +1046,9 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49b401d98f5757ebe97a26085998d6c0eecec4995cad6ab7fc30ffdf4b052843" +checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" dependencies = [ "pest", "pest_meta", @@ -966,12 +1059,12 @@ dependencies = [ [[package]] name = "pest_meta" -version = "2.8.3" +version = "2.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72f27a2cfee9f9039c4d86faa5af122a0ac3851441a34865b8a043b46be0065a" +checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" dependencies = [ "pest", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -1015,9 +1108,9 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "polib" @@ -1034,11 +1127,21 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "proc-macro2" -version = "1.0.103" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] @@ -1058,9 +1161,9 @@ dependencies = [ [[package]] name = "pulldown-cmark" -version = "0.13.0" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8bbe1a966bd2f362681a44f6edce3c2310ac21e4d5067a6e7ec396297a6ea0" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ "bitflags", "memchr", @@ -1089,29 +1192,29 @@ version = "21.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8246feae3db61428fd0bb94285c690b460e4517d83152377543ca802357785f1" dependencies = [ - "pulldown-cmark 0.13.0", + "pulldown-cmark 0.13.4", ] [[package]] name = "quote" -version = "1.0.42" +version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "r-efi" -version = "5.3.0" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "railroad" -version = "0.3.3" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5b8e8a7c20c600f9b98cbf46b64e63d5c9e69deb98cee1ff264de9f1dda5d" +checksum = "813b53dbe6d583f1ac0c3fb394e0ce3b613530adff8265c274fcdfcd82cc6da5" dependencies = [ "unicode-width", ] @@ -1127,9 +1230,9 @@ dependencies = [ [[package]] name = "regex" -version = "1.12.2" +version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", @@ -1139,9 +1242,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", @@ -1150,9 +1253,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rustbook" @@ -1168,9 +1271,9 @@ dependencies = [ [[package]] name = "rustix" -version = "1.1.2" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ "bitflags", "errno", @@ -1185,12 +1288,6 @@ version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" -[[package]] -name = "ryu" -version = "1.0.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" - [[package]] name = "same-file" version = "1.0.6" @@ -1208,9 +1305,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -1244,15 +1341,15 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.145" +version = "1.0.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" dependencies = [ "itoa", "memchr", - "ryu", "serde", "serde_core", + "zmij", ] [[package]] @@ -1266,9 +1363,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.3" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -1280,8 +1377,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -1301,15 +1409,21 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "siphasher" -version = "1.0.1" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" @@ -1327,7 +1441,6 @@ dependencies = [ "parking_lot", "phf_shared", "precomputed-hash", - "serde", ] [[package]] @@ -1350,9 +1463,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.110" +version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", @@ -1373,15 +1486,15 @@ dependencies = [ "regex-syntax", "serde", "serde_derive", - "thiserror 2.0.17", + "thiserror 2.0.18", "walkdir", ] [[package]] name = "tempfile" -version = "3.23.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom", @@ -1392,12 +1505,11 @@ dependencies = [ [[package]] name = "tendril" -version = "0.4.3" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d24a120c5fc464a3458240ee02c299ebcb9d67b5249c8848b09d639dca8d7bb0" +checksum = "c4790fc369d5a530f4b544b094e31388b9b3a37c0f4652ade4505945f5660d24" dependencies = [ - "futf", - "mac", + "new_debug_unreachable", "utf-8", ] @@ -1418,11 +1530,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -1438,9 +1550,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -1470,17 +1582,17 @@ dependencies = [ [[package]] name = "toml" -version = "0.9.8" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "indexmap", "serde_core", - "serde_spanned 1.0.3", - "toml_datetime 0.7.3", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 1.0.3", ] [[package]] @@ -1494,9 +1606,9 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] @@ -1512,16 +1624,16 @@ dependencies = [ "serde_spanned 0.6.9", "toml_datetime 0.6.11", "toml_write", - "winnow", + "winnow 0.7.15", ] [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.3", ] [[package]] @@ -1532,9 +1644,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[package]] name = "toml_writer" -version = "1.0.4" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "topological-sort" @@ -1544,9 +1656,9 @@ checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d" [[package]] name = "tracing" -version = "0.1.43" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d15d90a0b5c19378952d479dc858407149d7bb45a14de0142f6c534b16fc647" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", @@ -1566,9 +1678,9 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a04e24fab5c89c6a36eb8558c9656f30d81de51dfa4d3b45f26b21d61fa0a6c" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", "valuable", @@ -1587,9 +1699,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -1605,9 +1717,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -1617,15 +1729,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicase" -version = "2.8.1" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] name = "unicode-ident" -version = "1.0.22" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-width" @@ -1633,6 +1745,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "utf-8" version = "0.7.6" @@ -1669,18 +1787,27 @@ dependencies = [ [[package]] name = "wasip2" -version = "1.0.1+wasi-0.2.4" +version = "1.0.3+wasi-0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" +dependencies = [ + "wit-bindgen 0.57.1", +] + +[[package]] +name = "wasip3" +version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da95793dfc411fbbd93f5be7715b0578ec61fe87cb1a42b12eb625caa5c5ea60" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -1691,9 +1818,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04264334509e04a7bf8690f2384ef5265f05143a4bff3889ab7a3269adab59c2" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1701,9 +1828,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "420bc339d9f322e562942d52e115d57e950d12d88983a14c79b86859ee6c7ebc" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -1714,18 +1841,52 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.105" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76f218a38c84bcb33c25ec7059b07847d465ce0e0a76b995e134a45adcb6af76" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] +[[package]] +name = "wasm-encoder" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +dependencies = [ + "leb128fmt", + "wasmparser", +] + +[[package]] +name = "wasm-metadata" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +dependencies = [ + "anyhow", + "indexmap", + "wasm-encoder", + "wasmparser", +] + +[[package]] +name = "wasmparser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +dependencies = [ + "bitflags", + "hashbrown 0.15.5", + "indexmap", + "semver", +] + [[package]] name = "web_atoms" -version = "0.2.0" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acd0c322f146d0f8aad130ce6c187953889359584497dac6561204c8e17bb43d" +checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ "phf", "phf_codegen", @@ -1812,15 +1973,115 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.13" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" + +[[package]] +name = "wit-bindgen" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" +dependencies = [ + "wit-bindgen-rust-macro", +] + [[package]] name = "wit-bindgen" -version = "0.46.0" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "wit-bindgen-core" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" +dependencies = [ + "anyhow", + "heck", + "wit-parser", +] + +[[package]] +name = "wit-bindgen-rust" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" +dependencies = [ + "anyhow", + "heck", + "indexmap", + "prettyplease", + "syn", + "wasm-metadata", + "wit-bindgen-core", + "wit-component", +] + +[[package]] +name = "wit-bindgen-rust-macro" +version = "0.51.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" +dependencies = [ + "anyhow", + "prettyplease", + "proc-macro2", + "quote", + "syn", + "wit-bindgen-core", + "wit-bindgen-rust", +] + +[[package]] +name = "wit-component" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" +dependencies = [ + "anyhow", + "bitflags", + "indexmap", + "log", + "serde", + "serde_derive", + "serde_json", + "wasm-encoder", + "wasm-metadata", + "wasmparser", + "wit-parser", +] + +[[package]] +name = "wit-parser" +version = "0.244.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" +dependencies = [ + "anyhow", + "id-arena", + "indexmap", + "log", + "semver", + "serde", + "serde_derive", + "serde_json", + "unicode-xid", + "wasmparser", +] + +[[package]] +name = "zmij" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml index 6f28ebe519314..f981d09fbbee1 100644 --- a/src/tools/rustbook/Cargo.toml +++ b/src/tools/rustbook/Cargo.toml @@ -9,7 +9,7 @@ edition = "2021" [dependencies] clap = { version = "4.0.32", features = ["cargo"] } -mdbook-driver = { version = "0.5.2", features = ["search"] } +mdbook-driver = { version = "0.5.3", features = ["search"] } mdbook-i18n-helpers = "0.4.0" mdbook-spec = { path = "../../doc/reference/tools/mdbook-spec" } mdbook-trpl = { path = "../../doc/book/packages/mdbook-trpl" } From 527ed7ff85514f9261ff66c87b9b55905dd4ae0b Mon Sep 17 00:00:00 2001 From: xonx <119700621+xonx4l@users.noreply.github.com> Date: Tue, 26 May 2026 04:43:06 +0000 Subject: [PATCH 172/174] add optional output-dir argument --- .../stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs | 6 +++++- library/stdarch/crates/stdarch-gen-hexagon/src/main.rs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs index 3e3f00bb7fb02..934c7e28fe374 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon-scalar/src/main.rs @@ -664,7 +664,11 @@ fn main() -> Result<(), String> { let intrinsics = parse_header(&header_content); println!("Parsed {} scalar intrinsics", intrinsics.len()); - let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + let hexagon_dir = std::env::args() + .nth(1) + .map(std::path::PathBuf::from) + .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); + std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; let scalar_path = hexagon_dir.join("scalar.rs"); generate_scalar_file(&intrinsics, &scalar_path)?; diff --git a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs index 8a58c66313daf..7a1c3030c0042 100644 --- a/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs +++ b/library/stdarch/crates/stdarch-gen-hexagon/src/main.rs @@ -1691,7 +1691,11 @@ fn main() -> Result<(), String> { } // Generate output files - let hexagon_dir = crate_dir.join("../core_arch/src/hexagon"); + let hexagon_dir = std::env::args() + .nth(1) + .map(std::path::PathBuf::from) + .unwrap_or_else(|| crate_dir.join("../core_arch/src/hexagon")); + std::fs::create_dir_all(&hexagon_dir).map_err(|e| e.to_string())?; // Generate v64.rs (64-byte vector mode) let v64_path = hexagon_dir.join("v64.rs"); From 40a782103b749f09d15e7629083e18803dc307db Mon Sep 17 00:00:00 2001 From: aerooneqq Date: Tue, 26 May 2026 08:06:36 +0300 Subject: [PATCH 173/174] Remove method call generation in delegation --- compiler/rustc_ast_lowering/src/delegation.rs | 182 +- .../src/delegation/generics.rs | 45 +- compiler/rustc_hir/src/hir.rs | 11 +- compiler/rustc_hir_analysis/src/delegation.rs | 22 +- compiler/rustc_hir_analysis/src/lib.rs | 2 +- compiler/rustc_hir_typeck/src/callee.rs | 123 +- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 9 +- .../rustc_hir_typeck/src/method/confirm.rs | 31 +- compiler/rustc_hir_typeck/src/method/mod.rs | 2 +- tests/pretty/delegation-inline-attribute.pp | 31 +- tests/pretty/delegation-self-rename.pp | 2 +- ...y_owner_parent_found_in_diagnostics.stderr | 47 +- .../delegation/generics/generics-aux-pass.rs | 5 - .../generics/impl-to-trait-method.rs | 1 - .../generics/impl-to-trait-method.stderr | 11 +- tests/ui/delegation/self-coercion-errors.rs | 234 ++ .../ui/delegation/self-coercion-errors.stderr | 1916 +++++++++++++++++ .../delegation/self-coercion-static-free.rs | 52 + .../self-coercion-static-free.stderr | 87 + tests/ui/delegation/self-coercion.rs | 14 +- tests/ui/delegation/wrong-lifetime-rib.rs | 2 +- tests/ui/delegation/wrong-lifetime-rib.stderr | 14 +- 22 files changed, 2606 insertions(+), 237 deletions(-) create mode 100644 tests/ui/delegation/self-coercion-errors.rs create mode 100644 tests/ui/delegation/self-coercion-errors.stderr create mode 100644 tests/ui/delegation/self-coercion-static-free.rs create mode 100644 tests/ui/delegation/self-coercion-static-free.stderr diff --git a/compiler/rustc_ast_lowering/src/delegation.rs b/compiler/rustc_ast_lowering/src/delegation.rs index df5ea7c3168ff..8accaa8c3125c 100644 --- a/compiler/rustc_ast_lowering/src/delegation.rs +++ b/compiler/rustc_ast_lowering/src/delegation.rs @@ -58,8 +58,8 @@ use smallvec::SmallVec; use crate::delegation::generics::{GenericsGenerationResult, GenericsGenerationResults}; use crate::errors::{CycleInDelegationSignatureResolution, UnresolvedDelegationCallee}; use crate::{ - AllowReturnTypeNotation, GenericArgsMode, ImplTraitContext, ImplTraitPosition, LoweringContext, - ParamMode, ResolverAstLoweringExt, + AllowReturnTypeNotation, ImplTraitContext, ImplTraitPosition, LoweringContext, ParamMode, + ResolverAstLoweringExt, }; mod generics; @@ -144,7 +144,7 @@ impl<'hir> LoweringContext<'_, 'hir> { let mut generics = self.uplift_delegation_generics(delegation, sig_id, is_method); - let body_id = self.lower_delegation_body( + let (body_id, call_expr_id) = self.lower_delegation_body( delegation, is_method, param_count, @@ -152,16 +152,23 @@ impl<'hir> LoweringContext<'_, 'hir> { span, ); - let decl = - self.lower_delegation_decl(sig_id, param_count, c_variadic, span, &generics); + let decl = self.lower_delegation_decl( + sig_id, + param_count, + c_variadic, + span, + &generics, + delegation.id, + call_expr_id, + ); let sig = self.lower_delegation_sig(sig_id, decl, span); let ident = self.lower_ident(delegation.ident); let generics = self.arena.alloc(hir::Generics { has_where_clause_predicates: false, - params: self.arena.alloc_from_iter(generics.all_params(span, self)), - predicates: self.arena.alloc_from_iter(generics.all_predicates(span, self)), + params: self.arena.alloc_from_iter(generics.all_params()), + predicates: self.arena.alloc_from_iter(generics.all_predicates()), span, where_clause_span: span, }); @@ -280,6 +287,8 @@ impl<'hir> LoweringContext<'_, 'hir> { c_variadic: bool, span: Span, generics: &GenericsGenerationResults<'hir>, + call_path_node_id: NodeId, + call_expr_id: HirId, ) -> &'hir hir::FnDecl<'hir> { // The last parameter in C variadic functions is skipped in the signature, // like during regular lowering. @@ -297,7 +306,9 @@ impl<'hir> LoweringContext<'_, 'hir> { hir_id: self.next_id(), kind: hir::TyKind::InferDelegation(hir::InferDelegation::Sig( sig_id, - hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationGenerics { + hir::InferDelegationSig::Output(self.arena.alloc(hir::DelegationInfo { + call_expr_id, + call_path_res: self.get_resolution_id(call_path_node_id), child_args_segment_id: generics.child.args_segment_id, parent_args_segment_id: generics.parent.args_segment_id, self_ty_id: generics.self_ty_id, @@ -400,10 +411,11 @@ impl<'hir> LoweringContext<'_, 'hir> { param_count: usize, generics: &mut GenericsGenerationResults<'hir>, span: Span, - ) -> BodyId { + ) -> (BodyId, HirId) { let block = delegation.body.as_deref(); + let mut call_expr_id = HirId::INVALID; - self.lower_body(|this| { + let block_id = self.lower_body(|this| { let mut parameters: Vec> = Vec::with_capacity(param_count); let mut args: Vec> = Vec::with_capacity(param_count); @@ -440,10 +452,17 @@ impl<'hir> LoweringContext<'_, 'hir> { args.push(this.lower_target_expr(&block)); } - let final_expr = this.finalize_body_lowering(delegation, args, generics, span); + let (final_expr, hir_id) = + this.finalize_body_lowering(delegation, args, generics, span); + + call_expr_id = hir_id; (this.arena.alloc_from_iter(parameters), final_expr) - }) + }); + + debug_assert_ne!(call_expr_id, HirId::INVALID); + + (block_id, call_expr_id) } // FIXME(fn_delegation): Alternatives for target expression lowering: @@ -459,108 +478,59 @@ impl<'hir> LoweringContext<'_, 'hir> { self.mk_expr(hir::ExprKind::Block(block, None), block.span) } - // Generates expression for the resulting body. If possible, `MethodCall` is used - // to allow autoref/autoderef for target expression. For example in: - // - // trait Trait : Sized { - // fn by_value(self) -> i32 { 1 } - // fn by_mut_ref(&mut self) -> i32 { 2 } - // fn by_ref(&self) -> i32 { 3 } - // } - // - // struct NewType(SomeType); - // impl Trait for NewType { - // reuse Trait::* { self.0 } - // } - // - // `self.0` will automatically coerce. fn finalize_body_lowering( &mut self, delegation: &Delegation, args: Vec>, generics: &mut GenericsGenerationResults<'hir>, span: Span, - ) -> hir::Expr<'hir> { - let args = self.arena.alloc_from_iter(args); - - let has_generic_args = - delegation.path.segments.iter().rev().skip(1).any(|segment| segment.args.is_some()); - - let call = if self - .get_resolution_id(delegation.id) - .map(|def_id| self.is_method(def_id, span)) - .unwrap_or_default() - && delegation.qself.is_none() - && !has_generic_args - && !args.is_empty() - { - let ast_segment = delegation.path.segments.last().unwrap(); - let segment = self.lower_path_segment( - delegation.path.span, - ast_segment, - ParamMode::Optional, - GenericArgsMode::Err, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - // FIXME(fn_delegation): proper support for parent generics propagation - // in method call scenario. - let segment = self.process_segment(span, &segment, &mut generics.child); - let segment = self.arena.alloc(segment); - - self.arena.alloc(hir::Expr { - hir_id: self.next_id(), - kind: hir::ExprKind::MethodCall(segment, &args[0], &args[1..], span), - span, - }) - } else { - let path = self.lower_qpath( - delegation.id, - &delegation.qself, - &delegation.path, - ParamMode::Optional, - AllowReturnTypeNotation::No, - ImplTraitContext::Disallowed(ImplTraitPosition::Path), - None, - ); - - let new_path = match path { - hir::QPath::Resolved(ty, path) => { - let mut new_path = path.clone(); - let len = new_path.segments.len(); - - new_path.segments = self.arena.alloc_from_iter( - new_path.segments.iter().enumerate().map(|(idx, segment)| { - if idx + 2 == len { - self.process_segment(span, segment, &mut generics.parent) - } else if idx + 1 == len { - self.process_segment(span, segment, &mut generics.child) - } else { - segment.clone() - } - }), - ); - - hir::QPath::Resolved(ty, self.arena.alloc(new_path)) - } - hir::QPath::TypeRelative(ty, segment) => { - let segment = self.process_segment(span, segment, &mut generics.child); - - hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) - } - }; + ) -> (hir::Expr<'hir>, HirId) { + let path = self.lower_qpath( + delegation.id, + &delegation.qself, + &delegation.path, + ParamMode::Optional, + AllowReturnTypeNotation::No, + ImplTraitContext::Disallowed(ImplTraitPosition::Path), + None, + ); + + let new_path = match path { + hir::QPath::Resolved(ty, path) => { + let mut new_path = path.clone(); + let len = new_path.segments.len(); + + new_path.segments = self.arena.alloc_from_iter( + new_path.segments.iter().enumerate().map(|(idx, segment)| { + if idx + 2 == len { + self.process_segment(span, segment, &mut generics.parent) + } else if idx + 1 == len { + self.process_segment(span, segment, &mut generics.child) + } else { + segment.clone() + } + }), + ); - generics.self_ty_id = match new_path { - hir::QPath::Resolved(ty, _) => ty, - hir::QPath::TypeRelative(ty, _) => Some(ty), + hir::QPath::Resolved(ty, self.arena.alloc(new_path)) } - .map(|ty| ty.hir_id); + hir::QPath::TypeRelative(ty, segment) => { + let segment = self.process_segment(span, segment, &mut generics.child); - let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); - self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)) + hir::QPath::TypeRelative(ty, self.arena.alloc(segment)) + } }; + generics.self_ty_id = match new_path { + hir::QPath::Resolved(ty, _) => ty, + hir::QPath::TypeRelative(ty, _) => Some(ty), + } + .map(|ty| ty.hir_id); + + let callee_path = self.arena.alloc(self.mk_expr(hir::ExprKind::Path(new_path), span)); + let args = self.arena.alloc_from_iter(args); + let call = self.arena.alloc(self.mk_expr(hir::ExprKind::Call(callee_path, args), span)); + let block = self.arena.alloc(hir::Block { stmts: &[], expr: Some(call), @@ -570,7 +540,7 @@ impl<'hir> LoweringContext<'_, 'hir> { targeted_by_break: false, }); - self.mk_expr(hir::ExprKind::Block(block, None), span) + (self.mk_expr(hir::ExprKind::Block(block, None), span), call.hir_id) } fn process_segment( @@ -581,8 +551,10 @@ impl<'hir> LoweringContext<'_, 'hir> { ) -> hir::PathSegment<'hir> { let details = result.generics.args_propagation_details(); + // Always uplift generic params, because if they are not empty then they + // should be generated in delegation. + let generics = result.generics.into_hir_generics(self, span); let segment = if details.should_propagate { - let generics = result.generics.into_hir_generics(self, span); let args = generics.into_generic_args(self, span); // Needed for better error messages (`trait-impl-wrong-args-count.rs` test). diff --git a/compiler/rustc_ast_lowering/src/delegation/generics.rs b/compiler/rustc_ast_lowering/src/delegation/generics.rs index c2c3bd740e3cf..f8e3528750035 100644 --- a/compiler/rustc_ast_lowering/src/delegation/generics.rs +++ b/compiler/rustc_ast_lowering/src/delegation/generics.rs @@ -171,22 +171,9 @@ impl<'hir> GenericsGenerationResult<'hir> { } impl<'hir> GenericsGenerationResults<'hir> { - pub(super) fn all_params( - &mut self, - span: Span, - ctx: &mut LoweringContext<'_, 'hir>, - ) -> impl Iterator> { - // Now we always call `into_hir_generics` both on child and parent, - // however in future we would not do that, when scenarios like - // method call will be supported (if HIR generics were not obtained - // then it means that we did not propagated them, thus we do not need - // to generate params). - let mut create_params = |result: &mut GenericsGenerationResult<'hir>| { - result.generics.into_hir_generics(ctx, span).hir_generics_or_empty().params - }; - - let parent = create_params(&mut self.parent); - let child = create_params(&mut self.child); + pub(super) fn all_params(&self) -> impl Iterator> { + let parent = self.parent.generics.hir_generics_or_empty().params; + let child = self.child.generics.hir_generics_or_empty().params; // Order generics, first we have parent and child lifetimes, // then parent and child types and consts. @@ -205,24 +192,14 @@ impl<'hir> GenericsGenerationResults<'hir> { /// and `generate_lifetime_predicate` functions) we need to add them to delegation generics. /// Those predicates will not affect resulting predicate inheritance and folding /// in `rustc_hir_analysis`, as we inherit all predicates from delegation signature. - pub(super) fn all_predicates( - &mut self, - span: Span, - ctx: &mut LoweringContext<'_, 'hir>, - ) -> impl Iterator> { - // Now we always call `into_hir_generics` both on child and parent, - // however in future we would not do that, when scenarios like - // method call will be supported (if HIR generics were not obtained - // then it means that we did not propagated them, thus we do not need - // to generate predicates). - let mut create_predicates = |result: &mut GenericsGenerationResult<'hir>| { - result.generics.into_hir_generics(ctx, span).hir_generics_or_empty().predicates - }; - - let parent = create_predicates(&mut self.parent); - let child = create_predicates(&mut self.child); - - parent.into_iter().chain(child).copied() + pub(super) fn all_predicates(&self) -> impl Iterator> { + self.parent + .generics + .hir_generics_or_empty() + .predicates + .into_iter() + .chain(self.child.generics.hir_generics_or_empty().predicates) + .copied() } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index 0569c1b986d1a..59d1b4b5576ee 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3864,9 +3864,10 @@ pub enum OpaqueTyOrigin { }, } -// Ids of parent (or child) path segment that contains user-specified args #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] -pub struct DelegationGenerics { +pub struct DelegationInfo { + pub call_expr_id: HirId, + pub call_path_res: Option, pub parent_args_segment_id: Option, pub child_args_segment_id: Option, pub self_ty_id: Option, @@ -3876,8 +3877,8 @@ pub struct DelegationGenerics { #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] pub enum InferDelegationSig<'hir> { Input(usize), - // Place generics info here, as we always specify output type for delegations. - Output(&'hir DelegationGenerics), + // Place delegation info here, as we always specify output type for delegations. + Output(&'hir DelegationInfo), } #[derive(Debug, Clone, Copy, PartialEq, Eq, StableHash)] @@ -4164,7 +4165,7 @@ impl<'hir> FnDecl<'hir> { None } - pub fn opt_delegation_generics(&self) -> Option<&'hir DelegationGenerics> { + pub fn opt_delegation_info(&self) -> Option<&'hir DelegationInfo> { if let FnRetTy::Return(ty) = self.output && let TyKind::InferDelegation(InferDelegation::Sig(_, kind)) = ty.kind && let InferDelegationSig::Output(generics) = kind diff --git a/compiler/rustc_hir_analysis/src/delegation.rs b/compiler/rustc_hir_analysis/src/delegation.rs index 2d6d5a5d81f9a..f67181a4655b9 100644 --- a/compiler/rustc_hir_analysis/src/delegation.rs +++ b/compiler/rustc_hir_analysis/src/delegation.rs @@ -7,7 +7,7 @@ use std::debug_assert_matches; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::DefKind; use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::{DelegationGenerics, HirId, PathSegment}; +use rustc_hir::{DelegationInfo, HirId, PathSegment}; use rustc_middle::ty::{ self, EarlyBinder, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitableExt, }; @@ -71,13 +71,17 @@ enum SelfPositionKind { None, } -fn get_delegation_generics(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationGenerics { +pub fn opt_get_delegation_info( + tcx: TyCtxt<'_>, + delegation_id: LocalDefId, +) -> Option<&DelegationInfo> { tcx.hir_node(tcx.local_def_id_to_hir_id(delegation_id)) .fn_sig() - .expect("processing delegation") - .decl - .opt_delegation_generics() - .expect("processing delegation") + .and_then(|sig| sig.decl.opt_delegation_info()) +} + +fn get_delegation_info(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> &DelegationInfo { + opt_get_delegation_info(tcx, delegation_id).expect("processing delegation") } fn create_self_position_kind( @@ -92,7 +96,7 @@ fn create_self_position_kind( | (FnKind::AssocTrait, FnKind::Free) => SelfPositionKind::Zero, (FnKind::Free, FnKind::AssocTrait) => { - let propagate_self_ty = get_delegation_generics(tcx, delegation_id).propagate_self_ty; + let propagate_self_ty = get_delegation_info(tcx, delegation_id).propagate_self_ty; SelfPositionKind::AfterLifetimes(propagate_self_ty) } @@ -278,7 +282,7 @@ fn get_parent_and_inheritance_kind<'tcx>( } fn get_delegation_self_ty_or_err(tcx: TyCtxt<'_>, delegation_id: LocalDefId) -> Ty<'_> { - get_delegation_generics(tcx, delegation_id) + get_delegation_info(tcx, delegation_id) .self_ty_id .map(|id| { let ctx = ItemCtxt::new(tcx, delegation_id); @@ -640,7 +644,7 @@ fn get_delegation_user_specified_args<'tcx>( tcx: TyCtxt<'tcx>, delegation_id: LocalDefId, ) -> (&'tcx [ty::GenericArg<'tcx>], &'tcx [ty::GenericArg<'tcx>]) { - let info = get_delegation_generics(tcx, delegation_id); + let info = get_delegation_info(tcx, delegation_id); let get_segment = |hir_id: HirId| -> Option<(&'tcx PathSegment<'tcx>, DefId)> { let segment = tcx.hir_node(hir_id).expect_path_segment(); diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs index 9cadaef8f886b..1e9bc80749881 100644 --- a/compiler/rustc_hir_analysis/src/lib.rs +++ b/compiler/rustc_hir_analysis/src/lib.rs @@ -73,7 +73,7 @@ mod check_unused; mod coherence; mod collect; mod constrained_generic_params; -mod delegation; +pub mod delegation; pub mod errors; pub mod hir_ty_lowering; pub mod hir_wf_check; diff --git a/compiler/rustc_hir_typeck/src/callee.rs b/compiler/rustc_hir_typeck/src/callee.rs index e15c9fe661641..d51a0bf2c3ef4 100644 --- a/compiler/rustc_hir_typeck/src/callee.rs +++ b/compiler/rustc_hir_typeck/src/callee.rs @@ -7,15 +7,16 @@ use rustc_hir::def::{self, CtorKind, Namespace, Res}; use rustc_hir::def_id::DefId; use rustc_hir::{self as hir, HirId, LangItem, find_attr}; use rustc_hir_analysis::autoderef::Autoderef; +use rustc_hir_analysis::delegation::opt_get_delegation_info; use rustc_infer::infer::BoundRegionConversionTime; use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; use rustc_middle::ty::adjustment::{ Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, }; -use rustc_middle::ty::{self, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; +use rustc_middle::ty::{self, FnSig, GenericArgsRef, Ty, TyCtxt, TypeVisitableExt, Unnormalized}; use rustc_middle::{bug, span_bug}; use rustc_span::def_id::LocalDefId; -use rustc_span::{Span, sym}; +use rustc_span::{Ident, Span, sym}; use rustc_target::spec::{AbiMap, AbiMapping}; use rustc_trait_selection::error_reporting::traits::DefIdOrName; use rustc_trait_selection::infer::InferCtxtExt as _; @@ -27,6 +28,8 @@ use super::method::probe::ProbeScope; use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::errors; use crate::method::TreatNotYetDefinedOpaques; +use crate::method::confirm::ConfirmContext; +use crate::method::probe::{IsSuggestion, Mode}; /// Checks that it is legal to call methods of the trait corresponding /// to `trait_id` (this only cares about the trait, not the specific @@ -591,16 +594,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ); let fn_sig = self.normalize(call_expr.span, Unnormalized::new_wip(fn_sig)); - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - fn_sig.output(), - expected, - arg_exprs, - fn_sig.c_variadic(), - TupleArgumentsFlag::DontTupleArguments, - def_id, + self.check_argument_types_maybe_method_like( + &fn_sig, call_expr, arg_exprs, expected, def_id, ); if fn_sig.abi() == rustc_abi::ExternAbi::RustCall { @@ -620,6 +615,110 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn_sig.output() } + /// Performs arguments check with an additional routine of adjusting the first argument, + /// so it corresponds to the first parameter of the function. We reuse adjustments + /// that are obtained from `probe_for_name`, where the first argument pretends to be + /// a receiver like in a method call. At this point this routine is used for delegations, + /// as from this moment we always generate a call (earlier method calls were generated), + /// so we can both propagate parent generics and get benefits from adjustments from method call. + fn check_argument_types_maybe_method_like( + &self, + fn_sig: &FnSig<'tcx>, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + expected: Expectation<'tcx>, + def_id: Option, + ) { + let do_check = || { + self.check_argument_types( + call_expr.span, + call_expr, + fn_sig.inputs(), + fn_sig.output(), + expected, + arg_exprs, + fn_sig.c_variadic(), + TupleArgumentsFlag::DontTupleArguments, + def_id, + ); + }; + + let Some(scope) = self.get_scope_for_method_call_adjustments(call_expr, arg_exprs) else { + return do_check(); + }; + + let first_expr = &arg_exprs[0]; + let first_arg_type = self.check_expr(first_expr); + + // Reuse method probing that is used during method call, as all this code pretends that + // we generated method call. + let pick = self.probe_for_name( + Mode::MethodCall, + Ident::dummy(), + None, + IsSuggestion(false), + first_arg_type, + call_expr.hir_id, + scope, + ); + + let Ok(ref pick) = pick else { return do_check() }; + + // Fool typechecker by placing an adjusted type of the first arg to avoid errors. + // We already wrote type of `first_expr` during `self.check_expr(first_expr)` above. + let first_arg_type = self + .typeck_results + .borrow_mut() + .node_types_mut() + .insert(first_expr.hir_id, pick.self_ty) + .expect("must be set"); + + do_check(); + + let mut results = self.typeck_results.borrow_mut(); + + // Remove any added adjustments for `first_expr` during `do_check` and replace them with ours. + let mut adjustments = results.adjustments_mut(); + let adjustments = adjustments.entry(first_expr.hir_id).or_default(); + + let mut ctx = ConfirmContext::new(self, first_expr.span, first_expr, first_expr); + *adjustments = ctx.create_ty_adjustments_from_pick(first_arg_type, pick).1; + + // Restore original first provided arg type. + results.node_types_mut().insert(first_expr.hir_id, first_arg_type); + } + + /// Gets scope for method-call like adjustments for the first argument of the call. + /// Now only delegations are processed this way. + fn get_scope_for_method_call_adjustments( + &self, + call_expr: &'tcx hir::Expr<'tcx>, + arg_exprs: &'tcx [hir::Expr<'tcx>], + ) -> Option { + // Check that we are inside delegation and processing its call. First, we check that + // the parent of call expr. is delegation and then make sure that it is compiler-generated + // by comparing their hir ids (otherwise we will encounter errors in nested delegations, + // see tests\ui\delegation\impl-reuse-pass.rs:237). + let parent_def = self.tcx.hir_get_parent_item(call_expr.hir_id).def_id; + let Some(info) = opt_get_delegation_info(self.tcx, parent_def) else { return None }; + + if call_expr.hir_id != info.call_expr_id { + return None; + }; + + let Some(path_res_id) = info.call_path_res else { return None }; + + // Check that delegation has first provided arg and that the call path + // resolves to a trait method (inherent methods are not yet supported). + if arg_exprs.is_empty() + || !self.tcx.opt_associated_item(path_res_id).is_some_and(|i| i.is_method()) + { + return None; + } + + Some(ProbeScope::Single(path_res_id)) + } + /// Attempts to reinterpret `method(rcvr, args...)` as `rcvr.method(args...)` /// and suggesting the fix if the method probe is successful. fn suggest_call_as_method( diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 415630dab38b3..d62ba9cf804eb 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -13,6 +13,7 @@ use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; use rustc_hir::{Expr, ExprKind, FnRetTy, HirId, LangItem, Node, QPath, is_range_literal}; use rustc_hir_analysis::check::potentially_plural_count; +use rustc_hir_analysis::delegation::opt_get_delegation_info; use rustc_hir_analysis::hir_ty_lowering::{HirTyLowerer, ResolvedStructPath}; use rustc_index::IndexVec; use rustc_infer::infer::{BoundRegionConversionTime, DefineOpaqueTypes, InferOk, TypeTrace}; @@ -329,7 +330,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let demand_compatible = |idx| { let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; - let provided_arg = &provided_args[idx]; + let provided_arg: &hir::Expr<'tcx> = &provided_args[idx]; debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); @@ -338,7 +339,11 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // 1. Unify the provided argument with the expected type let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); + // If we are processing first arg of delegation then we could have adjusted it + // in `execute_delegation_aware_arguments_check`. + let checked_ty = opt_get_delegation_info(self.tcx, self.body_id) + .and_then(|_| self.typeck_results.borrow().node_type_opt(provided_arg.hir_id)) + .unwrap_or_else(|| self.check_expr_with_expectation(provided_arg, expectation)); // 2. Coerce to the most detailed type that could be coerced // to, which is `expected_ty` if `rvalue_hint` returns an diff --git a/compiler/rustc_hir_typeck/src/method/confirm.rs b/compiler/rustc_hir_typeck/src/method/confirm.rs index 0c83c1948d6f2..4ef6de12f7623 100644 --- a/compiler/rustc_hir_typeck/src/method/confirm.rs +++ b/compiler/rustc_hir_typeck/src/method/confirm.rs @@ -34,7 +34,7 @@ use super::{MethodCallee, probe}; use crate::errors::{SupertraitItemShadowee, SupertraitItemShadower, SupertraitItemShadowing}; use crate::{FnCtxt, callee}; -struct ConfirmContext<'a, 'tcx> { +pub(crate) struct ConfirmContext<'a, 'tcx> { fcx: &'a FnCtxt<'a, 'tcx>, span: Span, self_expr: &'tcx hir::Expr<'tcx>, @@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { - fn new( + pub(crate) fn new( fcx: &'a FnCtxt<'a, 'tcx>, span: Span, self_expr: &'tcx hir::Expr<'tcx>, @@ -178,14 +178,32 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { ) -> Ty<'tcx> { // Commit the autoderefs by calling `autoderef` again, but this // time writing the results into the various typeck results. + let (target, adjustments) = self.create_ty_adjustments_from_pick(unadjusted_self_ty, pick); + + // Write out the final adjustments. + if !self.skip_record_for_diagnostics { + self.apply_adjustments(self.self_expr, adjustments); + } + + target + } + + pub(crate) fn create_ty_adjustments_from_pick( + &mut self, + unadjusted_self_ty: Ty<'tcx>, + pick: &probe::Pick<'tcx>, + ) -> (Ty<'tcx>, Vec>) { let mut autoderef = self.autoderef(self.call_expr.span, unadjusted_self_ty); let Some((mut target, n)) = autoderef.nth(pick.autoderefs) else { - return Ty::new_error_with_message( + let error_ty = Ty::new_error_with_message( self.tcx, DUMMY_SP, format!("failed autoderef {}", pick.autoderefs), ); + + return (error_ty, vec![]); }; + assert_eq!(n, pick.autoderefs); let mut adjustments = self.adjust_steps(&autoderef); @@ -260,12 +278,7 @@ impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { self.register_predicates(autoderef.into_obligations()); - // Write out the final adjustments. - if !self.skip_record_for_diagnostics { - self.apply_adjustments(self.self_expr, adjustments); - } - - target + (target, adjustments) } /// Returns a set of generic parameters for the method *receiver* where all type and region diff --git a/compiler/rustc_hir_typeck/src/method/mod.rs b/compiler/rustc_hir_typeck/src/method/mod.rs index 49126ff0e964c..2ebff26ff9822 100644 --- a/compiler/rustc_hir_typeck/src/method/mod.rs +++ b/compiler/rustc_hir_typeck/src/method/mod.rs @@ -2,7 +2,7 @@ //! //! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/hir-typeck/method-lookup.html -mod confirm; +pub(crate) mod confirm; mod prelude_edition_lints; pub(crate) mod probe; mod suggest; diff --git a/tests/pretty/delegation-inline-attribute.pp b/tests/pretty/delegation-inline-attribute.pp index 125ed1c298262..fc409aa636d5c 100644 --- a/tests/pretty/delegation-inline-attribute.pp +++ b/tests/pretty/delegation-inline-attribute.pp @@ -38,33 +38,34 @@ fn foo(self: _) -> _ { - { - // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + Trait::foo( + // Check that #[inline(hint)] is added to foo0 reuse inside another reuse + + // Check that #[inline(hint)] is added when other attributes present in inner reuse + + // Check that #[inline(never)] is preserved in inner reuse + + // Check that #[inline(always)] is preserved in inner reuse + + // Check that #[inline(never)] is preserved when there are other attributes in inner reuse + { #[attr = Inline(Hint)] fn foo0(arg0: _) -> _ { to_reuse::foo(self + 1) } - - // Check that #[inline(hint)] is added when other attributes present in inner reuse #[attr = Cold] #[attr = MustUse] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] fn foo1(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(never)] is preserved in inner reuse #[attr = Inline(Never)] fn foo2(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(always)] is preserved in inner reuse #[attr = Inline(Always)] fn foo3(arg0: _) -> _ { to_reuse::foo(self / 2) } - - // Check that #[inline(never)] is preserved when there are other attributes in inner reuse #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] fn foo4(arg0: _) -> _ { to_reuse::foo(self / 2) } - }.foo() + }) } // Check that #[inline(hint)] is added when there are other attributes present in trait reuse @@ -72,22 +73,22 @@ #[attr = MustUse] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] #[attr = Inline(Hint)] - fn foo1(self: _) -> _ { self.0.foo1() } + fn foo1(self: _) -> _ { Trait::foo1(self.0) } // Check that #[inline(never)] is preserved in trait reuse #[attr = Inline(Never)] - fn foo2(self: _) -> _ { self.0.foo2() } + fn foo2(self: _) -> _ { Trait::foo2(self.0) } // Check that #[inline(always)] is preserved in trait reuse #[attr = Inline(Always)] - fn foo3(self: _) -> _ { self.0.foo3() } + fn foo3(self: _) -> _ { Trait::foo3(self.0) } // Check that #[inline(never)] is preserved when there are other attributes in trait reuse #[attr = Cold] #[attr = MustUse] #[attr = Inline(Never)] #[attr = Deprecated {deprecation: Deprecation {since: Unspecified}}] - fn foo4(self: _) -> _ { self.0.foo4() } + fn foo4(self: _) -> _ { Trait::foo4(self.0) } } fn main() { } diff --git a/tests/pretty/delegation-self-rename.pp b/tests/pretty/delegation-self-rename.pp index 59a07315185c7..7f7afc403607b 100644 --- a/tests/pretty/delegation-self-rename.pp +++ b/tests/pretty/delegation-self-rename.pp @@ -19,7 +19,7 @@ #[attr = Inline(Hint)] fn foo<'a, Self, A, const B: _, const B2: _, T, U, impl FnOnce() -> usize>(self: _, arg1: _) -> _ where - 'a:'a { self.foo::(arg1) } + 'a:'a { Trait::<'a, A, B>::foo::(self, arg1) } #[attr = Inline(Hint)] fn bar usize>(self: _, arg1: _) -> _ { Trait::<'static, (), true>::foo::(self, arg1) } diff --git a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr index ab0c6d66e9c51..9537e45f3b8f0 100644 --- a/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr +++ b/tests/ui/delegation/correct_body_owner_parent_found_in_diagnostics.stderr @@ -65,13 +65,20 @@ LL | impl Trait for Z { | ^^^^^^^^^^^^^^^^ error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:13:8 + | +LL | fn foo(&self) -> u8 { 0 } + | ^^^ ----- error[E0277]: the trait bound `u8: Trait` is not satisfied --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:12 @@ -87,14 +94,20 @@ LL | impl Trait for Z { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:14:8 + | +LL | fn bar(&self) -> u8 { 1 } + | ^^^ ----- error[E0277]: the trait bound `u8: Trait` is not satisfied --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:12 @@ -110,14 +123,20 @@ LL | impl Trait for Z { = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` error[E0308]: mismatched types - --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:53 + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:21:44 | LL | reuse ::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } } - | ^^^^^^^^^^^^^^^^^^^^^^^ expected `u8`, found `InvariantRef<'_, ()>` + | --- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `&u8`, found `&InvariantRef<'_, ()>` + | | + | arguments to this function are incorrect | - = note: expected type `u8` - found struct `InvariantRef<'_, ()>` - = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + = note: expected reference `&u8` + found reference `&InvariantRef<'_, ()>` +note: method defined here + --> $DIR/correct_body_owner_parent_found_in_diagnostics.rs:15:8 + | +LL | fn meh(&self) -> u8 { 2 } + | ^^^ ----- error: aborting due to 10 previous errors diff --git a/tests/ui/delegation/generics/generics-aux-pass.rs b/tests/ui/delegation/generics/generics-aux-pass.rs index 06f5c6d4d212e..f5d49c03a6137 100644 --- a/tests/ui/delegation/generics/generics-aux-pass.rs +++ b/tests/ui/delegation/generics/generics-aux-pass.rs @@ -16,20 +16,17 @@ impl generics::Trait<'static, i32, 1> for X {} impl X { reuse generics::foo as bar; - reuse generics::Trait::foo as trait_foo; reuse generics::foo::<'static, 'static, i32, i32, 1> as bar1; reuse generics::Trait::<'static, i32, 1>::foo::<'static, i32, false> as trait_foo1; } trait LocalTrait { - fn get() -> u8 { 123 } fn get_self(&self) -> u8 { 123 } reuse generics::foo as bar; reuse generics::foo::<'static, 'static, i32, i32, 1> as bar1; - reuse generics::Trait::foo as trait_foo { Self::get() } reuse generics::Trait::<'static, i32, 1>::foo::<'static, i32, false> as trait_foo1 { Self::get_self(&self) } @@ -50,13 +47,11 @@ fn main() { X::bar::(); X::bar::<'static, 'static, i32, i32, 1>(); X::bar1(); - x.trait_foo::<'static, 'static, i32, 1, String, true>(); x.trait_foo1(); ::bar::(); ::bar::<'static, 'static, i32, i32, 1>(); ::bar1(); - 1usize.trait_foo::<'static, 'static, i32, 1, String, true>(); 1usize.trait_foo1(); } diff --git a/tests/ui/delegation/generics/impl-to-trait-method.rs b/tests/ui/delegation/generics/impl-to-trait-method.rs index 102e905068e2a..88d6e4fdaabef 100644 --- a/tests/ui/delegation/generics/impl-to-trait-method.rs +++ b/tests/ui/delegation/generics/impl-to-trait-method.rs @@ -36,7 +36,6 @@ mod unconstrained_parameter { struct S(F); impl S { reuse Trait::foo { &self.0 } - //~^ ERROR type annotations needed } } diff --git a/tests/ui/delegation/generics/impl-to-trait-method.stderr b/tests/ui/delegation/generics/impl-to-trait-method.stderr index 114ebf48cca06..34259d49a1bd9 100644 --- a/tests/ui/delegation/generics/impl-to-trait-method.stderr +++ b/tests/ui/delegation/generics/impl-to-trait-method.stderr @@ -46,13 +46,6 @@ LL | fn foo(&self) LL | Self: Trait0, | ^^^^^^ required by this bound in `Trait1::foo` -error[E0282]: type annotations needed - --> $DIR/impl-to-trait-method.rs:38:22 - | -LL | reuse Trait::foo { &self.0 } - | ^^^ cannot infer type for type parameter `T` declared on the trait `Trait` - -error: aborting due to 3 previous errors +error: aborting due to 2 previous errors -Some errors have detailed explanations: E0277, E0282. -For more information about an error, try `rustc --explain E0277`. +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-coercion-errors.rs b/tests/ui/delegation/self-coercion-errors.rs new file mode 100644 index 0000000000000..6c8356bd7495b --- /dev/null +++ b/tests/ui/delegation/self-coercion-errors.rs @@ -0,0 +1,234 @@ +// Tests below represent situations when type of the first argument can not be adjusted +// to the type of first parameter (i.e., Rc -> &mut T). + +#![feature(fn_delegation)] + +use std::sync::Arc; +use std::pin::Pin; +use std::rc::Rc; + +trait Trait: Sized { + fn by_value(self) -> i32 { 1 } + fn by_mut_ref(&mut self) -> i32 { 2 } + fn by_ref(&self) -> i32 { 3 } + fn r#box(self: Box) -> i32 { 4 } + fn arc(self: Arc) -> i32 { 5 } + fn rc(self: Rc) -> i32 { 6 } + fn pin_box(self: Pin>) -> i32 { 7 } + fn pin_rc(self: Pin>) -> i32 { 8 } + fn pin_arc(self: Pin>) -> i32 { 9 } + fn box_box(self: Box>) -> i32 { 10 } +} + +struct F; +impl Trait for F {} + +struct S(F); + +fn foo() -> F { + F +} + +impl S { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + foo() + } +} + +struct S1(F); + +impl S1 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: the trait bound `fn() -> F {foo}: Trait` is not satisfied + println!("123"); + let x = &self.0; + foo + } +} + +struct S2(F); + +impl S2 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + let x = foo(); + + x + } +} + +struct S3(F); + +impl S3 { + reuse Trait::{by_value, by_mut_ref, by_ref} { + println!("123"); + let x = &self.0; + let x = foo(); + + &mut x + //~^ ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot borrow `x` as mutable, as it is not declared as mutable + //~| ERROR: cannot move out of a mutable reference + } +} + +struct X(F); + +impl X { + reuse Trait::* { &mut self.0 } + //~^ ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot move out of a mutable reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X1(F); + +impl X1 { + reuse Trait::* { &self.0 } + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X2(F); + +impl X2 { + reuse Trait::* { &&&&self.0 } + //~^ ERROR: cannot move out of a shared reference + //~| ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X3(Box); + +impl X3 { + reuse Trait::* { self.0.as_ref() } + //~^ ERROR: cannot borrow data in a `&` reference as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X4(F); + +impl X4 { + reuse Trait::* { &mut &mut &mut self.0 } + //~^ ERROR: cannot move out of a mutable reference + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X5(F); + +impl X5 { + reuse Trait::* { &&mut self.0 } + //~^ ERROR: cannot borrow `self.0` as mutable, as it is behind a `&` reference + //~| ERROR: cannot borrow data in a `&` reference as mutable [E0596] + //~| ERROR: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + //~| ERROR: cannot move out of a shared reference + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X6(Box); + +impl X6 { + reuse Trait::* { self.0 } + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X7(Box>>); + +impl X7 { + reuse Trait::* { self.0 } + //~^ ERROR: cannot borrow data in an `Arc` as mutable + //~| ERROR: cannot move out of an `Arc` + //~| ERROR: cannot move out of an `Arc` + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct X8(Pin>); + +impl X8 { + reuse Trait::* { self.0 } + //~^ ERROR: cannot move out of dereference of `Pin>` + //~| ERROR: cannot borrow data in dereference of `Pin>` as mutable + //~| ERROR: cannot move out of dereference of `Pin>` + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +struct OtherStruct; +struct X9(OtherStruct); + +impl X9 { + reuse Trait::* { self.0 } + //~^ ERROR: the trait bound `OtherStruct: Trait` is not satisfied + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types + //~| ERROR: mismatched types +} + +fn main() {} diff --git a/tests/ui/delegation/self-coercion-errors.stderr b/tests/ui/delegation/self-coercion-errors.stderr new file mode 100644 index 0000000000000..89308b15d2d84 --- /dev/null +++ b/tests/ui/delegation/self-coercion-errors.stderr @@ -0,0 +1,1916 @@ +error[E0277]: the trait bound `fn() -> F {foo}: Trait` is not satisfied + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | ___________________--------______________________^ + | | | + | | required by a bound introduced by this call +... | +LL | | foo + | | --- this tail expression is of type `fn() -> F {foo}` +LL | | } + | |_____^ the trait `Trait` is not implemented for fn item `fn() -> F {foo}` + | +help: use parentheses to call this function + | +LL | }() + | ++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | _____________________________----------__________^ + | | | + | | arguments to this function are incorrect +... | +LL | | foo +LL | | } + | |_____^ expected `&mut _`, found fn item + | + = note: expected mutable reference `&mut _` + found fn item `fn() -> F {foo}` +note: method defined here + --> $DIR/self-coercion-errors.rs:12:8 + | +LL | fn by_mut_ref(&mut self) -> i32 { 2 } + | ^^^^^^^^^^ --------- +help: consider mutably borrowing here + | +LL | &mut foo + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:43:49 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | _________________________________________------__^ + | | | + | | arguments to this function are incorrect +... | +LL | | foo +LL | | } + | |_____^ expected `&_`, found fn item + | + = note: expected reference `&_` + found fn item `fn() -> F {foo}` +note: method defined here + --> $DIR/self-coercion-errors.rs:13:8 + | +LL | fn by_ref(&self) -> i32 { 3 } + | ^^^^^^ ----- +help: consider borrowing here + | +LL | &foo + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Box<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found mutable reference `&mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Arc<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Rc<_>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Pin>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ expected `Box>`, found `&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found mutable reference `&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Box<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Arc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Arc<_>` + | +LL | reuse Trait::* { (&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Rc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Rc<_>` + | +LL | reuse Trait::* { (&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ expected `Box>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Box<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&&&&F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&&&&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Arc<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&&&&F` into `Arc<_>` + | +LL | reuse Trait::* { (&&&&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Rc<_>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&&&&F` into `Rc<_>` + | +LL | reuse Trait::* { (&&&&self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&&&&self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Pin>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ expected `Box>`, found `&&&&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&&&&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Box<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: try removing the method call + | +LL - reuse Trait::* { self.0.as_ref() } +LL + reuse Trait::* { self.0 } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Arc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Arc<_>` + | +LL | reuse Trait::* { self.0.as_ref().into() } + | +++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Rc<_>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&F` into `Rc<_>` + | +LL | reuse Trait::* { self.0.as_ref().into() } + | +++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(self.0.as_ref()) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Pin>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ expected `Box>`, found `&F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Box<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found mutable reference `&mut &mut &mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&mut &mut &mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Arc<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&mut &mut &mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&mut &mut &mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Rc<_>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&mut &mut &mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&mut &mut &mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&mut &mut &mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Pin>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ expected `Box>`, found `&mut &mut &mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found mutable reference `&mut &mut &mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Box<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box<_>` + found reference `&&mut F` + = note: for more on the distinction between the stack and the heap, read https://doc.rust-lang.org/book/ch15-01-box.html, https://doc.rust-lang.org/rust-by-example/std/box.html, and https://doc.rust-lang.org/std/boxed/index.html +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: store this in the heap by calling `Box::new` + | +LL | reuse Trait::* { Box::new(&&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Arc<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: call `Into::into` on this expression to convert `&&mut F` into `Arc<_>` + | +LL | reuse Trait::* { (&&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Rc<_>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: call `Into::into` on this expression to convert `&&mut F` into `Rc<_>` + | +LL | reuse Trait::* { (&&mut self.0).into() } + | + ++++++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: you need to pin and box this expression + | +LL | reuse Trait::* { Box::pin(&&mut self.0) } + | +++++++++ + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Pin>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ expected `Box>`, found `&&mut F` + | | + | arguments to this function are incorrect + | + = note: expected struct `Box>` + found reference `&&mut F` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:178:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Box` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X6` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:175:8 + | +LL | struct X6(Box); + | -- ------ this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Box` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider unboxing the value + | +LL | reuse Trait::* { *self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Box>>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Box>>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X7` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:187:8 + | +LL | struct X7(Box>>); + | -- ---------------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Box>>` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box` with type `std::ptr::Unique` but it is private; `0` from `X8` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:202:8 + | +LL | struct X8(Pin>); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `Pin>` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `Pin>` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X8` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:202:8 + | +LL | struct X8(Pin>); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `Pin>` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0277]: the trait bound `OtherStruct: Trait` is not satisfied + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ unsatisfied trait bound + | | + | required by a bound introduced by this call + | +help: the trait `Trait` is not implemented for `OtherStruct` + --> $DIR/self-coercion-errors.rs:217:1 + | +LL | struct OtherStruct; + | ^^^^^^^^^^^^^^^^^^ +help: the trait `Trait` is implemented for `F` + --> $DIR/self-coercion-errors.rs:24:1 + | +LL | impl Trait for F {} + | ^^^^^^^^^^^^^^^^ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `&mut _`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected mutable reference `&mut _` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:12:8 + | +LL | fn by_mut_ref(&mut self) -> i32 { 2 } + | ^^^^^^^^^^ --------- +help: consider mutably borrowing here + | +LL | reuse Trait::* { &mut self.0 } + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `&_`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:13:8 + | +LL | fn by_ref(&self) -> i32 { 3 } + | ^^^^^^ ----- +help: consider borrowing here + | +LL | reuse Trait::* { &self.0 } + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box` with type `std::ptr::Unique` but it is private; `0` from `X9` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:218:8 + | +LL | struct X9(OtherStruct); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:14:8 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Arc<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Arc<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:15:8 + | +LL | fn arc(self: Arc) -> i32 { 5 } + | ^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Rc<_>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Rc<_>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:16:8 + | +LL | fn rc(self: Rc) -> i32 { 6 } + | ^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:17:8 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:18:8 + | +LL | fn pin_rc(self: Pin>) -> i32 { 8 } + | ^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Pin>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | + = note: expected struct `Pin>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:19:8 + | +LL | fn pin_arc(self: Pin>) -> i32 { 9 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0308]: mismatched types + --> $DIR/self-coercion-errors.rs:221:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ expected `Box>`, found `OtherStruct` + | | + | arguments to this function are incorrect + | +note: there is a field `0` on `Box>` with type `std::ptr::Unique>` but it is private; `0` from `X9` was accessed through auto-deref instead + --> $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: in this struct + ::: $SRC_DIR/alloc/src/boxed.rs:LL:COL + | + = note: if this field wasn't private, it would be accessible + | + ::: $DIR/self-coercion-errors.rs:218:8 + | +LL | struct X9(OtherStruct); + | -- ----------- this is the field that was accessed + | | + | this struct is accessible through auto-deref + = note: expected struct `Box>` + found struct `OtherStruct` +note: method defined here + --> $DIR/self-coercion-errors.rs:20:8 + | +LL | fn box_box(self: Box>) -> i32 { 10 } + | ^^^^^^^ ---- +help: consider removing the tuple struct field `0` + | +LL - reuse Trait::* { self.0 } +LL + reuse Trait::* { self } + | + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | reuse Trait::{by_value, by_mut_ref, by_ref} { + | -------- value moved due to this method call +... +LL | &mut x + | ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | &mut x + | ------ you could clone this value + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable + --> $DIR/self-coercion-errors.rs:73:9 + | +LL | &mut x + | ^^^^^^ cannot borrow as mutable + | + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` +help: consider changing this to be mutable + | +LL | let mut x = foo(); + | +++ + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | - ^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &mut self.0 } + | ----------- you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &mut self.0 } + | +++ + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:84:22 + | +LL | reuse Trait::* { &mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &mut self.0 } +LL + reuse Trait::&mut self { &mut self.0 } + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | - ^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &self.0 } + | ------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:100:22 + | +LL | reuse Trait::* { &self.0 } + | ^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | - ^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &&&&self.0 } + | ---------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:115:22 + | +LL | reuse Trait::* { &&&&self.0 } + | ^^^^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | - ^^^^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0.as_ref() } + | --------------- you could clone this value + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:130:22 + | +LL | reuse Trait::* { self.0.as_ref() } + | ^^^^^^^^^^^^^^^ cannot borrow as mutable + +error[E0507]: cannot move out of a mutable reference + --> $DIR/self-coercion-errors.rs:145:22 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | - ^^^^^^^^^^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &mut &mut &mut self.0 } + | --------------------- you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:145:32 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &mut &mut &mut self.0 } + | +++ + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:145:32 + | +LL | reuse Trait::* { &mut &mut &mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &mut &mut &mut self.0 } +LL + reuse Trait::&mut self { &mut &mut &mut self.0 } + | + +error[E0507]: cannot move out of a shared reference + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | - ^^^^^^^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { &&mut self.0 } + | ------------ you could clone this value + +error[E0596]: cannot borrow `self.0` as mutable, as `self` is not declared as mutable + --> $DIR/self-coercion-errors.rs:161:23 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^ cannot borrow as mutable + | +help: consider changing this to be mutable + | +LL | reuse Trait::mut * { &&mut self.0 } + | +++ + +error[E0596]: cannot borrow data in a `&` reference as mutable + --> $DIR/self-coercion-errors.rs:161:22 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^^ cannot borrow as mutable + +error[E0596]: cannot borrow `self.0` as mutable, as it is behind a `&` reference + --> $DIR/self-coercion-errors.rs:161:23 + | +LL | reuse Trait::* { &&mut self.0 } + | ^^^^^^^^^^^ `self` is a `&` reference, so it cannot be borrowed as mutable + | +help: consider changing this to be a mutable reference + | +LL - reuse Trait::* { &&mut self.0 } +LL + reuse Trait::&mut self { &&mut self.0 } + | + +error[E0507]: cannot move out of an `Arc` + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value + +error[E0596]: cannot borrow data in an `Arc` as mutable + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | ^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc>` + +error[E0507]: cannot move out of an `Arc` + --> $DIR/self-coercion-errors.rs:190:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `Box`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::r#box` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:14:14 + | +LL | fn r#box(self: Box) -> i32 { 4 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value +help: you could `clone` the value and consume it, if the `F: Clone` trait bound could be satisfied + | +LL | reuse Trait::* { as Clone>::clone(&self.0) } + | ++++++++++++++++++++++++++ + +help: consider annotating `F` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct F; + | + +error[E0507]: cannot move out of dereference of `Pin>` + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `F`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::by_value` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:11:17 + | +LL | fn by_value(self) -> i32 { 1 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value + +error[E0596]: cannot borrow data in dereference of `Pin>` as mutable + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | ^^^^^^ cannot borrow as mutable + | + = help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Pin>` + +error[E0507]: cannot move out of dereference of `Pin>` + --> $DIR/self-coercion-errors.rs:205:22 + | +LL | reuse Trait::* { self.0 } + | - ^^^^^^ move occurs because value has type `Pin>`, which does not implement the `Copy` trait + | | + | value moved due to this method call + | +note: `Trait::pin_box` takes ownership of the receiver `self`, which moves value + --> $DIR/self-coercion-errors.rs:17:16 + | +LL | fn pin_box(self: Pin>) -> i32 { 7 } + | ^^^^ +note: if `F` implemented `Clone`, you could clone the value + --> $DIR/self-coercion-errors.rs:23:1 + | +LL | struct F; + | ^^^^^^^^ consider implementing `Clone` for this type +... +LL | reuse Trait::* { self.0 } + | ------ you could clone this value +help: you could `clone` the value and consume it, if the `F: Clone` trait bound could be satisfied + | +LL | reuse Trait::* { > as Clone>::clone(&self.0) } + | +++++++++++++++++++++++++++++++ + +help: consider annotating `F` with `#[derive(Clone)]` + | +LL + #[derive(Clone)] +LL | struct F; + | + +error: aborting due to 99 previous errors + +Some errors have detailed explanations: E0277, E0308, E0507, E0596. +For more information about an error, try `rustc --explain E0277`. diff --git a/tests/ui/delegation/self-coercion-static-free.rs b/tests/ui/delegation/self-coercion-static-free.rs new file mode 100644 index 0000000000000..f61d4247a1d94 --- /dev/null +++ b/tests/ui/delegation/self-coercion-static-free.rs @@ -0,0 +1,52 @@ +// Test that we do not adjust first argument type to first parameter type +// in delegations to static associated functions (we only do it for methods). +// Also test that we do not adjust first arg. type in delegations to free function. + +#![feature(fn_delegation)] + +pub trait Trait: Sized { + fn static_self() -> F { F } + + fn static_value(_: Self) -> i32 { 1 } + fn static_mut_ref(_: &mut Self) -> i32 { 2 } + fn static_ref(_: &Self) -> i32 { 3 } +} + +#[derive(Default)] +struct F; +impl Trait for F {} + +struct S(F); + +impl Trait for S { + reuse ::{static_value, static_mut_ref, static_ref} { + let _ = self; + S::static_self() + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + } +} + +struct S1(Box>>>>>); + +impl Trait for S1 { + reuse ::{static_value, static_mut_ref, static_ref} { + let _ = self; + S1::static_self() + //~^ ERROR: mismatched types + //~| ERROR: mismatched types + } +} + +mod to_reuse { + use super::Trait; + pub fn value(_: impl Trait) -> i32 { 1 } + pub fn mut_ref(_: &mut impl Trait) -> i32 { 2 } + pub fn r#ref(_: &impl Trait) -> i32 { 3 } +} + +reuse to_reuse::{value, mut_ref, r#ref} { F } +//~^ ERROR: mismatched types +//~| ERROR: mismatched types + +fn main() {} diff --git a/tests/ui/delegation/self-coercion-static-free.stderr b/tests/ui/delegation/self-coercion-static-free.stderr new file mode 100644 index 0000000000000..5f78272815aa5 --- /dev/null +++ b/tests/ui/delegation/self-coercion-static-free.stderr @@ -0,0 +1,87 @@ +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:24:9 + | +LL | S::static_self() + | ^^^^^^^^^^^^^^^^ expected `&mut F`, found `F` + | +help: consider mutably borrowing here + | +LL | &mut S::static_self() + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:24:9 + | +LL | S::static_self() + | ^^^^^^^^^^^^^^^^ expected `&F`, found `F` + | +help: consider borrowing here + | +LL | &S::static_self() + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:35:9 + | +LL | S1::static_self() + | ^^^^^^^^^^^^^^^^^ expected `&mut F`, found `F` + | +help: consider mutably borrowing here + | +LL | &mut S1::static_self() + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:35:9 + | +LL | S1::static_self() + | ^^^^^^^^^^^^^^^^^ expected `&F`, found `F` + | +help: consider borrowing here + | +LL | &S1::static_self() + | + + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:48:43 + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { F } + | ------- ^ expected `&mut _`, found `F` + | | + | arguments to this function are incorrect + | + = note: expected mutable reference `&mut _` + found struct `F` +note: function defined here + --> $DIR/self-coercion-static-free.rs:44:12 + | +LL | pub fn mut_ref(_: &mut impl Trait) -> i32 { 2 } + | ^^^^^^^ ------------------ +help: consider mutably borrowing here + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { &mut F } + | ++++ + +error[E0308]: mismatched types + --> $DIR/self-coercion-static-free.rs:48:43 + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { F } + | ----- ^ expected `&_`, found `F` + | | + | arguments to this function are incorrect + | + = note: expected reference `&_` + found struct `F` +note: function defined here + --> $DIR/self-coercion-static-free.rs:45:12 + | +LL | pub fn r#ref(_: &impl Trait) -> i32 { 3 } + | ^^^^^ -------------- +help: consider borrowing here + | +LL | reuse to_reuse::{value, mut_ref, r#ref} { &F } + | + + +error: aborting due to 6 previous errors + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/delegation/self-coercion.rs b/tests/ui/delegation/self-coercion.rs index e6c71ec1e257b..21b2b0cd2f56b 100644 --- a/tests/ui/delegation/self-coercion.rs +++ b/tests/ui/delegation/self-coercion.rs @@ -2,12 +2,13 @@ #![feature(fn_delegation)] -trait Trait : Sized { +trait Trait: Sized { fn by_value(self) -> i32 { 1 } fn by_mut_ref(&mut self) -> i32 { 2 } fn by_ref(&self) -> i32 { 3 } } +#[derive(Default)] struct F; impl Trait for F {} @@ -17,9 +18,20 @@ impl Trait for S { reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } } +struct S1(Box>>>>>); + +impl Trait for S1 { + reuse Trait::{by_value, by_mut_ref, by_ref} { self.0 } +} + fn main() { let mut s = S(F); assert_eq!(s.by_ref(), 3); assert_eq!(s.by_mut_ref(), 2); assert_eq!(s.by_value(), 1); + + let mut s = S1(Default::default()); + assert_eq!(s.by_ref(), 3); + assert_eq!(s.by_mut_ref(), 2); + assert_eq!(s.by_value(), 1); } diff --git a/tests/ui/delegation/wrong-lifetime-rib.rs b/tests/ui/delegation/wrong-lifetime-rib.rs index 825ac6a64008a..01645f20bf7b1 100644 --- a/tests/ui/delegation/wrong-lifetime-rib.rs +++ b/tests/ui/delegation/wrong-lifetime-rib.rs @@ -33,7 +33,7 @@ mod ice_156806 { trait X {} impl X { //~ ERROR: expected a type, found a trait - reuse Iterator::fold { //~ ERROR: `()` is not an iterator + reuse Iterator::fold { let _: &X; //~ ERROR: expected a type, found a trait } } diff --git a/tests/ui/delegation/wrong-lifetime-rib.stderr b/tests/ui/delegation/wrong-lifetime-rib.stderr index 07ddc94f7a4ae..0c4499eec192d 100644 --- a/tests/ui/delegation/wrong-lifetime-rib.stderr +++ b/tests/ui/delegation/wrong-lifetime-rib.stderr @@ -66,17 +66,7 @@ help: you can add the `dyn` keyword if you want a trait object LL | let _: &dyn X; | +++ -error[E0599]: `()` is not an iterator - --> $DIR/wrong-lifetime-rib.rs:36:25 - | -LL | reuse Iterator::fold { - | ^^^^ `()` is not an iterator - | - = note: the following trait bounds were not satisfied: - `(): Iterator` - which is required by `&mut (): Iterator` - -error: aborting due to 7 previous errors +error: aborting due to 6 previous errors -Some errors have detailed explanations: E0116, E0223, E0423, E0599, E0782. +Some errors have detailed explanations: E0116, E0223, E0423, E0782. For more information about an error, try `rustc --explain E0116`. From 22929962bbdba077e0e30e1a3f57645ee265c4c7 Mon Sep 17 00:00:00 2001 From: Weihang Lo Date: Tue, 26 May 2026 20:44:39 -0400 Subject: [PATCH 174/174] Update cargo submodule --- src/tools/cargo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/tools/cargo b/src/tools/cargo index 4d1f984518c77..fbb61be30e5f9 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 4d1f984518c77fad6eeef4f40153b002a659e662 +Subproject commit fbb61be30e5f9ac3a6ad58e56a5c0f5db2d2b3ef